mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-11 23:49:30 -05:00
replace flannel with cilium
This commit is contained in:
parent
7e1c898870
commit
791d5564ba
@ -34,6 +34,18 @@ type networkSecurityGroupsAPI interface {
|
|||||||
networkSecurityGroupsCreateOrUpdatePollerResponse, error)
|
networkSecurityGroupsCreateOrUpdatePollerResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loadBalancersClientCreateOrUpdatePollerResponse interface {
|
||||||
|
PollUntilDone(ctx context.Context, freq time.Duration) (armnetwork.LoadBalancersClientCreateOrUpdateResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadBalancersAPI interface {
|
||||||
|
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string,
|
||||||
|
loadBalancerName string, parameters armnetwork.LoadBalancer,
|
||||||
|
options *armnetwork.LoadBalancersClientBeginCreateOrUpdateOptions) (
|
||||||
|
loadBalancersClientCreateOrUpdatePollerResponse, error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type virtualMachineScaleSetsCreateOrUpdatePollerResponse interface {
|
type virtualMachineScaleSetsCreateOrUpdatePollerResponse interface {
|
||||||
PollUntilDone(ctx context.Context, freq time.Duration) (armcompute.VirtualMachineScaleSetsClientCreateOrUpdateResponse, error)
|
PollUntilDone(ctx context.Context, freq time.Duration) (armcompute.VirtualMachineScaleSetsClientCreateOrUpdateResponse, error)
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,29 @@ func (a stubNetworksAPI) BeginCreateOrUpdate(ctx context.Context, resourceGroupN
|
|||||||
return a.stubResponse, a.createErr
|
return a.stubResponse, a.createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stubLoadBalancersAPI struct {
|
||||||
|
createErr error
|
||||||
|
stubResponse stubLoadBalancersClientCreateOrUpdatePollerResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubLoadBalancersClientCreateOrUpdatePollerResponse struct {
|
||||||
|
pollResponse armnetwork.LoadBalancersClientCreateOrUpdateResponse
|
||||||
|
pollErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r stubLoadBalancersClientCreateOrUpdatePollerResponse) PollUntilDone(ctx context.Context, freq time.Duration,
|
||||||
|
) (armnetwork.LoadBalancersClientCreateOrUpdateResponse, error) {
|
||||||
|
return r.pollResponse, r.pollErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a stubLoadBalancersAPI) BeginCreateOrUpdate(ctx context.Context, resourceGroupName string,
|
||||||
|
loadBalancerName string, parameters armnetwork.LoadBalancer,
|
||||||
|
options *armnetwork.LoadBalancersClientBeginCreateOrUpdateOptions) (
|
||||||
|
loadBalancersClientCreateOrUpdatePollerResponse, error,
|
||||||
|
) {
|
||||||
|
return a.stubResponse, a.createErr
|
||||||
|
}
|
||||||
|
|
||||||
type stubNetworkSecurityGroupsCreateOrUpdatePollerResponse struct {
|
type stubNetworkSecurityGroupsCreateOrUpdatePollerResponse struct {
|
||||||
armnetwork.SecurityGroupsClientCreateOrUpdatePollerResponse
|
armnetwork.SecurityGroupsClientCreateOrUpdatePollerResponse
|
||||||
pollerErr error
|
pollerErr error
|
||||||
@ -143,23 +166,17 @@ func (a stubScaleSetsAPI) BeginCreateOrUpdate(ctx context.Context, resourceGroup
|
|||||||
return a.stubResponse, a.createErr
|
return a.stubResponse, a.createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
type stubPublicIPAddressesAPI struct {
|
type stubPublicIPAddressesAPI struct {
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
createErr error
|
||||||
createErr error
|
getErr error
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
getErr error
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
stubCreateResponse stubPublicIPAddressesClientCreateOrUpdatePollerResponse
|
stubCreateResponse stubPublicIPAddressesClientCreateOrUpdatePollerResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
type stubPublicIPAddressesClientCreateOrUpdatePollerResponse struct {
|
type stubPublicIPAddressesClientCreateOrUpdatePollerResponse struct {
|
||||||
armnetwork.PublicIPAddressesClientCreateOrUpdatePollerResponse
|
armnetwork.PublicIPAddressesClientCreateOrUpdatePollerResponse
|
||||||
pollErr error
|
pollErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
func (r stubPublicIPAddressesClientCreateOrUpdatePollerResponse) PollUntilDone(ctx context.Context, freq time.Duration) (
|
func (r stubPublicIPAddressesClientCreateOrUpdatePollerResponse) PollUntilDone(ctx context.Context, freq time.Duration) (
|
||||||
armnetwork.PublicIPAddressesClientCreateOrUpdateResponse, error,
|
armnetwork.PublicIPAddressesClientCreateOrUpdateResponse, error,
|
||||||
) {
|
) {
|
||||||
@ -167,6 +184,9 @@ func (r stubPublicIPAddressesClientCreateOrUpdatePollerResponse) PollUntilDone(c
|
|||||||
PublicIPAddressesClientCreateOrUpdateResult: armnetwork.PublicIPAddressesClientCreateOrUpdateResult{
|
PublicIPAddressesClientCreateOrUpdateResult: armnetwork.PublicIPAddressesClientCreateOrUpdateResult{
|
||||||
PublicIPAddress: armnetwork.PublicIPAddress{
|
PublicIPAddress: armnetwork.PublicIPAddress{
|
||||||
ID: to.StringPtr("pubIP-id"),
|
ID: to.StringPtr("pubIP-id"),
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
|
||||||
|
IPAddress: to.StringPtr("192.0.2.1"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, r.pollErr
|
}, r.pollErr
|
||||||
@ -206,7 +226,6 @@ func (a stubPublicIPAddressesAPI) ListVirtualMachineScaleSetVMPublicIPAddresses(
|
|||||||
return &stubPublicIPAddressesListVirtualMachineScaleSetVMPublicIPAddressesPager{pagesCounter: 0, PagesMax: 1}
|
return &stubPublicIPAddressesListVirtualMachineScaleSetVMPublicIPAddressesPager{pagesCounter: 0, PagesMax: 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
func (a stubPublicIPAddressesAPI) BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, publicIPAddressName string,
|
func (a stubPublicIPAddressesAPI) BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, publicIPAddressName string,
|
||||||
parameters armnetwork.PublicIPAddress, options *armnetwork.PublicIPAddressesClientBeginCreateOrUpdateOptions) (
|
parameters armnetwork.PublicIPAddress, options *armnetwork.PublicIPAddressesClientBeginCreateOrUpdateOptions) (
|
||||||
publicIPAddressesClientCreateOrUpdatePollerResponse, error,
|
publicIPAddressesClientCreateOrUpdatePollerResponse, error,
|
||||||
@ -214,7 +233,6 @@ func (a stubPublicIPAddressesAPI) BeginCreateOrUpdate(ctx context.Context, resou
|
|||||||
return a.stubCreateResponse, a.createErr
|
return a.stubCreateResponse, a.createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
func (a stubPublicIPAddressesAPI) Get(ctx context.Context, resourceGroupName string, publicIPAddressName string, options *armnetwork.PublicIPAddressesClientGetOptions) (
|
func (a stubPublicIPAddressesAPI) Get(ctx context.Context, resourceGroupName string, publicIPAddressName string, options *armnetwork.PublicIPAddressesClientGetOptions) (
|
||||||
armnetwork.PublicIPAddressesClientGetResponse, error,
|
armnetwork.PublicIPAddressesClientGetResponse, error,
|
||||||
) {
|
) {
|
||||||
@ -230,11 +248,9 @@ func (a stubPublicIPAddressesAPI) Get(ctx context.Context, resourceGroupName str
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stubNetworkInterfacesAPI struct {
|
type stubNetworkInterfacesAPI struct {
|
||||||
getErr error
|
getErr error
|
||||||
// TODO: deprecate as soon as scale sets are available
|
|
||||||
createErr error
|
createErr error
|
||||||
// TODO: deprecate as soon as scale sets are available
|
stubResp stubInterfacesClientCreateOrUpdatePollerResponse
|
||||||
stubResp stubInterfacesClientCreateOrUpdatePollerResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a stubNetworkInterfacesAPI) GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName string,
|
func (a stubNetworkInterfacesAPI) GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName string,
|
||||||
|
@ -35,6 +35,17 @@ func (c *networkInterfacesClient) BeginCreateOrUpdate(ctx context.Context, resou
|
|||||||
return c.InterfacesClient.BeginCreateOrUpdate(ctx, resourceGroupName, networkInterfaceName, parameters, options)
|
return c.InterfacesClient.BeginCreateOrUpdate(ctx, resourceGroupName, networkInterfaceName, parameters, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loadBalancersClient struct {
|
||||||
|
*armnetwork.LoadBalancersClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *loadBalancersClient) BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, loadBalancerName string,
|
||||||
|
parameters armnetwork.LoadBalancer, options *armnetwork.LoadBalancersClientBeginCreateOrUpdateOptions) (
|
||||||
|
loadBalancersClientCreateOrUpdatePollerResponse, error,
|
||||||
|
) {
|
||||||
|
return c.LoadBalancersClient.BeginCreateOrUpdate(ctx, resourceGroupName, loadBalancerName, parameters, options)
|
||||||
|
}
|
||||||
|
|
||||||
type networkSecurityGroupsClient struct {
|
type networkSecurityGroupsClient struct {
|
||||||
*armnetwork.SecurityGroupsClient
|
*armnetwork.SecurityGroupsClient
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ type Client struct {
|
|||||||
scaleSetsAPI
|
scaleSetsAPI
|
||||||
publicIPAddressesAPI
|
publicIPAddressesAPI
|
||||||
networkInterfacesAPI
|
networkInterfacesAPI
|
||||||
|
loadBalancersAPI
|
||||||
virtualMachinesAPI
|
virtualMachinesAPI
|
||||||
applicationsAPI
|
applicationsAPI
|
||||||
servicePrincipalsAPI
|
servicePrincipalsAPI
|
||||||
@ -53,6 +54,8 @@ type Client struct {
|
|||||||
subnetID string
|
subnetID string
|
||||||
coordinatorsScaleSet string
|
coordinatorsScaleSet string
|
||||||
nodesScaleSet string
|
nodesScaleSet string
|
||||||
|
loadBalancerName string
|
||||||
|
loadBalancerPubIP string
|
||||||
networkSecurityGroup string
|
networkSecurityGroup string
|
||||||
adAppObjectID string
|
adAppObjectID string
|
||||||
}
|
}
|
||||||
@ -77,6 +80,7 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
|
|||||||
scaleSetAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
|
scaleSetAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
|
||||||
publicIPAddressesAPI := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
|
publicIPAddressesAPI := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
|
||||||
networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
|
networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
|
||||||
|
loadBalancersAPI := armnetwork.NewLoadBalancersClient(subscriptionID, cred, nil)
|
||||||
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
|
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
|
||||||
applicationsAPI := graphrbac.NewApplicationsClient(tenantID)
|
applicationsAPI := graphrbac.NewApplicationsClient(tenantID)
|
||||||
applicationsAPI.Authorizer = graphAuthorizer
|
applicationsAPI.Authorizer = graphAuthorizer
|
||||||
@ -92,6 +96,7 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
|
|||||||
scaleSetsAPI: &virtualMachineScaleSetsClient{scaleSetAPI},
|
scaleSetsAPI: &virtualMachineScaleSetsClient{scaleSetAPI},
|
||||||
publicIPAddressesAPI: &publicIPAddressesClient{publicIPAddressesAPI},
|
publicIPAddressesAPI: &publicIPAddressesClient{publicIPAddressesAPI},
|
||||||
networkInterfacesAPI: &networkInterfacesClient{networkInterfacesAPI},
|
networkInterfacesAPI: &networkInterfacesClient{networkInterfacesAPI},
|
||||||
|
loadBalancersAPI: &loadBalancersClient{loadBalancersAPI},
|
||||||
applicationsAPI: &applicationsClient{&applicationsAPI},
|
applicationsAPI: &applicationsClient{&applicationsAPI},
|
||||||
servicePrincipalsAPI: &servicePrincipalsClient{&servicePrincipalsAPI},
|
servicePrincipalsAPI: &servicePrincipalsClient{&servicePrincipalsAPI},
|
||||||
roleAssignmentsAPI: &roleAssignmentsClient{&roleAssignmentsAPI},
|
roleAssignmentsAPI: &roleAssignmentsClient{&roleAssignmentsAPI},
|
||||||
@ -233,7 +238,7 @@ func (c *Client) SetState(stat state.ConstellationState) error {
|
|||||||
}
|
}
|
||||||
c.coordinatorsScaleSet = stat.AzureCoordinatorsScaleSet
|
c.coordinatorsScaleSet = stat.AzureCoordinatorsScaleSet
|
||||||
if len(stat.AzureNodes) == 0 {
|
if len(stat.AzureNodes) == 0 {
|
||||||
return errors.New("state has no coordinator scale set")
|
return errors.New("state has no nodes")
|
||||||
}
|
}
|
||||||
c.nodes = stat.AzureNodes
|
c.nodes = stat.AzureNodes
|
||||||
if len(stat.AzureCoordinators) == 0 {
|
if len(stat.AzureCoordinators) == 0 {
|
||||||
|
@ -13,13 +13,14 @@ import (
|
|||||||
func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput) error {
|
func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput) error {
|
||||||
// Create nodes scale set
|
// Create nodes scale set
|
||||||
createNodesInput := CreateScaleSetInput{
|
createNodesInput := CreateScaleSetInput{
|
||||||
Name: "constellation-scale-set-nodes-" + c.uid,
|
Name: "constellation-scale-set-nodes-" + c.uid,
|
||||||
NamePrefix: c.name + "-worker-" + c.uid + "-",
|
NamePrefix: c.name + "-worker-" + c.uid + "-",
|
||||||
Count: input.CountNodes,
|
Count: input.CountNodes,
|
||||||
InstanceType: input.InstanceType,
|
InstanceType: input.InstanceType,
|
||||||
StateDiskSizeGB: int32(input.StateDiskSizeGB),
|
StateDiskSizeGB: int32(input.StateDiskSizeGB),
|
||||||
Image: input.Image,
|
Image: input.Image,
|
||||||
UserAssingedIdentity: input.UserAssingedIdentity,
|
UserAssingedIdentity: input.UserAssingedIdentity,
|
||||||
|
LoadBalancerBackendAddressPool: azure.BackendAddressPoolWorkerName + "-" + c.uid,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.createScaleSet(ctx, createNodesInput); err != nil {
|
if err := c.createScaleSet(ctx, createNodesInput); err != nil {
|
||||||
@ -30,13 +31,14 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
|
|||||||
|
|
||||||
// Create coordinator scale set
|
// Create coordinator scale set
|
||||||
createCoordinatorsInput := CreateScaleSetInput{
|
createCoordinatorsInput := CreateScaleSetInput{
|
||||||
Name: "constellation-scale-set-coordinators-" + c.uid,
|
Name: "constellation-scale-set-coordinators-" + c.uid,
|
||||||
NamePrefix: c.name + "-control-plane-" + c.uid + "-",
|
NamePrefix: c.name + "-control-plane-" + c.uid + "-",
|
||||||
Count: input.CountCoordinators,
|
Count: input.CountCoordinators,
|
||||||
InstanceType: input.InstanceType,
|
InstanceType: input.InstanceType,
|
||||||
StateDiskSizeGB: int32(input.StateDiskSizeGB),
|
StateDiskSizeGB: int32(input.StateDiskSizeGB),
|
||||||
Image: input.Image,
|
Image: input.Image,
|
||||||
UserAssingedIdentity: input.UserAssingedIdentity,
|
UserAssingedIdentity: input.UserAssingedIdentity,
|
||||||
|
LoadBalancerBackendAddressPool: azure.BackendAddressPoolControlPlaneName + "-" + c.uid,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.createScaleSet(ctx, createCoordinatorsInput); err != nil {
|
if err := c.createScaleSet(ctx, createCoordinatorsInput); err != nil {
|
||||||
@ -58,6 +60,14 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
|
|||||||
}
|
}
|
||||||
c.coordinators = instances
|
c.coordinators = instances
|
||||||
|
|
||||||
|
// Set the load balancer public IP in the first coordinator
|
||||||
|
coord, ok := c.coordinators["0"]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("coordinator 0 not found")
|
||||||
|
}
|
||||||
|
coord.PublicIP = c.loadBalancerPubIP
|
||||||
|
c.coordinators["0"] = coord
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,13 +129,13 @@ func (c *Client) CreateInstancesVMs(ctx context.Context, input CreateInstancesIn
|
|||||||
// TODO: deprecate as soon as scale sets are available.
|
// TODO: deprecate as soon as scale sets are available.
|
||||||
func (c *Client) createInstanceVM(ctx context.Context, input azure.VMInstance) (azure.Instance, error) {
|
func (c *Client) createInstanceVM(ctx context.Context, input azure.VMInstance) (azure.Instance, error) {
|
||||||
pubIPName := input.Name + "-pubIP"
|
pubIPName := input.Name + "-pubIP"
|
||||||
pubIPID, err := c.createPublicIPAddress(ctx, pubIPName)
|
pubIP, err := c.createPublicIPAddress(ctx, pubIPName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return azure.Instance{}, err
|
return azure.Instance{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nicName := input.Name + "-NIC"
|
nicName := input.Name + "-NIC"
|
||||||
privIP, nicID, err := c.createNIC(ctx, nicName, pubIPID)
|
privIP, nicID, err := c.createNIC(ctx, nicName, *pubIP.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return azure.Instance{}, err
|
return azure.Instance{}, err
|
||||||
}
|
}
|
||||||
@ -167,18 +177,22 @@ func (c *Client) createScaleSet(ctx context.Context, input CreateScaleSetInput)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
scaleSet := azure.ScaleSet{
|
scaleSet := azure.ScaleSet{
|
||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
NamePrefix: input.NamePrefix,
|
NamePrefix: input.NamePrefix,
|
||||||
Location: c.location,
|
Location: c.location,
|
||||||
InstanceType: input.InstanceType,
|
InstanceType: input.InstanceType,
|
||||||
StateDiskSizeGB: input.StateDiskSizeGB,
|
StateDiskSizeGB: input.StateDiskSizeGB,
|
||||||
Count: int64(input.Count),
|
Count: int64(input.Count),
|
||||||
Username: "constellation",
|
Username: "constellation",
|
||||||
SubnetID: c.subnetID,
|
SubnetID: c.subnetID,
|
||||||
NetworkSecurityGroup: c.networkSecurityGroup,
|
NetworkSecurityGroup: c.networkSecurityGroup,
|
||||||
Image: input.Image,
|
Image: input.Image,
|
||||||
Password: pw,
|
Password: pw,
|
||||||
UserAssignedIdentity: input.UserAssingedIdentity,
|
UserAssignedIdentity: input.UserAssingedIdentity,
|
||||||
|
Subscription: c.subscriptionID,
|
||||||
|
ResourceGroup: c.resourceGroup,
|
||||||
|
LoadBalancerName: c.loadBalancerName,
|
||||||
|
LoadBalancerBackendAddressPool: input.LoadBalancerBackendAddressPool,
|
||||||
}.Azure()
|
}.Azure()
|
||||||
|
|
||||||
poller, err := c.scaleSetsAPI.BeginCreateOrUpdate(
|
poller, err := c.scaleSetsAPI.BeginCreateOrUpdate(
|
||||||
@ -242,13 +256,14 @@ func (c *Client) getInstanceIPs(ctx context.Context, scaleSet string, count int)
|
|||||||
|
|
||||||
// CreateScaleSetInput is the input for a CreateScaleSet operation.
|
// CreateScaleSetInput is the input for a CreateScaleSet operation.
|
||||||
type CreateScaleSetInput struct {
|
type CreateScaleSetInput struct {
|
||||||
Name string
|
Name string
|
||||||
NamePrefix string
|
NamePrefix string
|
||||||
Count int
|
Count int
|
||||||
InstanceType string
|
InstanceType string
|
||||||
StateDiskSizeGB int32
|
StateDiskSizeGB int32
|
||||||
Image string
|
Image string
|
||||||
UserAssingedIdentity string
|
UserAssingedIdentity string
|
||||||
|
LoadBalancerBackendAddressPool string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateResourceGroup creates a resource group.
|
// CreateResourceGroup creates a resource group.
|
||||||
|
@ -216,6 +216,7 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
roleAssignmentsAPI: tc.roleAssignmentsAPI,
|
roleAssignmentsAPI: tc.roleAssignmentsAPI,
|
||||||
nodes: make(azure.Instances),
|
nodes: make(azure.Instances),
|
||||||
coordinators: make(azure.Instances),
|
coordinators: make(azure.Instances),
|
||||||
|
loadBalancerPubIP: "lbip",
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
@ -227,7 +228,7 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
assert.NotEmpty(client.nodes["0"].PrivateIP)
|
assert.NotEmpty(client.nodes["0"].PrivateIP)
|
||||||
assert.NotEmpty(client.nodes["0"].PublicIP)
|
assert.NotEmpty(client.nodes["0"].PublicIP)
|
||||||
assert.NotEmpty(client.coordinators["0"].PrivateIP)
|
assert.NotEmpty(client.coordinators["0"].PrivateIP)
|
||||||
assert.NotEmpty(client.coordinators["0"].PublicIP)
|
assert.Equal("lbip", client.coordinators["0"].PublicIP)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,34 @@ import (
|
|||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
|
"github.com/edgelesssys/constellation/cli/azure"
|
||||||
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
|
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createNetworkInput struct {
|
type createNetworkInput struct {
|
||||||
name string
|
name string
|
||||||
location string
|
location string
|
||||||
addressSpace string
|
addressSpace string
|
||||||
|
nodeAddressSpace string
|
||||||
|
podAddressSpace string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
nodeNetworkName = "nodeNetwork"
|
||||||
|
podNetworkName = "podNetwork"
|
||||||
|
networkAddressSpace = "10.0.0.0/8"
|
||||||
|
nodeAddressSpace = "10.9.0.0/16"
|
||||||
|
podAddressSpace = "10.10.0.0/16"
|
||||||
|
)
|
||||||
|
|
||||||
// CreateVirtualNetwork creates a virtual network.
|
// CreateVirtualNetwork creates a virtual network.
|
||||||
func (c *Client) CreateVirtualNetwork(ctx context.Context) error {
|
func (c *Client) CreateVirtualNetwork(ctx context.Context) error {
|
||||||
createNetworkInput := createNetworkInput{
|
createNetworkInput := createNetworkInput{
|
||||||
name: "constellation-" + c.uid,
|
name: "constellation-" + c.uid,
|
||||||
location: c.location,
|
location: c.location,
|
||||||
addressSpace: "172.20.0.0/16",
|
addressSpace: networkAddressSpace,
|
||||||
|
nodeAddressSpace: nodeAddressSpace,
|
||||||
|
podAddressSpace: podAddressSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
poller, err := c.networksAPI.BeginCreateOrUpdate(
|
poller, err := c.networksAPI.BeginCreateOrUpdate(
|
||||||
@ -36,9 +49,15 @@ func (c *Client) CreateVirtualNetwork(ctx context.Context) error {
|
|||||||
},
|
},
|
||||||
Subnets: []*armnetwork.Subnet{
|
Subnets: []*armnetwork.Subnet{
|
||||||
{
|
{
|
||||||
Name: to.StringPtr("default"),
|
Name: to.StringPtr(nodeNetworkName),
|
||||||
Properties: &armnetwork.SubnetPropertiesFormat{
|
Properties: &armnetwork.SubnetPropertiesFormat{
|
||||||
AddressPrefix: to.StringPtr(createNetworkInput.addressSpace),
|
AddressPrefix: to.StringPtr(createNetworkInput.nodeAddressSpace),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(podNetworkName),
|
||||||
|
Properties: &armnetwork.SubnetPropertiesFormat{
|
||||||
|
AddressPrefix: to.StringPtr(createNetworkInput.podAddressSpace),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -141,25 +160,29 @@ func (c *Client) createNIC(ctx context.Context, name, publicIPAddressID string)
|
|||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createPublicIPAddress creates a public IP address.
|
func (c *Client) createPublicIPAddress(ctx context.Context, name string) (*armnetwork.PublicIPAddress, error) {
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
func (c *Client) createPublicIPAddress(ctx context.Context, name string) (string, error) {
|
|
||||||
poller, err := c.publicIPAddressesAPI.BeginCreateOrUpdate(
|
poller, err := c.publicIPAddressesAPI.BeginCreateOrUpdate(
|
||||||
ctx, c.resourceGroup, name,
|
ctx, c.resourceGroup, name,
|
||||||
armnetwork.PublicIPAddress{
|
armnetwork.PublicIPAddress{
|
||||||
Location: to.StringPtr(c.location),
|
Location: to.StringPtr(c.location),
|
||||||
|
SKU: &armnetwork.PublicIPAddressSKU{
|
||||||
|
Name: armnetwork.PublicIPAddressSKUNameStandard.ToPtr(),
|
||||||
|
},
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
|
||||||
|
PublicIPAllocationMethod: armnetwork.IPAllocationMethodStatic.ToPtr(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
pollerResp, err := poller.PollUntilDone(ctx, 30*time.Second)
|
pollerResp, err := poller.PollUntilDone(ctx, 30*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return *pollerResp.PublicIPAddressesClientCreateOrUpdateResult.PublicIPAddress.ID, nil
|
return &pollerResp.PublicIPAddressesClientCreateOrUpdateResult.PublicIPAddress, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkSecurityGroupInput defines firewall rules to be set.
|
// NetworkSecurityGroupInput defines firewall rules to be set.
|
||||||
@ -167,3 +190,42 @@ type NetworkSecurityGroupInput struct {
|
|||||||
Ingress cloudtypes.Firewall
|
Ingress cloudtypes.Firewall
|
||||||
Egress cloudtypes.Firewall
|
Egress cloudtypes.Firewall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateExternalLoadBalancer creates an external load balancer.
|
||||||
|
func (c *Client) CreateExternalLoadBalancer(ctx context.Context) error {
|
||||||
|
// First, create a public IP address for the load balancer.
|
||||||
|
publicIPAddress, err := c.createPublicIPAddress(ctx, "loadbalancer-public-ip-"+c.uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, create the load balancer.
|
||||||
|
loadBalancerName := "constellation-load-balancer-" + c.uid
|
||||||
|
loadBalancer := azure.LoadBalancer{
|
||||||
|
Name: loadBalancerName,
|
||||||
|
Location: c.location,
|
||||||
|
ResourceGroup: c.resourceGroup,
|
||||||
|
Subscription: c.subscriptionID,
|
||||||
|
PublicIPID: *publicIPAddress.ID,
|
||||||
|
UID: c.uid,
|
||||||
|
}
|
||||||
|
azureLoadBalancer := loadBalancer.Azure()
|
||||||
|
|
||||||
|
poller, err := c.loadBalancersAPI.BeginCreateOrUpdate(
|
||||||
|
ctx, c.resourceGroup, loadBalancerName,
|
||||||
|
azureLoadBalancer,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = poller.PollUntilDone(ctx, 30*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.loadBalancerName = loadBalancerName
|
||||||
|
|
||||||
|
c.loadBalancerPubIP = *publicIPAddress.Properties.IPAddress
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -170,7 +170,6 @@ func TestCreateNIC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecate as soon as scale sets are available.
|
|
||||||
func TestCreatePublicIPAddress(t *testing.T) {
|
func TestCreatePublicIPAddress(t *testing.T) {
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
@ -218,3 +217,58 @@ func TestCreatePublicIPAddress(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateExternalLoadBalancer(t *testing.T) {
|
||||||
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
publicIPAddressesAPI publicIPAddressesAPI
|
||||||
|
loadBalancersAPI loadBalancersAPI
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"successful create": {
|
||||||
|
publicIPAddressesAPI: stubPublicIPAddressesAPI{stubCreateResponse: stubPublicIPAddressesClientCreateOrUpdatePollerResponse{}},
|
||||||
|
loadBalancersAPI: stubLoadBalancersAPI{},
|
||||||
|
},
|
||||||
|
"failed to get response from successful create": {
|
||||||
|
loadBalancersAPI: stubLoadBalancersAPI{stubResponse: stubLoadBalancersClientCreateOrUpdatePollerResponse{pollErr: someErr}},
|
||||||
|
publicIPAddressesAPI: stubPublicIPAddressesAPI{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"failed create": {
|
||||||
|
loadBalancersAPI: stubLoadBalancersAPI{createErr: someErr},
|
||||||
|
publicIPAddressesAPI: stubPublicIPAddressesAPI{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"cannot create public IP": {
|
||||||
|
publicIPAddressesAPI: stubPublicIPAddressesAPI{createErr: someErr},
|
||||||
|
loadBalancersAPI: stubLoadBalancersAPI{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
client := Client{
|
||||||
|
resourceGroup: "resource-group",
|
||||||
|
location: "location",
|
||||||
|
name: "name",
|
||||||
|
uid: "uid",
|
||||||
|
nodes: make(azure.Instances),
|
||||||
|
coordinators: make(azure.Instances),
|
||||||
|
loadBalancersAPI: tc.loadBalancersAPI,
|
||||||
|
publicIPAddressesAPI: tc.publicIPAddressesAPI,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.CreateExternalLoadBalancer(ctx)
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
162
cli/azure/loadbalancer.go
Normal file
162
cli/azure/loadbalancer.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package azure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadBalancer defines a Azure load balancer.
|
||||||
|
type LoadBalancer struct {
|
||||||
|
Name string
|
||||||
|
Subscription string
|
||||||
|
ResourceGroup string
|
||||||
|
Location string
|
||||||
|
PublicIPID string
|
||||||
|
UID string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackendAddressPoolWorkerName = "backendAddressWorkerPool"
|
||||||
|
BackendAddressPoolControlPlaneName = "backendAddressControlPlanePool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Azure returns a Azure representation of LoadBalancer.
|
||||||
|
func (l LoadBalancer) Azure() armnetwork.LoadBalancer {
|
||||||
|
frontEndIPConfigName := "frontEndIPConfig"
|
||||||
|
kubeHealthProbeName := "kubeHealthProbe"
|
||||||
|
coordHealthProbeName := "coordHealthProbe"
|
||||||
|
debugdHealthProbeName := "debugdHealthProbe"
|
||||||
|
backEndAddressPoolNodeName := BackendAddressPoolWorkerName + "-" + l.UID
|
||||||
|
backEndAddressPoolControlPlaneName := BackendAddressPoolControlPlaneName + "-" + l.UID
|
||||||
|
|
||||||
|
return armnetwork.LoadBalancer{
|
||||||
|
Name: to.StringPtr(l.Name),
|
||||||
|
Location: to.StringPtr(l.Location),
|
||||||
|
SKU: &armnetwork.LoadBalancerSKU{Name: armnetwork.LoadBalancerSKUNameStandard.ToPtr()},
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(frontEndIPConfigName),
|
||||||
|
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: to.StringPtr(l.PublicIPID),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BackendAddressPools: []*armnetwork.BackendAddressPool{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(backEndAddressPoolNodeName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(backEndAddressPoolControlPlaneName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("all"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Probes: []*armnetwork.Probe{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(kubeHealthProbeName),
|
||||||
|
Properties: &armnetwork.ProbePropertiesFormat{
|
||||||
|
Protocol: armnetwork.ProbeProtocolTCP.ToPtr(),
|
||||||
|
Port: to.Int32Ptr(int32(6443)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(coordHealthProbeName),
|
||||||
|
Properties: &armnetwork.ProbePropertiesFormat{
|
||||||
|
Protocol: armnetwork.ProbeProtocolTCP.ToPtr(),
|
||||||
|
Port: to.Int32Ptr(int32(constants.CoordinatorPort)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(debugdHealthProbeName),
|
||||||
|
Properties: &armnetwork.ProbePropertiesFormat{
|
||||||
|
Protocol: armnetwork.ProbeProtocolTCP.ToPtr(),
|
||||||
|
Port: to.Int32Ptr(int32(4000)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LoadBalancingRules: []*armnetwork.LoadBalancingRule{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("kubeLoadBalancerRule"),
|
||||||
|
Properties: &armnetwork.LoadBalancingRulePropertiesFormat{
|
||||||
|
FrontendIPConfiguration: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/frontendIPConfigurations/" + frontEndIPConfigName),
|
||||||
|
},
|
||||||
|
FrontendPort: to.Int32Ptr(int32(6443)),
|
||||||
|
BackendPort: to.Int32Ptr(int32(6443)),
|
||||||
|
Protocol: armnetwork.TransportProtocolTCP.ToPtr(),
|
||||||
|
Probe: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/probes/" + kubeHealthProbeName),
|
||||||
|
},
|
||||||
|
DisableOutboundSnat: to.BoolPtr(true),
|
||||||
|
BackendAddressPools: []*armnetwork.SubResource{
|
||||||
|
{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/backendAddressPools/" + backEndAddressPoolControlPlaneName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("coordLoadBalancerRule"),
|
||||||
|
Properties: &armnetwork.LoadBalancingRulePropertiesFormat{
|
||||||
|
FrontendIPConfiguration: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/frontendIPConfigurations/" + frontEndIPConfigName),
|
||||||
|
},
|
||||||
|
FrontendPort: to.Int32Ptr(int32(constants.CoordinatorPort)),
|
||||||
|
BackendPort: to.Int32Ptr(int32(constants.CoordinatorPort)),
|
||||||
|
Protocol: armnetwork.TransportProtocolTCP.ToPtr(),
|
||||||
|
Probe: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/probes/" + coordHealthProbeName),
|
||||||
|
},
|
||||||
|
DisableOutboundSnat: to.BoolPtr(true),
|
||||||
|
BackendAddressPools: []*armnetwork.SubResource{
|
||||||
|
{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/backendAddressPools/" + backEndAddressPoolControlPlaneName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("debudLoadBalancerRule"),
|
||||||
|
Properties: &armnetwork.LoadBalancingRulePropertiesFormat{
|
||||||
|
FrontendIPConfiguration: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/frontendIPConfigurations/" + frontEndIPConfigName),
|
||||||
|
},
|
||||||
|
FrontendPort: to.Int32Ptr(int32(4000)),
|
||||||
|
BackendPort: to.Int32Ptr(int32(4000)),
|
||||||
|
Protocol: armnetwork.TransportProtocolTCP.ToPtr(),
|
||||||
|
Probe: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/probes/" + debugdHealthProbeName),
|
||||||
|
},
|
||||||
|
DisableOutboundSnat: to.BoolPtr(true),
|
||||||
|
BackendAddressPools: []*armnetwork.SubResource{
|
||||||
|
{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/backendAddressPools/" + backEndAddressPoolControlPlaneName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OutboundRules: []*armnetwork.OutboundRule{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("outboundRuleControlPlane"),
|
||||||
|
Properties: &armnetwork.OutboundRulePropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.SubResource{
|
||||||
|
{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/frontendIPConfigurations/" + frontEndIPConfigName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BackendAddressPool: &armnetwork.SubResource{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + l.Subscription + "/resourceGroups/" + l.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + l.Name + "/backendAddressPools/all"),
|
||||||
|
},
|
||||||
|
Protocol: armnetwork.LoadBalancerOutboundRuleProtocolAll.ToPtr(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -10,18 +10,22 @@ import (
|
|||||||
|
|
||||||
// ScaleSet defines a Azure scale set.
|
// ScaleSet defines a Azure scale set.
|
||||||
type ScaleSet struct {
|
type ScaleSet struct {
|
||||||
Name string
|
Name string
|
||||||
NamePrefix string
|
NamePrefix string
|
||||||
Location string
|
Subscription string
|
||||||
InstanceType string
|
ResourceGroup string
|
||||||
StateDiskSizeGB int32
|
Location string
|
||||||
Count int64
|
InstanceType string
|
||||||
Username string
|
StateDiskSizeGB int32
|
||||||
SubnetID string
|
Count int64
|
||||||
NetworkSecurityGroup string
|
Username string
|
||||||
Password string
|
SubnetID string
|
||||||
Image string
|
NetworkSecurityGroup string
|
||||||
UserAssignedIdentity string
|
Password string
|
||||||
|
Image string
|
||||||
|
UserAssignedIdentity string
|
||||||
|
LoadBalancerName string
|
||||||
|
LoadBalancerBackendAddressPool string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Azure returns the Azure representation of ScaleSet.
|
// Azure returns the Azure representation of ScaleSet.
|
||||||
@ -72,13 +76,16 @@ func (s ScaleSet) Azure() armcompute.VirtualMachineScaleSet {
|
|||||||
{
|
{
|
||||||
Name: to.StringPtr(s.Name),
|
Name: to.StringPtr(s.Name),
|
||||||
Properties: &armcompute.VirtualMachineScaleSetIPConfigurationProperties{
|
Properties: &armcompute.VirtualMachineScaleSetIPConfigurationProperties{
|
||||||
|
Primary: to.BoolPtr(true),
|
||||||
Subnet: &armcompute.APIEntityReference{
|
Subnet: &armcompute.APIEntityReference{
|
||||||
ID: to.StringPtr(s.SubnetID),
|
ID: to.StringPtr(s.SubnetID),
|
||||||
},
|
},
|
||||||
PublicIPAddressConfiguration: &armcompute.VirtualMachineScaleSetPublicIPAddressConfiguration{
|
LoadBalancerBackendAddressPools: []*armcompute.SubResource{
|
||||||
Name: to.StringPtr(s.Name),
|
{
|
||||||
Properties: &armcompute.VirtualMachineScaleSetPublicIPAddressConfigurationProperties{
|
ID: to.StringPtr("/subscriptions/" + s.Subscription + "/resourcegroups/" + s.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + s.LoadBalancerName + "/backendAddressPools/" + s.LoadBalancerBackendAddressPool),
|
||||||
IdleTimeoutInMinutes: to.Int32Ptr(15), // default per https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-networking#creating-a-scale-set-with-public-ip-per-virtual-machine
|
},
|
||||||
|
{
|
||||||
|
ID: to.StringPtr("/subscriptions/" + s.Subscription + "/resourcegroups/" + s.ResourceGroup + "/providers/Microsoft.Network/loadBalancers/" + s.LoadBalancerName + "/backendAddressPools/all"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -26,6 +26,7 @@ type azureclient interface {
|
|||||||
GetState() (state.ConstellationState, error)
|
GetState() (state.ConstellationState, error)
|
||||||
SetState(state.ConstellationState) error
|
SetState(state.ConstellationState) error
|
||||||
CreateResourceGroup(ctx context.Context) error
|
CreateResourceGroup(ctx context.Context) error
|
||||||
|
CreateExternalLoadBalancer(ctx context.Context) error
|
||||||
CreateVirtualNetwork(ctx context.Context) error
|
CreateVirtualNetwork(ctx context.Context) error
|
||||||
CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error
|
CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error
|
||||||
CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error
|
CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error
|
||||||
|
@ -24,6 +24,7 @@ type fakeAzureClient struct {
|
|||||||
subscriptionID string
|
subscriptionID string
|
||||||
tenantID string
|
tenantID string
|
||||||
subnetID string
|
subnetID string
|
||||||
|
loadBalancerName string
|
||||||
coordinatorsScaleSet string
|
coordinatorsScaleSet string
|
||||||
nodesScaleSet string
|
nodesScaleSet string
|
||||||
networkSecurityGroup string
|
networkSecurityGroup string
|
||||||
@ -77,6 +78,11 @@ func (c *fakeAzureClient) CreateVirtualNetwork(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *fakeAzureClient) CreateExternalLoadBalancer(ctx context.Context) error {
|
||||||
|
c.loadBalancerName = "loadBalancer"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error {
|
func (c *fakeAzureClient) CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error {
|
||||||
c.networkSecurityGroup = "network-security-group"
|
c.networkSecurityGroup = "network-security-group"
|
||||||
return nil
|
return nil
|
||||||
@ -152,6 +158,7 @@ type stubAzureClient struct {
|
|||||||
createResourceGroupErr error
|
createResourceGroupErr error
|
||||||
createVirtualNetworkErr error
|
createVirtualNetworkErr error
|
||||||
createSecurityGroupErr error
|
createSecurityGroupErr error
|
||||||
|
createLoadBalancerErr error
|
||||||
createInstancesErr error
|
createInstancesErr error
|
||||||
createServicePrincipalErr error
|
createServicePrincipalErr error
|
||||||
terminateResourceGroupErr error
|
terminateResourceGroupErr error
|
||||||
@ -166,6 +173,10 @@ func (c *stubAzureClient) SetState(state.ConstellationState) error {
|
|||||||
return c.setStateErr
|
return c.setStateErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *stubAzureClient) CreateExternalLoadBalancer(ctx context.Context) error {
|
||||||
|
return c.createLoadBalancerErr
|
||||||
|
}
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateResourceGroup(ctx context.Context) error {
|
func (c *stubAzureClient) CreateResourceGroup(ctx context.Context) error {
|
||||||
return c.createResourceGroupErr
|
return c.createResourceGroupErr
|
||||||
}
|
}
|
||||||
@ -271,11 +282,9 @@ func (c *fakeGcpClient) CreateFirewall(ctx context.Context, input gcpcl.Firewall
|
|||||||
if c.network == "" {
|
if c.network == "" {
|
||||||
return errors.New("client has not network")
|
return errors.New("client has not network")
|
||||||
}
|
}
|
||||||
var firewalls []string
|
|
||||||
for _, rule := range input.Ingress {
|
for _, rule := range input.Ingress {
|
||||||
firewalls = append(firewalls, rule.Name)
|
c.firewalls = append(c.firewalls, rule.Name)
|
||||||
}
|
}
|
||||||
c.firewalls = firewalls
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +82,45 @@ func (c *Creator) createGCP(ctx context.Context, cl gcpclient, config *config.Co
|
|||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// additionally create allow-internal rules
|
||||||
|
internalFirewallInput := gcpcl.FirewallInput{
|
||||||
|
Ingress: cloudtypes.Firewall{
|
||||||
|
{
|
||||||
|
Name: "allow-cluster-internal-tcp",
|
||||||
|
Protocol: "tcp",
|
||||||
|
IPRange: gcpcl.SubnetExtCIDR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "allow-cluster-internal-udp",
|
||||||
|
Protocol: "udp",
|
||||||
|
IPRange: gcpcl.SubnetExtCIDR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "allow-cluster-internal-icmp",
|
||||||
|
Protocol: "icmp",
|
||||||
|
IPRange: gcpcl.SubnetExtCIDR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "allow-node-internal-tcp",
|
||||||
|
Protocol: "tcp",
|
||||||
|
IPRange: gcpcl.SubnetCIDR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "allow-node-internal-udp",
|
||||||
|
Protocol: "udp",
|
||||||
|
IPRange: gcpcl.SubnetCIDR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "allow-node-internal-icmp",
|
||||||
|
Protocol: "icmp",
|
||||||
|
IPRange: gcpcl.SubnetCIDR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := cl.CreateFirewall(ctx, internalFirewallInput); err != nil {
|
||||||
|
return state.ConstellationState{}, err
|
||||||
|
}
|
||||||
|
|
||||||
createInput := client.CreateInstancesInput{
|
createInput := client.CreateInstancesInput{
|
||||||
CountCoordinators: coordCount,
|
CountCoordinators: coordCount,
|
||||||
CountNodes: nodeCount,
|
CountNodes: nodeCount,
|
||||||
@ -104,6 +143,9 @@ func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *confi
|
|||||||
if err := cl.CreateResourceGroup(ctx); err != nil {
|
if err := cl.CreateResourceGroup(ctx); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
if err := cl.CreateExternalLoadBalancer(ctx); err != nil {
|
||||||
|
return state.ConstellationState{}, err
|
||||||
|
}
|
||||||
if err := cl.CreateVirtualNetwork(ctx); err != nil {
|
if err := cl.CreateVirtualNetwork(ctx); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,11 @@ func TestCreator(t *testing.T) {
|
|||||||
GCPCoordinatorInstanceTemplate: "coordinator-template",
|
GCPCoordinatorInstanceTemplate: "coordinator-template",
|
||||||
GCPNetwork: "network",
|
GCPNetwork: "network",
|
||||||
GCPSubnetwork: "subnetwork",
|
GCPSubnetwork: "subnetwork",
|
||||||
GCPFirewalls: []string{"coordinator", "wireguard", "ssh", "nodeport"},
|
GCPFirewalls: []string{
|
||||||
|
"coordinator", "wireguard", "ssh", "nodeport", "kubernetes",
|
||||||
|
"allow-cluster-internal-tcp", "allow-cluster-internal-udp", "allow-cluster-internal-icmp",
|
||||||
|
"allow-node-internal-tcp", "allow-node-internal-udp", "allow-node-internal-icmp",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
wantAzureState := state.ConstellationState{
|
wantAzureState := state.ConstellationState{
|
||||||
|
@ -19,25 +19,30 @@ type Firewall config.Firewall
|
|||||||
func (f Firewall) GCP() ([]*computepb.Firewall, error) {
|
func (f Firewall) GCP() ([]*computepb.Firewall, error) {
|
||||||
var fw []*computepb.Firewall
|
var fw []*computepb.Firewall
|
||||||
for _, rule := range f {
|
for _, rule := range f {
|
||||||
var destRange []string = nil
|
var srcRange []string
|
||||||
if rule.IPRange != "" {
|
if rule.IPRange != "" {
|
||||||
destRange = append(destRange, rule.IPRange)
|
srcRange = []string{rule.IPRange}
|
||||||
}
|
}
|
||||||
|
|
||||||
ports, err := portOrRange(rule.FromPort, rule.ToPort)
|
var ports []string
|
||||||
if err != nil {
|
if rule.FromPort != 0 || rule.ToPort != 0 {
|
||||||
return nil, err
|
port, err := portOrRange(rule.FromPort, rule.ToPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ports = []string{port}
|
||||||
}
|
}
|
||||||
|
|
||||||
fw = append(fw, &computepb.Firewall{
|
fw = append(fw, &computepb.Firewall{
|
||||||
Allowed: []*computepb.Allowed{
|
Allowed: []*computepb.Allowed{
|
||||||
{
|
{
|
||||||
IPProtocol: proto.String(rule.Protocol),
|
IPProtocol: proto.String(rule.Protocol),
|
||||||
Ports: []string{ports},
|
Ports: ports,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Description: proto.String(rule.Description),
|
Description: proto.String(rule.Description),
|
||||||
DestinationRanges: destRange,
|
SourceRanges: srcRange,
|
||||||
Name: proto.String(rule.Name),
|
Name: proto.String(rule.Name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return fw, nil
|
return fw, nil
|
||||||
|
@ -29,11 +29,18 @@ func TestFirewallGCP(t *testing.T) {
|
|||||||
IPRange: "",
|
IPRange: "",
|
||||||
FromPort: 51820,
|
FromPort: 51820,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "test-3",
|
||||||
|
Description: "This is the Test-3 Permission",
|
||||||
|
Protocol: "tcp",
|
||||||
|
IPRange: "192.0.2.0/24",
|
||||||
|
FromPort: 4000,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
firewalls, err := testFw.GCP()
|
firewalls, err := testFw.GCP()
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(2, len(firewalls))
|
assert.Equal(len(testFw), len(firewalls))
|
||||||
|
|
||||||
// Check permissions
|
// Check permissions
|
||||||
for i := 0; i < len(testFw); i++ {
|
for i := 0; i < len(testFw); i++ {
|
||||||
@ -47,6 +54,11 @@ func TestFirewallGCP(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(testFw[i].Name, firewall1.GetName())
|
assert.Equal(testFw[i].Name, firewall1.GetName())
|
||||||
assert.Equal(testFw[i].Description, firewall1.GetDescription())
|
assert.Equal(testFw[i].Description, firewall1.GetDescription())
|
||||||
|
|
||||||
|
if testFw[i].IPRange != "" {
|
||||||
|
require.Len(firewall1.GetSourceRanges(), 1)
|
||||||
|
assert.Equal(testFw[i].IPRange, firewall1.GetSourceRanges()[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +357,9 @@ func readOrGenerateVPNKey(fileHandler file.Handler, privKeyPath string) (privKey
|
|||||||
func ipsToEndpoints(ips []string, port string) []string {
|
func ipsToEndpoints(ips []string, port string) []string {
|
||||||
var endpoints []string
|
var endpoints []string
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
|
if ip == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
endpoints = append(endpoints, net.JoinHostPort(ip, port))
|
endpoints = append(endpoints, net.JoinHostPort(ip, port))
|
||||||
}
|
}
|
||||||
return endpoints
|
return endpoints
|
||||||
|
@ -355,7 +355,7 @@ func TestWriteOutput(t *testing.T) {
|
|||||||
func TestIpsToEndpoints(t *testing.T) {
|
func TestIpsToEndpoints(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ips := []string{"192.0.2.1", "192.0.2.2", "192.0.2.3"}
|
ips := []string{"192.0.2.1", "192.0.2.2", "", "192.0.2.3"}
|
||||||
port := "8080"
|
port := "8080"
|
||||||
endpoints := ipsToEndpoints(ips, port)
|
endpoints := ipsToEndpoints(ips, port)
|
||||||
assert.Equal([]string{"192.0.2.1:8080", "192.0.2.2:8080", "192.0.2.3:8080"}, endpoints)
|
assert.Equal([]string{"192.0.2.1:8080", "192.0.2.2:8080", "192.0.2.3:8080"}, endpoints)
|
||||||
|
@ -48,18 +48,19 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
|
|||||||
c.nodeTemplate = nodeTemplateInput.Name
|
c.nodeTemplate = nodeTemplateInput.Name
|
||||||
|
|
||||||
coordinatorTemplateInput := insertInstanceTemplateInput{
|
coordinatorTemplateInput := insertInstanceTemplateInput{
|
||||||
Name: c.name + "-control-plane-" + c.uid,
|
Name: c.name + "-control-plane-" + c.uid,
|
||||||
Network: c.network,
|
Network: c.network,
|
||||||
Subnetwork: c.subnetwork,
|
Subnetwork: c.subnetwork,
|
||||||
ImageId: input.ImageId,
|
SecondarySubnetworkRangeName: c.secondarySubnetworkRange,
|
||||||
InstanceType: input.InstanceType,
|
ImageId: input.ImageId,
|
||||||
StateDiskSizeGB: int64(input.StateDiskSizeGB),
|
InstanceType: input.InstanceType,
|
||||||
Role: role.Coordinator.String(),
|
StateDiskSizeGB: int64(input.StateDiskSizeGB),
|
||||||
KubeEnv: input.KubeEnv,
|
Role: role.Coordinator.String(),
|
||||||
Project: c.project,
|
KubeEnv: input.KubeEnv,
|
||||||
Zone: c.zone,
|
Project: c.project,
|
||||||
Region: c.region,
|
Zone: c.zone,
|
||||||
UID: c.uid,
|
Region: c.region,
|
||||||
|
UID: c.uid,
|
||||||
}
|
}
|
||||||
op, err = c.insertInstanceTemplate(ctx, coordinatorTemplateInput)
|
op, err = c.insertInstanceTemplate(ctx, coordinatorTemplateInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -72,6 +73,21 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
|
|||||||
}
|
}
|
||||||
ops = []Operation{}
|
ops = []Operation{}
|
||||||
|
|
||||||
|
coordinatorGroupInput := instanceGroupManagerInput{
|
||||||
|
Count: input.CountCoordinators,
|
||||||
|
Name: strings.Join([]string{c.name, "control-plane", c.uid}, "-"),
|
||||||
|
Template: c.coordinatorTemplate,
|
||||||
|
UID: c.uid,
|
||||||
|
Project: c.project,
|
||||||
|
Zone: c.zone,
|
||||||
|
}
|
||||||
|
op, err = c.insertInstanceGroupManger(ctx, coordinatorGroupInput)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inserting instanceGroupManager failed: %w", err)
|
||||||
|
}
|
||||||
|
ops = append(ops, op)
|
||||||
|
c.coordinatorInstanceGroup = coordinatorGroupInput.Name
|
||||||
|
|
||||||
nodeGroupInput := instanceGroupManagerInput{
|
nodeGroupInput := instanceGroupManagerInput{
|
||||||
Count: input.CountNodes,
|
Count: input.CountNodes,
|
||||||
Name: strings.Join([]string{c.name, "worker", c.uid}, "-"),
|
Name: strings.Join([]string{c.name, "worker", c.uid}, "-"),
|
||||||
@ -87,20 +103,6 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
|
|||||||
ops = append(ops, op)
|
ops = append(ops, op)
|
||||||
c.nodesInstanceGroup = nodeGroupInput.Name
|
c.nodesInstanceGroup = nodeGroupInput.Name
|
||||||
|
|
||||||
coordinatorGroupInput := instanceGroupManagerInput{
|
|
||||||
Count: input.CountCoordinators,
|
|
||||||
Name: strings.Join([]string{c.name, "control-plane", c.uid}, "-"),
|
|
||||||
Template: c.coordinatorTemplate,
|
|
||||||
UID: c.uid,
|
|
||||||
Project: c.project,
|
|
||||||
Zone: c.zone,
|
|
||||||
}
|
|
||||||
op, err = c.insertInstanceGroupManger(ctx, coordinatorGroupInput)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("inserting instanceGroupManager failed: %w", err)
|
|
||||||
}
|
|
||||||
ops = append(ops, op)
|
|
||||||
c.coordinatorInstanceGroup = coordinatorGroupInput.Name
|
|
||||||
if err := c.waitForOperations(ctx, ops); err != nil {
|
if err := c.waitForOperations(ctx, ops); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -390,7 +392,7 @@ func (i insertInstanceTemplateInput) insertInstanceTemplateRequest() *computepb.
|
|||||||
EnableVtpm: proto.Bool(true),
|
EnableVtpm: proto.Bool(true),
|
||||||
},
|
},
|
||||||
Tags: &computepb.Tags{
|
Tags: &computepb.Tags{
|
||||||
Items: []string{"constellation"},
|
Items: []string{"constellation-" + i.UID},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SubnetCIDR = "192.168.178.0/24"
|
||||||
|
SubnetExtCIDR = "10.10.0.0/16"
|
||||||
|
)
|
||||||
|
|
||||||
// CreateFirewall creates a set of firewall rules for the client's network.
|
// CreateFirewall creates a set of firewall rules for the client's network.
|
||||||
//
|
//
|
||||||
// The client must have a VPC network to set firewall rules.
|
// The client must have a VPC network to set firewall rules.
|
||||||
@ -163,13 +168,13 @@ func (c *Client) createSubnet(ctx context.Context, name, network, secondaryRange
|
|||||||
Project: c.project,
|
Project: c.project,
|
||||||
Region: c.region,
|
Region: c.region,
|
||||||
SubnetworkResource: &computepb.Subnetwork{
|
SubnetworkResource: &computepb.Subnetwork{
|
||||||
IpCidrRange: proto.String("192.168.178.0/24"),
|
IpCidrRange: proto.String(SubnetCIDR),
|
||||||
Name: proto.String(name),
|
Name: proto.String(name),
|
||||||
Network: proto.String("projects/" + c.project + "/global/networks/" + network),
|
Network: proto.String("projects/" + c.project + "/global/networks/" + network),
|
||||||
SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{
|
SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{
|
||||||
{
|
{
|
||||||
RangeName: proto.String(secondaryRangeName),
|
RangeName: proto.String(secondaryRangeName),
|
||||||
IpCidrRange: proto.String("10.10.0.0/16"),
|
IpCidrRange: proto.String(SubnetExtCIDR),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aws
|
package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
@ -15,7 +15,7 @@ func (a Autoscaler) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
func (a Autoscaler) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (a Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
return resources.Secrets{}, nil
|
return resources.Secrets{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package aws
|
package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
@ -32,13 +34,13 @@ func (c CloudControllerManager) ExtraArgs() []string {
|
|||||||
|
|
||||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||||
func (c CloudControllerManager) ConfigMaps(instance core.Instance) (resources.ConfigMaps, error) {
|
func (c CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||||
return resources.ConfigMaps{}, nil
|
return resources.ConfigMaps{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||||
func (c CloudControllerManager) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (c CloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
return resources.Secrets{}, nil
|
return resources.Secrets{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,13 +60,6 @@ func (c CloudControllerManager) Env() []k8s.EnvVar {
|
|||||||
return []k8s.EnvVar{}
|
return []k8s.EnvVar{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
|
||||||
// Allows for cloud-provider specific hooks.
|
|
||||||
func (c CloudControllerManager) PrepareInstance(instance core.Instance, vpnIP string) error {
|
|
||||||
// no specific hook required.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
||||||
func (c CloudControllerManager) Supported() bool {
|
func (c CloudControllerManager) Supported() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,30 +14,30 @@ import (
|
|||||||
type Metadata struct{}
|
type Metadata struct{}
|
||||||
|
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
func (m Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (m Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
// TODO: implement using https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#Client.DescribeInstances
|
// TODO: implement using https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#Client.DescribeInstances
|
||||||
// And using AWS ec2 instance tags
|
// And using AWS ec2 instance tags
|
||||||
panic("function *Metadata.List not implemented")
|
panic("function *Metadata.List not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
func (m Metadata) Self(ctx context.Context) (core.Instance, error) {
|
func (m Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
identityDocument, err := retrieveIdentityDocument(ctx)
|
identityDocument, err := retrieveIdentityDocument(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
// TODO: implement metadata using AWS ec2 instance tags
|
// TODO: implement metadata using AWS ec2 instance tags
|
||||||
return core.Instance{
|
return cloudtypes.Instance{
|
||||||
Name: identityDocument.InstanceID,
|
Name: identityDocument.InstanceID,
|
||||||
ProviderID: providerID(identityDocument),
|
ProviderID: providerID(identityDocument),
|
||||||
IPs: []string{
|
PrivateIPs: []string{
|
||||||
identityDocument.PrivateIP,
|
identityDocument.PrivateIP,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance retrieves an instance using its providerID.
|
// GetInstance retrieves an instance using its providerID.
|
||||||
func (m Metadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
// TODO: implement using https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#DescribeInstancesAPIClient.DescribeInstances
|
// TODO: implement using https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#DescribeInstancesAPIClient.DescribeInstances
|
||||||
// And using AWS ec2 instance tags
|
// And using AWS ec2 instance tags
|
||||||
// Filter request to only return info on this instance
|
// Filter request to only return info on this instance
|
||||||
|
@ -12,6 +12,24 @@ type imdsAPI interface {
|
|||||||
Retrieve(ctx context.Context) (metadataResponse, error)
|
Retrieve(ctx context.Context) (metadataResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type virtualNetworksClientListPager interface {
|
||||||
|
NextPage(ctx context.Context) bool
|
||||||
|
PageResponse() armnetwork.VirtualNetworksClientListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type virtualNetworksAPI interface {
|
||||||
|
List(resourceGroupName string, options *armnetwork.VirtualNetworksClientListOptions) virtualNetworksClientListPager
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityGroupsClientListPager interface {
|
||||||
|
NextPage(ctx context.Context) bool
|
||||||
|
PageResponse() armnetwork.SecurityGroupsClientListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityGroupsAPI interface {
|
||||||
|
List(resourceGroupName string, options *armnetwork.SecurityGroupsClientListOptions) securityGroupsClientListPager
|
||||||
|
}
|
||||||
|
|
||||||
type networkInterfacesAPI interface {
|
type networkInterfacesAPI interface {
|
||||||
GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName string,
|
GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName string,
|
||||||
virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string,
|
virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string,
|
||||||
@ -21,6 +39,16 @@ type networkInterfacesAPI interface {
|
|||||||
options *armnetwork.InterfacesClientGetOptions) (armnetwork.InterfacesClientGetResponse, error)
|
options *armnetwork.InterfacesClientGetOptions) (armnetwork.InterfacesClientGetResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type publicIPAddressesAPI interface {
|
||||||
|
GetVirtualMachineScaleSetPublicIPAddress(ctx context.Context, resourceGroupName string,
|
||||||
|
virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string,
|
||||||
|
ipConfigurationName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressOptions,
|
||||||
|
) (armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse, error)
|
||||||
|
Get(ctx context.Context, resourceGroupName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetOptions) (armnetwork.PublicIPAddressesClientGetResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
type virtualMachinesAPI interface {
|
type virtualMachinesAPI interface {
|
||||||
Get(ctx context.Context, resourceGroupName string, vmName string, options *armcompute.VirtualMachinesClientGetOptions) (armcompute.VirtualMachinesClientGetResponse, error)
|
Get(ctx context.Context, resourceGroupName string, vmName string, options *armcompute.VirtualMachinesClientGetOptions) (armcompute.VirtualMachinesClientGetResponse, error)
|
||||||
List(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) virtualMachinesClientListPager
|
List(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) virtualMachinesClientListPager
|
||||||
@ -45,6 +73,15 @@ type scaleSetsAPI interface {
|
|||||||
List(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) virtualMachineScaleSetsClientListPager
|
List(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) virtualMachineScaleSetsClientListPager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loadBalancersClientListPager interface {
|
||||||
|
NextPage(ctx context.Context) bool
|
||||||
|
PageResponse() armnetwork.LoadBalancersClientListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadBalancerAPI interface {
|
||||||
|
List(resourceGroupName string, options *armnetwork.LoadBalancersClientListOptions) loadBalancersClientListPager
|
||||||
|
}
|
||||||
|
|
||||||
type virtualMachineScaleSetsClientListPager interface {
|
type virtualMachineScaleSetsClientListPager interface {
|
||||||
NextPage(ctx context.Context) bool
|
NextPage(ctx context.Context) bool
|
||||||
PageResponse() armcompute.VirtualMachineScaleSetsClientListResponse
|
PageResponse() armcompute.VirtualMachineScaleSetsClientListResponse
|
||||||
|
@ -174,3 +174,121 @@ func (a *stubTagsAPI) CreateOrUpdateAtScope(ctx context.Context, scope string, p
|
|||||||
func (a *stubTagsAPI) UpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsPatchResource, options *armresources.TagsClientUpdateAtScopeOptions) (armresources.TagsClientUpdateAtScopeResponse, error) {
|
func (a *stubTagsAPI) UpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsPatchResource, options *armresources.TagsClientUpdateAtScopeOptions) (armresources.TagsClientUpdateAtScopeResponse, error) {
|
||||||
return armresources.TagsClientUpdateAtScopeResponse{}, a.updateAtScopeErr
|
return armresources.TagsClientUpdateAtScopeResponse{}, a.updateAtScopeErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stubSecurityGroupsClientListPager struct {
|
||||||
|
pagesCounter int
|
||||||
|
pages [][]*armnetwork.SecurityGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubSecurityGroupsClientListPager) NextPage(ctx context.Context) bool {
|
||||||
|
return p.pagesCounter < len(p.pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubSecurityGroupsClientListPager) PageResponse() armnetwork.SecurityGroupsClientListResponse {
|
||||||
|
if p.pagesCounter >= len(p.pages) {
|
||||||
|
return armnetwork.SecurityGroupsClientListResponse{}
|
||||||
|
}
|
||||||
|
p.pagesCounter = p.pagesCounter + 1
|
||||||
|
return armnetwork.SecurityGroupsClientListResponse{
|
||||||
|
SecurityGroupsClientListResult: armnetwork.SecurityGroupsClientListResult{
|
||||||
|
SecurityGroupListResult: armnetwork.SecurityGroupListResult{
|
||||||
|
Value: p.pages[p.pagesCounter-1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubSecurityGroupsAPI struct {
|
||||||
|
listPages [][]*armnetwork.SecurityGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubSecurityGroupsAPI) List(resourceGroupName string, options *armnetwork.SecurityGroupsClientListOptions) securityGroupsClientListPager {
|
||||||
|
return &stubSecurityGroupsClientListPager{
|
||||||
|
pages: a.listPages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubVirtualNetworksClientListPager struct {
|
||||||
|
pagesCounter int
|
||||||
|
pages [][]*armnetwork.VirtualNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubVirtualNetworksClientListPager) NextPage(ctx context.Context) bool {
|
||||||
|
return p.pagesCounter < len(p.pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubVirtualNetworksClientListPager) PageResponse() armnetwork.VirtualNetworksClientListResponse {
|
||||||
|
if p.pagesCounter >= len(p.pages) {
|
||||||
|
return armnetwork.VirtualNetworksClientListResponse{}
|
||||||
|
}
|
||||||
|
p.pagesCounter = p.pagesCounter + 1
|
||||||
|
return armnetwork.VirtualNetworksClientListResponse{
|
||||||
|
VirtualNetworksClientListResult: armnetwork.VirtualNetworksClientListResult{
|
||||||
|
VirtualNetworkListResult: armnetwork.VirtualNetworkListResult{
|
||||||
|
Value: p.pages[p.pagesCounter-1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubVirtualNetworksAPI struct {
|
||||||
|
listPages [][]*armnetwork.VirtualNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubVirtualNetworksAPI) List(resourceGroupName string, options *armnetwork.VirtualNetworksClientListOptions) virtualNetworksClientListPager {
|
||||||
|
return &stubVirtualNetworksClientListPager{
|
||||||
|
pages: a.listPages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubLoadBalancersClientListPager struct {
|
||||||
|
pagesCounter int
|
||||||
|
pages [][]*armnetwork.LoadBalancer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubLoadBalancersClientListPager) NextPage(ctx context.Context) bool {
|
||||||
|
return p.pagesCounter < len(p.pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *stubLoadBalancersClientListPager) PageResponse() armnetwork.LoadBalancersClientListResponse {
|
||||||
|
if p.pagesCounter >= len(p.pages) {
|
||||||
|
return armnetwork.LoadBalancersClientListResponse{}
|
||||||
|
}
|
||||||
|
p.pagesCounter = p.pagesCounter + 1
|
||||||
|
return armnetwork.LoadBalancersClientListResponse{
|
||||||
|
LoadBalancersClientListResult: armnetwork.LoadBalancersClientListResult{
|
||||||
|
LoadBalancerListResult: armnetwork.LoadBalancerListResult{
|
||||||
|
Value: p.pages[p.pagesCounter-1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubLoadBalancersAPI struct {
|
||||||
|
listPages [][]*armnetwork.LoadBalancer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubLoadBalancersAPI) List(resourceGroupName string, options *armnetwork.LoadBalancersClientListOptions) loadBalancersClientListPager {
|
||||||
|
return &stubLoadBalancersClientListPager{
|
||||||
|
pages: a.listPages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubPublicIPAddressesAPI struct {
|
||||||
|
getResponse armnetwork.PublicIPAddressesClientGetResponse
|
||||||
|
getVirtualMachineScaleSetPublicIPAddressResponse armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse
|
||||||
|
getErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubPublicIPAddressesAPI) Get(ctx context.Context, resourceGroupName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetOptions,
|
||||||
|
) (armnetwork.PublicIPAddressesClientGetResponse, error) {
|
||||||
|
return a.getResponse, a.getErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubPublicIPAddressesAPI) GetVirtualMachineScaleSetPublicIPAddress(ctx context.Context, resourceGroupName string, virtualMachineScaleSetName string,
|
||||||
|
virtualmachineIndex string, networkInterfaceName string, IPConfigurationName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressOptions,
|
||||||
|
) (armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse, error) {
|
||||||
|
return a.getVirtualMachineScaleSetPublicIPAddressResponse, a.getErr
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -16,7 +16,7 @@ func (a *Autoscaler) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
func (a *Autoscaler) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (a *Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
subscriptionID, resourceGroup, err := extractBasicsFromProviderID(instance.ProviderID)
|
subscriptionID, resourceGroup, err := extractBasicsFromProviderID(instance.ProviderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resources.Secrets{}, err
|
return resources.Secrets{}, err
|
||||||
|
@ -3,7 +3,7 @@ package azure
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -13,13 +13,13 @@ import (
|
|||||||
|
|
||||||
func TestAutoscalerSecrets(t *testing.T) {
|
func TestAutoscalerSecrets(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
instance core.Instance
|
instance cloudtypes.Instance
|
||||||
cloudServiceAccountURI string
|
cloudServiceAccountURI string
|
||||||
wantSecrets resources.Secrets
|
wantSecrets resources.Secrets
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"Secrets works": {
|
"Secrets works": {
|
||||||
instance: core.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret",
|
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret",
|
||||||
wantSecrets: resources.Secrets{
|
wantSecrets: resources.Secrets{
|
||||||
&k8s.Secret{
|
&k8s.Secret{
|
||||||
@ -43,11 +43,11 @@ func TestAutoscalerSecrets(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"invalid providerID fails": {
|
"invalid providerID fails": {
|
||||||
instance: core.Instance{ProviderID: "invalid"},
|
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"invalid cloudServiceAccountURI fails": {
|
"invalid cloudServiceAccountURI fails": {
|
||||||
instance: core.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||||
cloudServiceAccountURI: "invalid",
|
cloudServiceAccountURI: "invalid",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ccmMetadata interface {
|
||||||
|
GetNetworkSecurityGroupName(ctx context.Context) (string, error)
|
||||||
|
GetLoadBalancerName(ctx context.Context) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
// CloudControllerManager holds the Azure cloud-controller-manager configuration.
|
// CloudControllerManager holds the Azure cloud-controller-manager configuration.
|
||||||
type CloudControllerManager struct{}
|
type CloudControllerManager struct {
|
||||||
|
metadata ccmMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCloudControllerManager(metadata ccmMetadata) *CloudControllerManager {
|
||||||
|
return &CloudControllerManager{
|
||||||
|
metadata: metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
|
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
|
||||||
func (c *CloudControllerManager) Image() string {
|
func (c *CloudControllerManager) Image() string {
|
||||||
@ -33,18 +47,20 @@ func (c *CloudControllerManager) ExtraArgs() []string {
|
|||||||
return []string{
|
return []string{
|
||||||
"--controllers=*,-cloud-node",
|
"--controllers=*,-cloud-node",
|
||||||
"--cloud-config=/etc/azure/azure.json",
|
"--cloud-config=/etc/azure/azure.json",
|
||||||
|
"--allocate-node-cidrs=false",
|
||||||
|
"--configure-cloud-routes=true",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||||
func (c *CloudControllerManager) ConfigMaps(instance core.Instance) (resources.ConfigMaps, error) {
|
func (c *CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||||
return resources.ConfigMaps{}, nil
|
return resources.ConfigMaps{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||||
func (c *CloudControllerManager) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
// Azure CCM expects cloud provider config to contain cluster configuration and service principal client secrets
|
// Azure CCM expects cloud provider config to contain cluster configuration and service principal client secrets
|
||||||
// reference: https://kubernetes-sigs.github.io/cloud-provider-azure/install/configs/
|
// reference: https://kubernetes-sigs.github.io/cloud-provider-azure/install/configs/
|
||||||
|
|
||||||
@ -62,11 +78,24 @@ func (c *CloudControllerManager) Secrets(instance core.Instance, cloudServiceAcc
|
|||||||
vmType = "vmss"
|
vmType = "vmss"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
securityGroupName, err := c.metadata.GetNetworkSecurityGroupName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return resources.Secrets{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
loadBalancerName, err := c.metadata.GetLoadBalancerName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return resources.Secrets{}, err
|
||||||
|
}
|
||||||
|
|
||||||
config := cloudConfig{
|
config := cloudConfig{
|
||||||
Cloud: "AzurePublicCloud",
|
Cloud: "AzurePublicCloud",
|
||||||
TenantID: creds.TenantID,
|
TenantID: creds.TenantID,
|
||||||
SubscriptionID: subscriptionID,
|
SubscriptionID: subscriptionID,
|
||||||
ResourceGroup: resourceGroup,
|
ResourceGroup: resourceGroup,
|
||||||
|
LoadBalancerSku: "standard",
|
||||||
|
SecurityGroupName: securityGroupName,
|
||||||
|
LoadBalancerName: loadBalancerName,
|
||||||
UseInstanceMetadata: true,
|
UseInstanceMetadata: true,
|
||||||
VmType: vmType,
|
VmType: vmType,
|
||||||
Location: creds.Location,
|
Location: creds.Location,
|
||||||
@ -127,13 +156,6 @@ func (c *CloudControllerManager) Env() []k8s.EnvVar {
|
|||||||
return []k8s.EnvVar{}
|
return []k8s.EnvVar{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
|
||||||
// Allows for cloud-provider specific hooks.
|
|
||||||
func (c *CloudControllerManager) PrepareInstance(instance core.Instance, vpnIP string) error {
|
|
||||||
// no specific hook required.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
||||||
func (c *CloudControllerManager) Supported() bool {
|
func (c *CloudControllerManager) Supported() bool {
|
||||||
return true
|
return true
|
||||||
@ -148,6 +170,8 @@ type cloudConfig struct {
|
|||||||
SubnetName string `json:"subnetName,omitempty"`
|
SubnetName string `json:"subnetName,omitempty"`
|
||||||
SecurityGroupName string `json:"securityGroupName,omitempty"`
|
SecurityGroupName string `json:"securityGroupName,omitempty"`
|
||||||
SecurityGroupResourceGroup string `json:"securityGroupResourceGroup,omitempty"`
|
SecurityGroupResourceGroup string `json:"securityGroupResourceGroup,omitempty"`
|
||||||
|
LoadBalancerName string `json:"loadBalancerName,omitempty"`
|
||||||
|
LoadBalancerSku string `json:"loadBalancerSku,omitempty"`
|
||||||
VNetName string `json:"vnetName,omitempty"`
|
VNetName string `json:"vnetName,omitempty"`
|
||||||
VNetResourceGroup string `json:"vnetResourceGroup,omitempty"`
|
VNetResourceGroup string `json:"vnetResourceGroup,omitempty"`
|
||||||
CloudProviderBackoff bool `json:"cloudProviderBackoff,omitempty"`
|
CloudProviderBackoff bool `json:"cloudProviderBackoff,omitempty"`
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -12,15 +14,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSecrets(t *testing.T) {
|
func TestSecrets(t *testing.T) {
|
||||||
|
someErr := errors.New("some error")
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
instance core.Instance
|
instance cloudtypes.Instance
|
||||||
|
metadata ccmMetadata
|
||||||
cloudServiceAccountURI string
|
cloudServiceAccountURI string
|
||||||
wantSecrets resources.Secrets
|
wantSecrets resources.Secrets
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"Secrets works": {
|
"Secrets works": {
|
||||||
instance: core.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||||
|
metadata: &ccmMetadataStub{loadBalancerName: "load-balancer-name", networkSecurityGroupName: "network-security-group-name"},
|
||||||
wantSecrets: resources.Secrets{
|
wantSecrets: resources.Secrets{
|
||||||
&k8s.Secret{
|
&k8s.Secret{
|
||||||
TypeMeta: meta.TypeMeta{
|
TypeMeta: meta.TypeMeta{
|
||||||
@ -32,14 +37,15 @@ func TestSecrets(t *testing.T) {
|
|||||||
Namespace: "kube-system",
|
Namespace: "kube-system",
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"azure.json": []byte(`{"cloud":"AzurePublicCloud","tenantId":"tenant-id","subscriptionId":"subscription-id","resourceGroup":"resource-group","location":"location","useInstanceMetadata":true,"vmType":"standard","aadClientId":"client-id","aadClientSecret":"client-secret"}`),
|
"azure.json": []byte(`{"cloud":"AzurePublicCloud","tenantId":"tenant-id","subscriptionId":"subscription-id","resourceGroup":"resource-group","location":"location","securityGroupName":"network-security-group-name","loadBalancerName":"load-balancer-name","loadBalancerSku":"standard","useInstanceMetadata":true,"vmType":"standard","aadClientId":"client-id","aadClientSecret":"client-secret"}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Secrets works for scale sets": {
|
"Secrets works for scale sets": {
|
||||||
instance: core.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||||
|
metadata: &ccmMetadataStub{loadBalancerName: "load-balancer-name", networkSecurityGroupName: "network-security-group-name"},
|
||||||
wantSecrets: resources.Secrets{
|
wantSecrets: resources.Secrets{
|
||||||
&k8s.Secret{
|
&k8s.Secret{
|
||||||
TypeMeta: meta.TypeMeta{
|
TypeMeta: meta.TypeMeta{
|
||||||
@ -51,17 +57,31 @@ func TestSecrets(t *testing.T) {
|
|||||||
Namespace: "kube-system",
|
Namespace: "kube-system",
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"azure.json": []byte(`{"cloud":"AzurePublicCloud","tenantId":"tenant-id","subscriptionId":"subscription-id","resourceGroup":"resource-group","location":"location","useInstanceMetadata":true,"vmType":"vmss","aadClientId":"client-id","aadClientSecret":"client-secret"}`),
|
"azure.json": []byte(`{"cloud":"AzurePublicCloud","tenantId":"tenant-id","subscriptionId":"subscription-id","resourceGroup":"resource-group","location":"location","securityGroupName":"network-security-group-name","loadBalancerName":"load-balancer-name","loadBalancerSku":"standard","useInstanceMetadata":true,"vmType":"vmss","aadClientId":"client-id","aadClientSecret":"client-secret"}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"cannot get load balancer Name": {
|
||||||
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||||
|
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||||
|
metadata: &ccmMetadataStub{getLoadBalancerNameErr: someErr},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"cannot get network security group name": {
|
||||||
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||||
|
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||||
|
metadata: &ccmMetadataStub{getNetworkSecurityGroupNameErr: someErr},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
"invalid providerID fails": {
|
"invalid providerID fails": {
|
||||||
instance: core.Instance{ProviderID: "invalid"},
|
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||||
|
metadata: &ccmMetadataStub{},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"invalid cloudServiceAccountURI fails": {
|
"invalid cloudServiceAccountURI fails": {
|
||||||
instance: core.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||||
|
metadata: &ccmMetadataStub{},
|
||||||
cloudServiceAccountURI: "invalid",
|
cloudServiceAccountURI: "invalid",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@ -72,8 +92,8 @@ func TestSecrets(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
cloud := CloudControllerManager{}
|
cloud := NewCloudControllerManager(tc.metadata)
|
||||||
secrets, err := cloud.Secrets(tc.instance, tc.cloudServiceAccountURI)
|
secrets, err := cloud.Secrets(context.Background(), tc.instance, tc.cloudServiceAccountURI)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
@ -92,10 +112,25 @@ func TestTrivialCCMFunctions(t *testing.T) {
|
|||||||
assert.NotEmpty(cloud.Path())
|
assert.NotEmpty(cloud.Path())
|
||||||
assert.NotEmpty(cloud.Name())
|
assert.NotEmpty(cloud.Name())
|
||||||
assert.NotEmpty(cloud.ExtraArgs())
|
assert.NotEmpty(cloud.ExtraArgs())
|
||||||
assert.Empty(cloud.ConfigMaps(core.Instance{}))
|
assert.Empty(cloud.ConfigMaps(cloudtypes.Instance{}))
|
||||||
assert.NotEmpty(cloud.Volumes())
|
assert.NotEmpty(cloud.Volumes())
|
||||||
assert.NotEmpty(cloud.VolumeMounts())
|
assert.NotEmpty(cloud.VolumeMounts())
|
||||||
assert.Empty(cloud.Env())
|
assert.Empty(cloud.Env())
|
||||||
assert.NoError(cloud.PrepareInstance(core.Instance{}, "192.0.2.0"))
|
|
||||||
assert.True(cloud.Supported())
|
assert.True(cloud.Supported())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ccmMetadataStub struct {
|
||||||
|
networkSecurityGroupName string
|
||||||
|
loadBalancerName string
|
||||||
|
|
||||||
|
getNetworkSecurityGroupNameErr error
|
||||||
|
getLoadBalancerNameErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ccmMetadataStub) GetNetworkSecurityGroupName(ctx context.Context) (string, error) {
|
||||||
|
return c.networkSecurityGroupName, c.getNetworkSecurityGroupNameErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ccmMetadataStub) GetLoadBalancerName(ctx context.Context) (string, error) {
|
||||||
|
return c.loadBalancerName, c.getLoadBalancerNameErr
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@ func (c *CloudNodeManager) Path() string {
|
|||||||
// ExtraArgs returns a list of arguments to append to the cloud-node-manager command.
|
// ExtraArgs returns a list of arguments to append to the cloud-node-manager command.
|
||||||
func (c *CloudNodeManager) ExtraArgs() []string {
|
func (c *CloudNodeManager) ExtraArgs() []string {
|
||||||
return []string{
|
return []string{
|
||||||
"--wait-routes=false",
|
"--wait-routes=true",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,15 +10,25 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
publicIPAddressRegexp = regexp.MustCompile(`/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft.Network/publicIPAddresses/(?P<IPname>[^/]+)`)
|
||||||
|
keyPathRegexp = regexp.MustCompile(`^\/home\/([^\/]+)\/\.ssh\/authorized_keys$`)
|
||||||
|
)
|
||||||
|
|
||||||
// Metadata implements azure metadata APIs.
|
// Metadata implements azure metadata APIs.
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
imdsAPI
|
imdsAPI
|
||||||
|
virtualNetworksAPI
|
||||||
|
securityGroupsAPI
|
||||||
networkInterfacesAPI
|
networkInterfacesAPI
|
||||||
|
publicIPAddressesAPI
|
||||||
scaleSetsAPI
|
scaleSetsAPI
|
||||||
|
loadBalancerAPI
|
||||||
virtualMachinesAPI
|
virtualMachinesAPI
|
||||||
virtualMachineScaleSetVMsAPI
|
virtualMachineScaleSetVMsAPI
|
||||||
tagsAPI
|
tagsAPI
|
||||||
@ -44,15 +54,23 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
virtualNetworksAPI := armnetwork.NewVirtualNetworksClient(subscriptionID, cred, nil)
|
||||||
networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
|
networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
|
||||||
|
publicIPAddressesAPI := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
|
||||||
|
securityGroupsAPI := armnetwork.NewSecurityGroupsClient(subscriptionID, cred, nil)
|
||||||
scaleSetsAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
|
scaleSetsAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
|
||||||
|
loadBalancerAPI := armnetwork.NewLoadBalancersClient(subscriptionID, cred, nil)
|
||||||
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
|
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
|
||||||
virtualMachineScaleSetVMsAPI := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil)
|
virtualMachineScaleSetVMsAPI := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil)
|
||||||
tagsAPI := armresources.NewTagsClient(subscriptionID, cred, nil)
|
tagsAPI := armresources.NewTagsClient(subscriptionID, cred, nil)
|
||||||
|
|
||||||
return &Metadata{
|
return &Metadata{
|
||||||
imdsAPI: &imdsAPI,
|
imdsAPI: &imdsAPI,
|
||||||
|
virtualNetworksAPI: &virtualNetworksClient{virtualNetworksAPI},
|
||||||
networkInterfacesAPI: &networkInterfacesClient{networkInterfacesAPI},
|
networkInterfacesAPI: &networkInterfacesClient{networkInterfacesAPI},
|
||||||
|
securityGroupsAPI: &securityGroupsClient{securityGroupsAPI},
|
||||||
|
publicIPAddressesAPI: &publicIPAddressesClient{publicIPAddressesAPI},
|
||||||
|
loadBalancerAPI: &loadBalancersClient{loadBalancerAPI},
|
||||||
scaleSetsAPI: &scaleSetsClient{scaleSetsAPI},
|
scaleSetsAPI: &scaleSetsClient{scaleSetsAPI},
|
||||||
virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI},
|
virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI},
|
||||||
virtualMachineScaleSetVMsAPI: &virtualMachineScaleSetVMsClient{virtualMachineScaleSetVMsAPI},
|
virtualMachineScaleSetVMsAPI: &virtualMachineScaleSetVMsClient{virtualMachineScaleSetVMsAPI},
|
||||||
@ -61,7 +79,7 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
func (m *Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
providerID, err := m.providerID(ctx)
|
providerID, err := m.providerID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -78,23 +96,23 @@ func (m *Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
instances := make([]core.Instance, 0, len(singleInstances)+len(scaleSetInstances))
|
instances := make([]cloudtypes.Instance, 0, len(singleInstances)+len(scaleSetInstances))
|
||||||
instances = append(instances, singleInstances...)
|
instances = append(instances, singleInstances...)
|
||||||
instances = append(instances, scaleSetInstances...)
|
instances = append(instances, scaleSetInstances...)
|
||||||
return instances, nil
|
return instances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
func (m *Metadata) Self(ctx context.Context) (core.Instance, error) {
|
func (m *Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
providerID, err := m.providerID(ctx)
|
providerID, err := m.providerID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
return m.GetInstance(ctx, providerID)
|
return m.GetInstance(ctx, providerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance retrieves an instance using its providerID.
|
// GetInstance retrieves an instance using its providerID.
|
||||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
instance, singleErr := m.getVM(ctx, providerID)
|
instance, singleErr := m.getVM(ctx, providerID)
|
||||||
if singleErr == nil {
|
if singleErr == nil {
|
||||||
return instance, nil
|
return instance, nil
|
||||||
@ -103,7 +121,7 @@ func (m *Metadata) GetInstance(ctx context.Context, providerID string) (core.Ins
|
|||||||
if scaleSetErr == nil {
|
if scaleSetErr == nil {
|
||||||
return instance, nil
|
return instance, nil
|
||||||
}
|
}
|
||||||
return core.Instance{}, fmt.Errorf("could not retrieve instance given providerID %v as either single vm or scale set vm: %v %v", providerID, singleErr, scaleSetErr)
|
return cloudtypes.Instance{}, fmt.Errorf("could not retrieve instance given providerID %v as either single vm or scale set vm: %v %v", providerID, singleErr, scaleSetErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignalRole signals the constellation role via cloud provider metadata.
|
// SignalRole signals the constellation role via cloud provider metadata.
|
||||||
@ -120,6 +138,135 @@ func (m *Metadata) SignalRole(ctx context.Context, role role.Role) error {
|
|||||||
return m.setTag(ctx, core.RoleMetadataKey, role.String())
|
return m.setTag(ctx, core.RoleMetadataKey, role.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNetworkSecurityGroupName returns the security group name of the resource group.
|
||||||
|
func (m *Metadata) GetNetworkSecurityGroupName(ctx context.Context) (string, error) {
|
||||||
|
providerID, err := m.providerID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, resourceGroup, err := extractBasicsFromProviderID(providerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
nsg, err := m.getNetworkSecurityGroup(ctx, resourceGroup)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if nsg == nil || nsg.Name == nil {
|
||||||
|
return "", fmt.Errorf("could not dereference network security group name")
|
||||||
|
}
|
||||||
|
return *nsg.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubnetworkCIDR retrieves the subnetwork CIDR from cloud provider metadata.
|
||||||
|
func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||||
|
providerID, err := m.providerID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, resourceGroup, err := extractBasicsFromProviderID(providerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
virtualNetwork, err := m.getVirtualNetwork(ctx, resourceGroup)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if virtualNetwork == nil || virtualNetwork.Properties == nil || len(virtualNetwork.Properties.Subnets) == 0 ||
|
||||||
|
virtualNetwork.Properties.Subnets[0].Properties == nil || virtualNetwork.Properties.Subnets[0].Properties.AddressPrefix == nil {
|
||||||
|
return "", fmt.Errorf("could not retrieve subnetwork CIDR from virtual network %v", virtualNetwork)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *virtualNetwork.Properties.Subnets[0].Properties.AddressPrefix, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLoadBalancer retrieves the load balancer from cloud provider metadata.
|
||||||
|
func (m *Metadata) getLoadBalancer(ctx context.Context) (*armnetwork.LoadBalancer, error) {
|
||||||
|
providerID, err := m.providerID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resourceGroup, err := extractBasicsFromProviderID(providerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pager := m.loadBalancerAPI.List(resourceGroup, nil)
|
||||||
|
|
||||||
|
for pager.NextPage(ctx) {
|
||||||
|
for _, lb := range pager.PageResponse().Value {
|
||||||
|
if lb != nil && lb.Properties != nil {
|
||||||
|
return lb, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not get any load balancer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
|
||||||
|
func (m *Metadata) SupportsLoadBalancer() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoadBalancerName returns the load balancer name of the resource group.
|
||||||
|
func (m *Metadata) GetLoadBalancerName(ctx context.Context) (string, error) {
|
||||||
|
lb, err := m.getLoadBalancer(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if lb == nil || lb.Name == nil {
|
||||||
|
return "", fmt.Errorf("could not dereference load balancer name")
|
||||||
|
}
|
||||||
|
return *lb.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoadBalancerIP retrieves the first load balancer IP from cloud provider metadata.
|
||||||
|
func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||||
|
lb, err := m.getLoadBalancer(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if lb == nil || lb.Properties == nil {
|
||||||
|
return "", fmt.Errorf("could not dereference load balancer IP configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubIPID string
|
||||||
|
for _, fipConf := range lb.Properties.FrontendIPConfigurations {
|
||||||
|
if fipConf == nil || fipConf.Properties == nil || fipConf.Properties.PublicIPAddress == nil || fipConf.Properties.PublicIPAddress.ID == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pubIPID = *fipConf.Properties.PublicIPAddress.ID
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if pubIPID == "" {
|
||||||
|
return "", fmt.Errorf("could not find public IP address reference in load balancer")
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := publicIPAddressRegexp.FindStringSubmatch(pubIPID)
|
||||||
|
if len(matches) != 2 {
|
||||||
|
return "", fmt.Errorf("could not find public IP address name in load balancer: %v", pubIPID)
|
||||||
|
}
|
||||||
|
pubIPName := matches[1]
|
||||||
|
|
||||||
|
providerID, err := m.providerID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, resourceGroup, err := extractBasicsFromProviderID(providerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err := m.publicIPAddressesAPI.Get(ctx, resourceGroup, pubIPName, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not retrieve public IP address: %w", err)
|
||||||
|
}
|
||||||
|
if resp.Properties == nil || resp.Properties.IPAddress == nil {
|
||||||
|
return "", fmt.Errorf("could not resolve public IP address reference for load balancer")
|
||||||
|
}
|
||||||
|
return *resp.Properties.IPAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (not required on azure).
|
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (not required on azure).
|
||||||
func (m *Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
func (m *Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
||||||
return nil
|
return nil
|
||||||
@ -166,7 +313,6 @@ func extractInstanceTags(tags map[string]*string) map[string]string {
|
|||||||
|
|
||||||
// extractSSHKeys extracts SSH public keys from azure instance OS Profile.
|
// extractSSHKeys extracts SSH public keys from azure instance OS Profile.
|
||||||
func extractSSHKeys(sshConfig armcompute.SSHConfiguration) map[string][]string {
|
func extractSSHKeys(sshConfig armcompute.SSHConfiguration) map[string][]string {
|
||||||
keyPathRegexp := regexp.MustCompile(`^\/home\/([^\/]+)\/\.ssh\/authorized_keys$`)
|
|
||||||
sshKeys := map[string][]string{}
|
sshKeys := map[string][]string{}
|
||||||
for _, key := range sshConfig.PublicKeys {
|
for _, key := range sshConfig.PublicKeys {
|
||||||
if key == nil || key.Path == nil || key.KeyData == nil {
|
if key == nil || key.Path == nil || key.KeyData == nil {
|
||||||
|
@ -8,24 +8,24 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
wantInstances := []core.Instance{
|
wantInstances := []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "scale-set-name-instance-id",
|
Name: "scale-set-name-instance-id",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ func TestList(t *testing.T) {
|
|||||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||||
tagsAPI tagsAPI
|
tagsAPI tagsAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstances []core.Instance
|
wantInstances []cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"List works": {
|
"List works": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -98,16 +98,16 @@ func TestList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSelf(t *testing.T) {
|
func TestSelf(t *testing.T) {
|
||||||
wantVMInstance := core.Instance{
|
wantVMInstance := cloudtypes.Instance{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
}
|
}
|
||||||
wantScaleSetInstance := core.Instance{
|
wantScaleSetInstance := cloudtypes.Instance{
|
||||||
Name: "scale-set-name-instance-id",
|
Name: "scale-set-name-instance-id",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
}
|
}
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
@ -116,7 +116,7 @@ func TestSelf(t *testing.T) {
|
|||||||
virtualMachinesAPI virtualMachinesAPI
|
virtualMachinesAPI virtualMachinesAPI
|
||||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"self for individual instance works": {
|
"self for individual instance works": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -210,6 +210,349 @@ func TestSignalRole(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetNetworkSecurityGroupName(t *testing.T) {
|
||||||
|
name := "network-security-group-name"
|
||||||
|
testCases := map[string]struct {
|
||||||
|
securityGroupsAPI securityGroupsAPI
|
||||||
|
imdsAPI imdsAPI
|
||||||
|
wantName string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"GetNetworkSecurityGroupName works": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
securityGroupsAPI: &stubSecurityGroupsAPI{
|
||||||
|
listPages: [][]*armnetwork.SecurityGroup{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(name),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantName: name,
|
||||||
|
},
|
||||||
|
"no security group": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
securityGroupsAPI: &stubSecurityGroupsAPI{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"missing name in security group struct": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
securityGroupsAPI: &stubSecurityGroupsAPI{listPages: [][]*armnetwork.SecurityGroup{{{}}}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
imdsAPI: tc.imdsAPI,
|
||||||
|
securityGroupsAPI: tc.securityGroupsAPI,
|
||||||
|
}
|
||||||
|
name, err := metadata.GetNetworkSecurityGroupName(context.Background())
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantName, name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSubnetworkCIDR(t *testing.T) {
|
||||||
|
subnetworkCIDR := "192.0.2.0/24"
|
||||||
|
name := "name"
|
||||||
|
testCases := map[string]struct {
|
||||||
|
virtualNetworksAPI virtualNetworksAPI
|
||||||
|
imdsAPI imdsAPI
|
||||||
|
wantNetworkCIDR string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"GetSubnetworkCIDR works": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
virtualNetworksAPI: &stubVirtualNetworksAPI{listPages: [][]*armnetwork.VirtualNetwork{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(name),
|
||||||
|
Properties: &armnetwork.VirtualNetworkPropertiesFormat{
|
||||||
|
Subnets: []*armnetwork.Subnet{
|
||||||
|
{Properties: &armnetwork.SubnetPropertiesFormat{AddressPrefix: to.StringPtr(subnetworkCIDR)}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantNetworkCIDR: subnetworkCIDR,
|
||||||
|
},
|
||||||
|
"no virtual networks found": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
virtualNetworksAPI: &stubVirtualNetworksAPI{listPages: [][]*armnetwork.VirtualNetwork{
|
||||||
|
{},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
wantNetworkCIDR: subnetworkCIDR,
|
||||||
|
},
|
||||||
|
"malformed network struct": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
virtualNetworksAPI: &stubVirtualNetworksAPI{listPages: [][]*armnetwork.VirtualNetwork{
|
||||||
|
{
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
wantNetworkCIDR: subnetworkCIDR,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
imdsAPI: tc.imdsAPI,
|
||||||
|
virtualNetworksAPI: tc.virtualNetworksAPI,
|
||||||
|
}
|
||||||
|
subnetworkCIDR, err := metadata.GetSubnetworkCIDR(context.Background())
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantNetworkCIDR, subnetworkCIDR)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLoadBalancerName(t *testing.T) {
|
||||||
|
loadBalancerName := "load-balancer-name"
|
||||||
|
testCases := map[string]struct {
|
||||||
|
loadBalancerAPI loadBalancerAPI
|
||||||
|
imdsAPI imdsAPI
|
||||||
|
wantName string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"GetLoadBalancerName works": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantName: loadBalancerName,
|
||||||
|
},
|
||||||
|
"invalid load balancer struct": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{listPages: [][]*armnetwork.LoadBalancer{{{}}}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"invalid missing name": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{listPages: [][]*armnetwork.LoadBalancer{{{
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{},
|
||||||
|
}}}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
imdsAPI: tc.imdsAPI,
|
||||||
|
loadBalancerAPI: tc.loadBalancerAPI,
|
||||||
|
}
|
||||||
|
loadbalancerName, err := metadata.GetLoadBalancerName(context.Background())
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantName, loadbalancerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLoadBalancerIP(t *testing.T) {
|
||||||
|
loadBalancerName := "load-balancer-name"
|
||||||
|
publicIP := "192.0.2.1"
|
||||||
|
correctPublicIPID := "/subscriptions/subscription/resourceGroups/resourceGroup/providers/Microsoft.Network/publicIPAddresses/pubIPName"
|
||||||
|
someErr := errors.New("some error")
|
||||||
|
testCases := map[string]struct {
|
||||||
|
loadBalancerAPI loadBalancerAPI
|
||||||
|
publicIPAddressesAPI publicIPAddressesAPI
|
||||||
|
imdsAPI imdsAPI
|
||||||
|
wantIP string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"GetLoadBalancerIP works": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: &correctPublicIPID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getResponse: armnetwork.PublicIPAddressesClientGetResponse{
|
||||||
|
PublicIPAddressesClientGetResult: armnetwork.PublicIPAddressesClientGetResult{
|
||||||
|
PublicIPAddress: armnetwork.PublicIPAddress{
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
|
||||||
|
IPAddress: &publicIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantIP: publicIP,
|
||||||
|
},
|
||||||
|
"no load balancer": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"load balancer missing public IP reference": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"public IP reference has wrong format": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: to.StringPtr("wrong-format"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"no public IP address found": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: &correctPublicIPID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getErr: someErr},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"found public IP has no address field": {
|
||||||
|
imdsAPI: newIMDSStub(),
|
||||||
|
loadBalancerAPI: &stubLoadBalancersAPI{
|
||||||
|
listPages: [][]*armnetwork.LoadBalancer{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr(loadBalancerName),
|
||||||
|
Properties: &armnetwork.LoadBalancerPropertiesFormat{
|
||||||
|
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: &correctPublicIPID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getResponse: armnetwork.PublicIPAddressesClientGetResponse{
|
||||||
|
PublicIPAddressesClientGetResult: armnetwork.PublicIPAddressesClientGetResult{
|
||||||
|
PublicIPAddress: armnetwork.PublicIPAddress{
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
imdsAPI: tc.imdsAPI,
|
||||||
|
loadBalancerAPI: tc.loadBalancerAPI,
|
||||||
|
publicIPAddressesAPI: tc.publicIPAddressesAPI,
|
||||||
|
}
|
||||||
|
loadbalancerName, err := metadata.GetLoadBalancerIP(context.Background())
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantIP, loadbalancerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetVPNIP(t *testing.T) {
|
func TestSetVPNIP(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
metadata := Metadata{}
|
metadata := Metadata{}
|
||||||
|
@ -11,53 +11,81 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// getVMInterfaces retrieves all network interfaces referenced by a virtual machine.
|
// getVMInterfaces retrieves all network interfaces referenced by a virtual machine.
|
||||||
func (m *Metadata) getVMInterfaces(ctx context.Context, vm armcompute.VirtualMachine, resourceGroup string) ([]*armnetwork.InterfaceIPConfiguration, error) {
|
func (m *Metadata) getVMInterfaces(ctx context.Context, vm armcompute.VirtualMachine, resourceGroup string) ([]armnetwork.Interface, error) {
|
||||||
if vm.Properties == nil || vm.Properties.NetworkProfile == nil {
|
if vm.Properties == nil || vm.Properties.NetworkProfile == nil {
|
||||||
return []*armnetwork.InterfaceIPConfiguration{}, nil
|
return []armnetwork.Interface{}, nil
|
||||||
}
|
}
|
||||||
interfaceNames := extractInterfaceNamesFromInterfaceReferences(vm.Properties.NetworkProfile.NetworkInterfaces)
|
interfaceNames := extractInterfaceNamesFromInterfaceReferences(vm.Properties.NetworkProfile.NetworkInterfaces)
|
||||||
interfaceIPConfigurations := []*armnetwork.InterfaceIPConfiguration{}
|
networkInterfaces := []armnetwork.Interface{}
|
||||||
for _, interfaceName := range interfaceNames {
|
for _, interfaceName := range interfaceNames {
|
||||||
networkInterfacesResp, err := m.networkInterfacesAPI.Get(ctx, resourceGroup, interfaceName, nil)
|
networkInterfacesResp, err := m.networkInterfacesAPI.Get(ctx, resourceGroup, interfaceName, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve network interface %v: %w", interfaceName, err)
|
return nil, fmt.Errorf("failed to retrieve network interface %v: %w", interfaceName, err)
|
||||||
}
|
}
|
||||||
if networkInterfacesResp.Interface.Properties == nil || networkInterfacesResp.Interface.Properties.IPConfigurations == nil {
|
networkInterfaces = append(networkInterfaces, networkInterfacesResp.Interface)
|
||||||
return nil, errors.New("retrieved network interface has invalid ip configuration")
|
|
||||||
}
|
|
||||||
interfaceIPConfigurations = append(interfaceIPConfigurations, networkInterfacesResp.Properties.IPConfigurations...)
|
|
||||||
}
|
}
|
||||||
return interfaceIPConfigurations, nil
|
return networkInterfaces, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getScaleSetVMInterfaces retrieves all network interfaces referenced by a scale set virtual machine.
|
// getScaleSetVMInterfaces retrieves all network interfaces referenced by a scale set virtual machine.
|
||||||
func (m *Metadata) getScaleSetVMInterfaces(ctx context.Context, vm armcompute.VirtualMachineScaleSetVM, resourceGroup, scaleSet, instanceID string) ([]*armnetwork.InterfaceIPConfiguration, error) {
|
func (m *Metadata) getScaleSetVMInterfaces(ctx context.Context, vm armcompute.VirtualMachineScaleSetVM, resourceGroup, scaleSet, instanceID string) ([]armnetwork.Interface, error) {
|
||||||
if vm.Properties == nil || vm.Properties.NetworkProfile == nil {
|
if vm.Properties == nil || vm.Properties.NetworkProfile == nil {
|
||||||
return []*armnetwork.InterfaceIPConfiguration{}, nil
|
return []armnetwork.Interface{}, nil
|
||||||
}
|
}
|
||||||
interfaceNames := extractInterfaceNamesFromInterfaceReferences(vm.Properties.NetworkProfile.NetworkInterfaces)
|
interfaceNames := extractInterfaceNamesFromInterfaceReferences(vm.Properties.NetworkProfile.NetworkInterfaces)
|
||||||
interfaceIPConfigurations := []*armnetwork.InterfaceIPConfiguration{}
|
networkInterfaces := []armnetwork.Interface{}
|
||||||
for _, interfaceName := range interfaceNames {
|
for _, interfaceName := range interfaceNames {
|
||||||
networkInterfacesResp, err := m.networkInterfacesAPI.GetVirtualMachineScaleSetNetworkInterface(ctx, resourceGroup, scaleSet, instanceID, interfaceName, nil)
|
networkInterfacesResp, err := m.networkInterfacesAPI.GetVirtualMachineScaleSetNetworkInterface(ctx, resourceGroup, scaleSet, instanceID, interfaceName, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve network interface %v: %w", interfaceName, err)
|
return nil, fmt.Errorf("failed to retrieve network interface %v: %w", interfaceName, err)
|
||||||
}
|
}
|
||||||
if networkInterfacesResp.Interface.Properties == nil || networkInterfacesResp.Interface.Properties.IPConfigurations == nil {
|
networkInterfaces = append(networkInterfaces, networkInterfacesResp.Interface)
|
||||||
return nil, errors.New("retrieved network interface has invalid ip configuration")
|
|
||||||
}
|
|
||||||
interfaceIPConfigurations = append(interfaceIPConfigurations, networkInterfacesResp.Properties.IPConfigurations...)
|
|
||||||
}
|
}
|
||||||
return interfaceIPConfigurations, nil
|
return networkInterfaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScaleSetVMPublicIPAddresses retrieves all public IP addresses from a network interface which is referenced by a scale set virtual machine.
|
||||||
|
func (m *Metadata) getScaleSetVMPublicIPAddresses(ctx context.Context, resourceGroup, scaleSet, instanceID string,
|
||||||
|
networkInterfaces []armnetwork.Interface,
|
||||||
|
) ([]string, error) {
|
||||||
|
var publicIPAddresses []string
|
||||||
|
for _, networkInterface := range networkInterfaces {
|
||||||
|
if networkInterface.Properties == nil || networkInterface.Name == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, config := range networkInterface.Properties.IPConfigurations {
|
||||||
|
if config == nil || config.Properties == nil || config.Properties.PublicIPAddress == nil || config.Name == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
publicIPAddressName := *config.Properties.PublicIPAddress.ID
|
||||||
|
publicIPAddressNameParts := strings.Split(publicIPAddressName, "/")
|
||||||
|
publicIPAddressName = publicIPAddressNameParts[len(publicIPAddressNameParts)-1]
|
||||||
|
publicIPAddress, err := m.publicIPAddressesAPI.GetVirtualMachineScaleSetPublicIPAddress(ctx, resourceGroup, scaleSet, instanceID, *networkInterface.Name, *config.Name, publicIPAddressName, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve public ip address %v: %w", publicIPAddressName, err)
|
||||||
|
}
|
||||||
|
if publicIPAddress.Properties == nil || publicIPAddress.Properties.IPAddress == nil {
|
||||||
|
return nil, errors.New("retrieved public ip address has invalid ip address")
|
||||||
|
}
|
||||||
|
publicIPAddresses = append(publicIPAddresses, *publicIPAddress.Properties.IPAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicIPAddresses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractPrivateIPs extracts private IPs from a list of network interface IP configurations.
|
// extractPrivateIPs extracts private IPs from a list of network interface IP configurations.
|
||||||
func extractPrivateIPs(interfaceIPConfigs []*armnetwork.InterfaceIPConfiguration) []string {
|
func extractPrivateIPs(networkInterfaces []armnetwork.Interface) []string {
|
||||||
addresses := []string{}
|
addresses := []string{}
|
||||||
for _, config := range interfaceIPConfigs {
|
for _, networkInterface := range networkInterfaces {
|
||||||
if config == nil || config.Properties == nil || config.Properties.PrivateIPAddress == nil {
|
if networkInterface.Properties == nil || len(networkInterface.Properties.IPConfigurations) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addresses = append(addresses, *config.Properties.PrivateIPAddress)
|
for _, config := range networkInterface.Properties.IPConfigurations {
|
||||||
|
if config == nil || config.Properties == nil || config.Properties.PrivateIPAddress == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addresses = append(addresses, *config.Properties.PrivateIPAddress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return addresses
|
return addresses
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package azure
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
@ -12,10 +13,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGetVMInterfaces(t *testing.T) {
|
func TestGetVMInterfaces(t *testing.T) {
|
||||||
wantConfigs := []*armnetwork.InterfaceIPConfiguration{
|
wantNetworkInterfaces := []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Name: to.StringPtr("interface-name"),
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -31,25 +39,53 @@ func TestGetVMInterfaces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
vm armcompute.VirtualMachine
|
vm armcompute.VirtualMachine
|
||||||
networkInterfacesAPI networkInterfacesAPI
|
networkInterfacesAPI networkInterfacesAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantConfigs []*armnetwork.InterfaceIPConfiguration
|
wantNetworkInterfaces []armnetwork.Interface
|
||||||
}{
|
}{
|
||||||
"retrieval works": {
|
"retrieval works": {
|
||||||
vm: vm,
|
vm: vm,
|
||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantConfigs: wantConfigs,
|
getInterface: armnetwork.Interface{
|
||||||
|
Name: to.StringPtr("interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantNetworkInterfaces: wantNetworkInterfaces,
|
||||||
},
|
},
|
||||||
"vm can have 0 interfaces": {
|
"vm can have 0 interfaces": {
|
||||||
vm: armcompute.VirtualMachine{},
|
vm: armcompute.VirtualMachine{},
|
||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantConfigs: []*armnetwork.InterfaceIPConfiguration{},
|
getInterface: armnetwork.Interface{
|
||||||
|
Name: to.StringPtr("interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantNetworkInterfaces: []armnetwork.Interface{},
|
||||||
},
|
},
|
||||||
"interface retrieval fails": {
|
"interface retrieval fails": {
|
||||||
vm: vm,
|
vm: vm,
|
||||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantErr: true,
|
getErr: errors.New("get err"),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,23 +97,30 @@ func TestGetVMInterfaces(t *testing.T) {
|
|||||||
metadata := Metadata{
|
metadata := Metadata{
|
||||||
networkInterfacesAPI: tc.networkInterfacesAPI,
|
networkInterfacesAPI: tc.networkInterfacesAPI,
|
||||||
}
|
}
|
||||||
configs, err := metadata.getVMInterfaces(context.Background(), tc.vm, "resource-group")
|
vmNetworkInteraces, err := metadata.getVMInterfaces(context.Background(), tc.vm, "resource-group")
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(tc.wantConfigs, configs)
|
assert.Equal(tc.wantNetworkInterfaces, vmNetworkInteraces)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetScaleSetVMInterfaces(t *testing.T) {
|
func TestGetScaleSetVMInterfaces(t *testing.T) {
|
||||||
wantConfigs := []*armnetwork.InterfaceIPConfiguration{
|
wantNetworkInterfaces := []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Name: to.StringPtr("interface-name"),
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -93,25 +136,53 @@ func TestGetScaleSetVMInterfaces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
vm armcompute.VirtualMachineScaleSetVM
|
vm armcompute.VirtualMachineScaleSetVM
|
||||||
networkInterfacesAPI networkInterfacesAPI
|
networkInterfacesAPI networkInterfacesAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantConfigs []*armnetwork.InterfaceIPConfiguration
|
wantNetworkInterfaces []armnetwork.Interface
|
||||||
}{
|
}{
|
||||||
"retrieval works": {
|
"retrieval works": {
|
||||||
vm: vm,
|
vm: vm,
|
||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantConfigs: wantConfigs,
|
getInterface: armnetwork.Interface{
|
||||||
|
Name: to.StringPtr("interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantNetworkInterfaces: wantNetworkInterfaces,
|
||||||
},
|
},
|
||||||
"vm can have 0 interfaces": {
|
"vm can have 0 interfaces": {
|
||||||
vm: armcompute.VirtualMachineScaleSetVM{},
|
vm: armcompute.VirtualMachineScaleSetVM{},
|
||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantConfigs: []*armnetwork.InterfaceIPConfiguration{},
|
getInterface: armnetwork.Interface{
|
||||||
|
Name: to.StringPtr("interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantNetworkInterfaces: []armnetwork.Interface{},
|
||||||
},
|
},
|
||||||
"interface retrieval fails": {
|
"interface retrieval fails": {
|
||||||
vm: vm,
|
vm: vm,
|
||||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
networkInterfacesAPI: &stubNetworkInterfacesAPI{
|
||||||
wantErr: true,
|
getErr: errors.New("get err"),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,33 +201,147 @@ func TestGetScaleSetVMInterfaces(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(tc.wantConfigs, configs)
|
assert.Equal(tc.wantNetworkInterfaces, configs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetScaleSetVMPublicIPAddresses(t *testing.T) {
|
||||||
|
someErr := errors.New("some err")
|
||||||
|
newNetworkInterfaces := func() []armnetwork.Interface {
|
||||||
|
return []armnetwork.Interface{{
|
||||||
|
Name: to.StringPtr("interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("ip-config-name"),
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/publicIPAddresses/public-ip-name"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Name: to.StringPtr("interface-name2"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Name: to.StringPtr("ip-config-name2"),
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PublicIPAddress: &armnetwork.PublicIPAddress{
|
||||||
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/publicIPAddresses/public-ip-name2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
networkInterfacesMutator func(*[]armnetwork.Interface)
|
||||||
|
networkInterfaces []armnetwork.Interface
|
||||||
|
publicIPAddressesAPI publicIPAddressesAPI
|
||||||
|
wantIPs []string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"retrieval works": {
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getVirtualMachineScaleSetPublicIPAddressResponse: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse{
|
||||||
|
PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult{
|
||||||
|
PublicIPAddress: armnetwork.PublicIPAddress{
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
|
||||||
|
IPAddress: to.StringPtr("192.0.2.1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
networkInterfaces: newNetworkInterfaces(),
|
||||||
|
wantIPs: []string{"192.0.2.1", "192.0.2.1"},
|
||||||
|
},
|
||||||
|
"retrieval works for no valid interfaces": {
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getVirtualMachineScaleSetPublicIPAddressResponse: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse{
|
||||||
|
PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult{
|
||||||
|
PublicIPAddress: armnetwork.PublicIPAddress{
|
||||||
|
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
|
||||||
|
IPAddress: to.StringPtr("192.0.2.1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
networkInterfaces: newNetworkInterfaces(),
|
||||||
|
networkInterfacesMutator: func(nets *[]armnetwork.Interface) {
|
||||||
|
(*nets)[0].Properties.IPConfigurations = []*armnetwork.InterfaceIPConfiguration{nil}
|
||||||
|
(*nets)[1] = armnetwork.Interface{Name: nil}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fail to get public IP": {
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getErr: someErr},
|
||||||
|
networkInterfaces: newNetworkInterfaces(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"fail to parse IPv4 address of public IP": {
|
||||||
|
publicIPAddressesAPI: &stubPublicIPAddressesAPI{getVirtualMachineScaleSetPublicIPAddressResponse: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse{
|
||||||
|
PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult: armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResult{
|
||||||
|
PublicIPAddress: armnetwork.PublicIPAddress{},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
networkInterfaces: newNetworkInterfaces(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
if tc.networkInterfacesMutator != nil {
|
||||||
|
tc.networkInterfacesMutator(&tc.networkInterfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
publicIPAddressesAPI: tc.publicIPAddressesAPI,
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err := metadata.getScaleSetVMPublicIPAddresses(context.Background(), "resource-group", "scale-set-name", "instance-id", tc.networkInterfaces)
|
||||||
|
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantIPs, ips)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractPrivateIPs(t *testing.T) {
|
func TestExtractPrivateIPs(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
interfaceIPConfigs []*armnetwork.InterfaceIPConfiguration
|
networkInterfaces []armnetwork.Interface
|
||||||
wantIPs []string
|
wantIPs []string
|
||||||
}{
|
}{
|
||||||
"extraction works": {
|
"extraction works": {
|
||||||
interfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{
|
networkInterfaces: []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantIPs: []string{"192.0.2.0"},
|
wantIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
"can be empty": {
|
"can be empty": {
|
||||||
interfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{},
|
networkInterfaces: []armnetwork.Interface{},
|
||||||
},
|
},
|
||||||
"invalid interface is skipped": {
|
"invalid interface is skipped": {
|
||||||
interfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{
|
networkInterfaces: []armnetwork.Interface{{}},
|
||||||
{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +349,7 @@ func TestExtractPrivateIPs(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ips := extractPrivateIPs(tc.interfaceIPConfigs)
|
ips := extractPrivateIPs(tc.networkInterfaces)
|
||||||
|
|
||||||
assert.ElementsMatch(tc.wantIPs, ips)
|
assert.ElementsMatch(tc.wantIPs, ips)
|
||||||
})
|
})
|
||||||
|
@ -7,31 +7,41 @@ import (
|
|||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
azureVMSSProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`)
|
||||||
|
coordinatorScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-coordinators-[0-9a-zA-Z]+$`)
|
||||||
|
nodeScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-nodes-[0-9a-zA-Z]+$`)
|
||||||
|
)
|
||||||
|
|
||||||
// getScaleSetVM tries to get an azure vm belonging to a scale set.
|
// getScaleSetVM tries to get an azure vm belonging to a scale set.
|
||||||
func (m *Metadata) getScaleSetVM(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m *Metadata) getScaleSetVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
_, resourceGroup, scaleSet, instanceID, err := splitScaleSetProviderID(providerID)
|
_, resourceGroup, scaleSet, instanceID, err := splitScaleSetProviderID(providerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
vmResp, err := m.virtualMachineScaleSetVMsAPI.Get(ctx, resourceGroup, scaleSet, instanceID, nil)
|
vmResp, err := m.virtualMachineScaleSetVMsAPI.Get(ctx, resourceGroup, scaleSet, instanceID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
interfaceIPConfigurations, err := m.getScaleSetVMInterfaces(ctx, vmResp.VirtualMachineScaleSetVM, resourceGroup, scaleSet, instanceID)
|
networkInterfaces, err := m.getScaleSetVMInterfaces(ctx, vmResp.VirtualMachineScaleSetVM, resourceGroup, scaleSet, instanceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
|
}
|
||||||
|
publicIPAddresses, err := m.getScaleSetVMPublicIPAddresses(ctx, resourceGroup, scaleSet, instanceID, networkInterfaces)
|
||||||
|
if err != nil {
|
||||||
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertScaleSetVMToCoreInstance(scaleSet, vmResp.VirtualMachineScaleSetVM, interfaceIPConfigurations)
|
return convertScaleSetVMToCoreInstance(scaleSet, vmResp.VirtualMachineScaleSetVM, networkInterfaces, publicIPAddresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
// listScaleSetVMs lists all scale set VMs in the current resource group.
|
// listScaleSetVMs lists all scale set VMs in the current resource group.
|
||||||
func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([]core.Instance, error) {
|
func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([]cloudtypes.Instance, error) {
|
||||||
instances := []core.Instance{}
|
instances := []cloudtypes.Instance{}
|
||||||
scaleSetPager := m.scaleSetsAPI.List(resourceGroup, nil)
|
scaleSetPager := m.scaleSetsAPI.List(resourceGroup, nil)
|
||||||
for scaleSetPager.NextPage(ctx) {
|
for scaleSetPager.NextPage(ctx) {
|
||||||
for _, scaleSet := range scaleSetPager.PageResponse().Value {
|
for _, scaleSet := range scaleSetPager.PageResponse().Value {
|
||||||
@ -48,7 +58,7 @@ func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
instance, err := convertScaleSetVMToCoreInstance(*scaleSet.Name, *vm, interfaces)
|
instance, err := convertScaleSetVMToCoreInstance(*scaleSet.Name, *vm, interfaces, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -64,9 +74,7 @@ func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([
|
|||||||
// A providerID for scale set VMs is build after the following schema:
|
// A providerID for scale set VMs is build after the following schema:
|
||||||
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachineScaleSets/<scale-set-name>/virtualMachines/<instance-id>'
|
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachineScaleSets/<scale-set-name>/virtualMachines/<instance-id>'
|
||||||
func splitScaleSetProviderID(providerID string) (subscriptionID, resourceGroup, scaleSet, instanceID string, err error) {
|
func splitScaleSetProviderID(providerID string) (subscriptionID, resourceGroup, scaleSet, instanceID string, err error) {
|
||||||
// providerIDregex is a regex matching an azure scaleset vm providerID with each part of the URI being a submatch.
|
matches := azureVMSSProviderIDRegexp.FindStringSubmatch(providerID)
|
||||||
providerIDregex := regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`)
|
|
||||||
matches := providerIDregex.FindStringSubmatch(providerID)
|
|
||||||
if len(matches) != 5 {
|
if len(matches) != 5 {
|
||||||
return "", "", "", "", errors.New("error splitting providerID")
|
return "", "", "", "", errors.New("error splitting providerID")
|
||||||
}
|
}
|
||||||
@ -74,12 +82,12 @@ func splitScaleSetProviderID(providerID string) (subscriptionID, resourceGroup,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convertScaleSetVMToCoreInstance converts an azure scale set virtual machine with interface configurations into a core.Instance.
|
// convertScaleSetVMToCoreInstance converts an azure scale set virtual machine with interface configurations into a core.Instance.
|
||||||
func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachineScaleSetVM, interfaceIPConfigs []*armnetwork.InterfaceIPConfiguration) (core.Instance, error) {
|
func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachineScaleSetVM, networkInterfaces []armnetwork.Interface, publicIPAddresses []string) (cloudtypes.Instance, error) {
|
||||||
if vm.ID == nil {
|
if vm.ID == nil {
|
||||||
return core.Instance{}, errors.New("retrieving instance from armcompute API client returned no instance ID")
|
return cloudtypes.Instance{}, errors.New("retrieving instance from armcompute API client returned no instance ID")
|
||||||
}
|
}
|
||||||
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.ComputerName == nil {
|
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.ComputerName == nil {
|
||||||
return core.Instance{}, errors.New("retrieving instance from armcompute API client returned no computer name")
|
return cloudtypes.Instance{}, errors.New("retrieving instance from armcompute API client returned no computer name")
|
||||||
}
|
}
|
||||||
var sshKeys map[string][]string
|
var sshKeys map[string][]string
|
||||||
if vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
if vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
||||||
@ -87,19 +95,18 @@ func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachi
|
|||||||
} else {
|
} else {
|
||||||
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
||||||
}
|
}
|
||||||
return core.Instance{
|
return cloudtypes.Instance{
|
||||||
Name: *vm.Properties.OSProfile.ComputerName,
|
Name: *vm.Properties.OSProfile.ComputerName,
|
||||||
ProviderID: "azure://" + *vm.ID,
|
ProviderID: "azure://" + *vm.ID,
|
||||||
Role: extractScaleSetVMRole(scaleSet),
|
Role: extractScaleSetVMRole(scaleSet),
|
||||||
IPs: extractPrivateIPs(interfaceIPConfigs),
|
PrivateIPs: extractPrivateIPs(networkInterfaces),
|
||||||
|
PublicIPs: publicIPAddresses,
|
||||||
SSHKeys: sshKeys,
|
SSHKeys: sshKeys,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractScaleSetVMRole extracts the constellation role of a scale set using its name.
|
// extractScaleSetVMRole extracts the constellation role of a scale set using its name.
|
||||||
func extractScaleSetVMRole(scaleSet string) role.Role {
|
func extractScaleSetVMRole(scaleSet string) role.Role {
|
||||||
coordinatorScaleSetRegexp := regexp.MustCompile(`constellation-scale-set-coordinators-[0-9a-zA-Z]+$`)
|
|
||||||
nodeScaleSetRegexp := regexp.MustCompile(`constellation-scale-set-nodes-[0-9a-zA-Z]+$`)
|
|
||||||
if coordinatorScaleSetRegexp.MatchString(scaleSet) {
|
if coordinatorScaleSetRegexp.MatchString(scaleSet) {
|
||||||
return role.Coordinator
|
return role.Coordinator
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,17 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetScaleSetVM(t *testing.T) {
|
func TestGetScaleSetVM(t *testing.T) {
|
||||||
wantInstance := core.Instance{
|
wantInstance := cloudtypes.Instance{
|
||||||
Name: "scale-set-name-instance-id",
|
Name: "scale-set-name-instance-id",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
}
|
}
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
@ -26,7 +26,7 @@ func TestGetScaleSetVM(t *testing.T) {
|
|||||||
networkInterfacesAPI networkInterfacesAPI
|
networkInterfacesAPI networkInterfacesAPI
|
||||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"getVM for scale set instance works": {
|
"getVM for scale set instance works": {
|
||||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
@ -79,11 +79,11 @@ func TestGetScaleSetVM(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestListScaleSetVMs(t *testing.T) {
|
func TestListScaleSetVMs(t *testing.T) {
|
||||||
wantInstances := []core.Instance{
|
wantInstances := []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "scale-set-name-instance-id",
|
Name: "scale-set-name-instance-id",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ func TestListScaleSetVMs(t *testing.T) {
|
|||||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||||
scaleSetsAPI scaleSetsAPI
|
scaleSetsAPI scaleSetsAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstances []core.Instance
|
wantInstances []cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"listVMs works": {
|
"listVMs works": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -114,7 +114,7 @@ func TestListScaleSetVMs(t *testing.T) {
|
|||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||||
virtualMachineScaleSetVMsAPI: &stubVirtualMachineScaleSetVMsAPI{},
|
virtualMachineScaleSetVMsAPI: &stubVirtualMachineScaleSetVMsAPI{},
|
||||||
scaleSetsAPI: newScaleSetsStub(),
|
scaleSetsAPI: newScaleSetsStub(),
|
||||||
wantInstances: []core.Instance{},
|
wantInstances: []cloudtypes.Instance{},
|
||||||
},
|
},
|
||||||
"can skip nil in VM list": {
|
"can skip nil in VM list": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -210,10 +210,11 @@ func TestSplitScaleSetProviderID(t *testing.T) {
|
|||||||
|
|
||||||
func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
inVM armcompute.VirtualMachineScaleSetVM
|
inVM armcompute.VirtualMachineScaleSetVM
|
||||||
inInterfaceIPConfigs []*armnetwork.InterfaceIPConfiguration
|
inInterface []armnetwork.Interface
|
||||||
wantErr bool
|
inPublicIPs []string
|
||||||
wantInstance core.Instance
|
wantErr bool
|
||||||
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"conversion works": {
|
"conversion works": {
|
||||||
inVM: armcompute.VirtualMachineScaleSetVM{
|
inVM: armcompute.VirtualMachineScaleSetVM{
|
||||||
@ -226,17 +227,27 @@ func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
inInterfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{
|
inInterface: []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Name: to.StringPtr("scale-set-name_instance-id"),
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
inPublicIPs: []string{"192.0.2.100", "192.0.2.101"},
|
||||||
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "scale-set-name-instance-id",
|
Name: "scale-set-name-instance-id",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
PublicIPs: []string{"192.0.2.100", "192.0.2.101"},
|
||||||
SSHKeys: map[string][]string{},
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -251,7 +262,7 @@ func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
instance, err := convertScaleSetVMToCoreInstance("scale-set", tc.inVM, tc.inInterfaceIPConfigs)
|
instance, err := convertScaleSetVMToCoreInstance("scale-set", tc.inVM, tc.inInterface, tc.inPublicIPs)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
19
coordinator/cloudprovider/azure/securityGroup.go
Normal file
19
coordinator/cloudprovider/azure/securityGroup.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package azure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getNetworkSecurityGroup retrieves the list of security groups for the given resource group.
|
||||||
|
func (m *Metadata) getNetworkSecurityGroup(ctx context.Context, resourceGroup string) (*armnetwork.SecurityGroup, error) {
|
||||||
|
pager := m.securityGroupsAPI.List(resourceGroup, nil)
|
||||||
|
for pager.NextPage(ctx) {
|
||||||
|
for _, securityGroup := range pager.PageResponse().Value {
|
||||||
|
return securityGroup, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no security group found for resource group %q", resourceGroup)
|
||||||
|
}
|
@ -11,29 +11,31 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var azureVMProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
|
||||||
|
|
||||||
// getVM tries to get a single azure vm.
|
// getVM tries to get a single azure vm.
|
||||||
func (m *Metadata) getVM(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m *Metadata) getVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
_, resourceGroup, instanceName, err := splitVMProviderID(providerID)
|
_, resourceGroup, instanceName, err := splitVMProviderID(providerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
vmResp, err := m.virtualMachinesAPI.Get(ctx, resourceGroup, instanceName, nil)
|
vmResp, err := m.virtualMachinesAPI.Get(ctx, resourceGroup, instanceName, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
interfaceIPConfigurations, err := m.getVMInterfaces(ctx, vmResp.VirtualMachine, resourceGroup)
|
interfaceIPConfigurations, err := m.getVMInterfaces(ctx, vmResp.VirtualMachine, resourceGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
return convertVMToCoreInstance(vmResp.VirtualMachine, interfaceIPConfigurations)
|
return convertVMToCoreInstance(vmResp.VirtualMachine, interfaceIPConfigurations)
|
||||||
}
|
}
|
||||||
|
|
||||||
// listVMs lists all individual VMs in the current resource group.
|
// listVMs lists all individual VMs in the current resource group.
|
||||||
func (m *Metadata) listVMs(ctx context.Context, resourceGroup string) ([]core.Instance, error) {
|
func (m *Metadata) listVMs(ctx context.Context, resourceGroup string) ([]cloudtypes.Instance, error) {
|
||||||
instances := []core.Instance{}
|
instances := []cloudtypes.Instance{}
|
||||||
pager := m.virtualMachinesAPI.List(resourceGroup, nil)
|
pager := m.virtualMachinesAPI.List(resourceGroup, nil)
|
||||||
for pager.NextPage(ctx) {
|
for pager.NextPage(ctx) {
|
||||||
for _, vm := range pager.PageResponse().Value {
|
for _, vm := range pager.PageResponse().Value {
|
||||||
@ -75,19 +77,17 @@ func (m *Metadata) setTag(ctx context.Context, key, value string) error {
|
|||||||
// A providerID for individual VMs is build after the following schema:
|
// A providerID for individual VMs is build after the following schema:
|
||||||
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<instance-name>'
|
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<instance-name>'
|
||||||
func splitVMProviderID(providerID string) (subscriptionID, resourceGroup, instanceName string, err error) {
|
func splitVMProviderID(providerID string) (subscriptionID, resourceGroup, instanceName string, err error) {
|
||||||
// providerIDregex is a regex matching an azure vm providerID with each part of the URI being a submatch.
|
matches := azureVMProviderIDRegexp.FindStringSubmatch(providerID)
|
||||||
providerIDregex := regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
|
|
||||||
matches := providerIDregex.FindStringSubmatch(providerID)
|
|
||||||
if len(matches) != 4 {
|
if len(matches) != 4 {
|
||||||
return "", "", "", errors.New("error splitting providerID")
|
return "", "", "", errors.New("error splitting providerID")
|
||||||
}
|
}
|
||||||
return matches[1], matches[2], matches[3], nil
|
return matches[1], matches[2], matches[3], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertVMToCoreInstance converts an azure virtual machine with interface configurations into a core.Instance.
|
// convertVMToCoreInstance converts an azure virtual machine with interface configurations into a cloudtypes.Instance.
|
||||||
func convertVMToCoreInstance(vm armcompute.VirtualMachine, interfaceIPConfigs []*armnetwork.InterfaceIPConfiguration) (core.Instance, error) {
|
func convertVMToCoreInstance(vm armcompute.VirtualMachine, networkInterfaces []armnetwork.Interface) (cloudtypes.Instance, error) {
|
||||||
if vm.Name == nil || vm.ID == nil {
|
if vm.Name == nil || vm.ID == nil {
|
||||||
return core.Instance{}, fmt.Errorf("retrieving instance from armcompute API client returned invalid instance Name (%v) or ID (%v)", vm.Name, vm.ID)
|
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance from armcompute API client returned invalid instance Name (%v) or ID (%v)", vm.Name, vm.ID)
|
||||||
}
|
}
|
||||||
var sshKeys map[string][]string
|
var sshKeys map[string][]string
|
||||||
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
||||||
@ -96,11 +96,11 @@ func convertVMToCoreInstance(vm armcompute.VirtualMachine, interfaceIPConfigs []
|
|||||||
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
||||||
}
|
}
|
||||||
metadata := extractInstanceTags(vm.Tags)
|
metadata := extractInstanceTags(vm.Tags)
|
||||||
return core.Instance{
|
return cloudtypes.Instance{
|
||||||
Name: *vm.Name,
|
Name: *vm.Name,
|
||||||
ProviderID: "azure://" + *vm.ID,
|
ProviderID: "azure://" + *vm.ID,
|
||||||
Role: cloudprovider.ExtractRole(metadata),
|
Role: cloudprovider.ExtractRole(metadata),
|
||||||
IPs: extractPrivateIPs(interfaceIPConfigs),
|
PrivateIPs: extractPrivateIPs(networkInterfaces),
|
||||||
SSHKeys: sshKeys,
|
SSHKeys: sshKeys,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,16 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetVM(t *testing.T) {
|
func TestGetVM(t *testing.T) {
|
||||||
wantInstance := core.Instance{
|
wantInstance := cloudtypes.Instance{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
}
|
}
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
@ -25,7 +25,7 @@ func TestGetVM(t *testing.T) {
|
|||||||
networkInterfacesAPI networkInterfacesAPI
|
networkInterfacesAPI networkInterfacesAPI
|
||||||
virtualMachinesAPI virtualMachinesAPI
|
virtualMachinesAPI virtualMachinesAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"getVM for individual instance works": {
|
"getVM for individual instance works": {
|
||||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
@ -78,11 +78,11 @@ func TestGetVM(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestListVMs(t *testing.T) {
|
func TestListVMs(t *testing.T) {
|
||||||
wantInstances := []core.Instance{
|
wantInstances := []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ func TestListVMs(t *testing.T) {
|
|||||||
networkInterfacesAPI networkInterfacesAPI
|
networkInterfacesAPI networkInterfacesAPI
|
||||||
virtualMachinesAPI virtualMachinesAPI
|
virtualMachinesAPI virtualMachinesAPI
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstances []core.Instance
|
wantInstances []cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"listVMs works": {
|
"listVMs works": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -103,7 +103,7 @@ func TestListVMs(t *testing.T) {
|
|||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||||
virtualMachinesAPI: &stubVirtualMachinesAPI{},
|
virtualMachinesAPI: &stubVirtualMachinesAPI{},
|
||||||
wantInstances: []core.Instance{},
|
wantInstances: []cloudtypes.Instance{},
|
||||||
},
|
},
|
||||||
"can skip nil in VM list": {
|
"can skip nil in VM list": {
|
||||||
imdsAPI: newIMDSStub(),
|
imdsAPI: newIMDSStub(),
|
||||||
@ -234,10 +234,10 @@ func TestSplitVMProviderID(t *testing.T) {
|
|||||||
|
|
||||||
func TestConvertVMToCoreInstance(t *testing.T) {
|
func TestConvertVMToCoreInstance(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
inVM armcompute.VirtualMachine
|
inVM armcompute.VirtualMachine
|
||||||
inInterfaceIPConfigs []*armnetwork.InterfaceIPConfiguration
|
inInterface []armnetwork.Interface
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"conversion works": {
|
"conversion works": {
|
||||||
inVM: armcompute.VirtualMachine{
|
inVM: armcompute.VirtualMachine{
|
||||||
@ -259,17 +259,25 @@ func TestConvertVMToCoreInstance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
inInterfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{
|
inInterface: []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Name: to.StringPtr("interface-name"),
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -279,17 +287,25 @@ func TestConvertVMToCoreInstance(t *testing.T) {
|
|||||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||||
Tags: map[string]*string{"tag-key": to.StringPtr("tag-value")},
|
Tags: map[string]*string{"tag-key": to.StringPtr("tag-value")},
|
||||||
},
|
},
|
||||||
inInterfaceIPConfigs: []*armnetwork.InterfaceIPConfiguration{
|
inInterface: []armnetwork.Interface{
|
||||||
{
|
{
|
||||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
Name: to.StringPtr("interface-name"),
|
||||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||||
|
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||||
|
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||||
|
{
|
||||||
|
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||||
|
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "instance-name",
|
Name: "instance-name",
|
||||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
SSHKeys: map[string][]string{},
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -304,7 +320,7 @@ func TestConvertVMToCoreInstance(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
instance, err := convertVMToCoreInstance(tc.inVM, tc.inInterfaceIPConfigs)
|
instance, err := convertVMToCoreInstance(tc.inVM, tc.inInterface)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
21
coordinator/cloudprovider/azure/virtualnetwork.go
Normal file
21
coordinator/cloudprovider/azure/virtualnetwork.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package azure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getVirtualNetwork return the first virtual network found in the resource group.
|
||||||
|
func (m *Metadata) getVirtualNetwork(ctx context.Context, resourceGroup string) (*armnetwork.VirtualNetwork, error) {
|
||||||
|
pager := m.virtualNetworksAPI.List(resourceGroup, nil)
|
||||||
|
for pager.NextPage(ctx) {
|
||||||
|
for _, network := range pager.PageResponse().Value {
|
||||||
|
if network != nil {
|
||||||
|
return network, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no virtual network found in resource group %s", resourceGroup)
|
||||||
|
}
|
@ -8,6 +8,22 @@ import (
|
|||||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type virtualNetworksClient struct {
|
||||||
|
*armnetwork.VirtualNetworksClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *virtualNetworksClient) List(resourceGroupName string, options *armnetwork.VirtualNetworksClientListOptions) virtualNetworksClientListPager {
|
||||||
|
return c.VirtualNetworksClient.List(resourceGroupName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityGroupsClient struct {
|
||||||
|
*armnetwork.SecurityGroupsClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *securityGroupsClient) List(resourceGroupName string, options *armnetwork.SecurityGroupsClientListOptions) securityGroupsClientListPager {
|
||||||
|
return c.SecurityGroupsClient.List(resourceGroupName, options)
|
||||||
|
}
|
||||||
|
|
||||||
type networkInterfacesClient struct {
|
type networkInterfacesClient struct {
|
||||||
*armnetwork.InterfacesClient
|
*armnetwork.InterfacesClient
|
||||||
}
|
}
|
||||||
@ -25,6 +41,32 @@ func (c *networkInterfacesClient) Get(ctx context.Context, resourceGroupName str
|
|||||||
return c.InterfacesClient.Get(ctx, resourceGroupName, networkInterfaceName, options)
|
return c.InterfacesClient.Get(ctx, resourceGroupName, networkInterfaceName, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type publicIPAddressesClient struct {
|
||||||
|
*armnetwork.PublicIPAddressesClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *publicIPAddressesClient) GetVirtualMachineScaleSetPublicIPAddress(ctx context.Context, resourceGroupName string,
|
||||||
|
virtualMachineScaleSetName string, virtualmachineIndex string, networkInterfaceName string,
|
||||||
|
ipConfigurationName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressOptions,
|
||||||
|
) (armnetwork.PublicIPAddressesClientGetVirtualMachineScaleSetPublicIPAddressResponse, error) {
|
||||||
|
return c.PublicIPAddressesClient.GetVirtualMachineScaleSetPublicIPAddress(ctx, resourceGroupName, virtualMachineScaleSetName, virtualmachineIndex, networkInterfaceName, ipConfigurationName, publicIPAddressName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *publicIPAddressesClient) Get(ctx context.Context, resourceGroupName string, publicIPAddressName string,
|
||||||
|
options *armnetwork.PublicIPAddressesClientGetOptions,
|
||||||
|
) (armnetwork.PublicIPAddressesClientGetResponse, error) {
|
||||||
|
return c.PublicIPAddressesClient.Get(ctx, resourceGroupName, publicIPAddressName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadBalancersClient struct {
|
||||||
|
*armnetwork.LoadBalancersClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *loadBalancersClient) List(resourceGroupName string, options *armnetwork.LoadBalancersClientListOptions) loadBalancersClientListPager {
|
||||||
|
return c.LoadBalancersClient.List(resourceGroupName, options)
|
||||||
|
}
|
||||||
|
|
||||||
type virtualMachinesClient struct {
|
type virtualMachinesClient struct {
|
||||||
*armcompute.VirtualMachinesClient
|
*armcompute.VirtualMachinesClient
|
||||||
}
|
}
|
||||||
|
15
coordinator/cloudprovider/cloudtypes/instance.go
Normal file
15
coordinator/cloudprovider/cloudtypes/instance.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package cloudtypes
|
||||||
|
|
||||||
|
import "github.com/edgelesssys/constellation/coordinator/role"
|
||||||
|
|
||||||
|
// Instance describes metadata of a peer.
|
||||||
|
type Instance struct {
|
||||||
|
Name string
|
||||||
|
ProviderID string
|
||||||
|
Role role.Role
|
||||||
|
PrivateIPs []string
|
||||||
|
PublicIPs []string
|
||||||
|
AliasIPRanges []string
|
||||||
|
// SSHKeys maps usernames to ssh public keys.
|
||||||
|
SSHKeys map[string][]string
|
||||||
|
}
|
@ -16,6 +16,12 @@ type instanceAPI interface {
|
|||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type subnetworkAPI interface {
|
||||||
|
List(ctx context.Context, req *computepb.ListSubnetworksRequest, opts ...gax.CallOption) SubnetworkIterator
|
||||||
|
Get(ctx context.Context, req *computepb.GetSubnetworkRequest, opts ...gax.CallOption) (*computepb.Subnetwork, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
type metadataAPI interface {
|
type metadataAPI interface {
|
||||||
InstanceAttributeValue(attr string) (string, error)
|
InstanceAttributeValue(attr string) (string, error)
|
||||||
ProjectID() (string, error)
|
ProjectID() (string, error)
|
||||||
@ -30,3 +36,7 @@ type Operation interface {
|
|||||||
type InstanceIterator interface {
|
type InstanceIterator interface {
|
||||||
Next() (*computepb.Instance, error)
|
Next() (*computepb.Instance, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubnetworkIterator interface {
|
||||||
|
Next() (*computepb.Subnetwork, error)
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package gcp
|
package gcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
@ -15,7 +15,7 @@ func (a *Autoscaler) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
func (a *Autoscaler) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (a *Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
return resources.Secrets{}, nil
|
return resources.Secrets{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package gcp
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ func TestTrivialAutoscalerFunctions(t *testing.T) {
|
|||||||
autoscaler := Autoscaler{}
|
autoscaler := Autoscaler{}
|
||||||
|
|
||||||
assert.NotEmpty(autoscaler.Name())
|
assert.NotEmpty(autoscaler.Name())
|
||||||
assert.Empty(autoscaler.Secrets(core.Instance{}, ""))
|
assert.Empty(autoscaler.Secrets(cloudtypes.Instance{}, ""))
|
||||||
assert.NotEmpty(autoscaler.Volumes())
|
assert.NotEmpty(autoscaler.Volumes())
|
||||||
assert.NotEmpty(autoscaler.VolumeMounts())
|
assert.NotEmpty(autoscaler.VolumeMounts())
|
||||||
assert.NotEmpty(autoscaler.Env())
|
assert.NotEmpty(autoscaler.Env())
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package gcp
|
package gcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -34,14 +35,17 @@ func (c *CloudControllerManager) Name() string {
|
|||||||
func (c *CloudControllerManager) ExtraArgs() []string {
|
func (c *CloudControllerManager) ExtraArgs() []string {
|
||||||
return []string{
|
return []string{
|
||||||
"--use-service-account-credentials",
|
"--use-service-account-credentials",
|
||||||
"--controllers=cloud-node,cloud-node-lifecycle",
|
"--controllers=cloud-node,cloud-node-lifecycle,nodeipam,service,route",
|
||||||
"--cloud-config=/etc/gce/gce.conf",
|
"--cloud-config=/etc/gce/gce.conf",
|
||||||
|
"--cidr-allocator-type=CloudAllocator",
|
||||||
|
"--allocate-node-cidrs=true",
|
||||||
|
"--configure-cloud-routes=false",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||||
func (c *CloudControllerManager) ConfigMaps(instance core.Instance) (resources.ConfigMaps, error) {
|
func (c *CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||||
// GCP CCM expects cloud config to contain the GCP project-id and other configuration.
|
// GCP CCM expects cloud config to contain the GCP project-id and other configuration.
|
||||||
// reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892
|
// reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892
|
||||||
var config strings.Builder
|
var config strings.Builder
|
||||||
@ -51,7 +55,10 @@ func (c *CloudControllerManager) ConfigMaps(instance core.Instance) (resources.C
|
|||||||
return resources.ConfigMaps{}, err
|
return resources.ConfigMaps{}, err
|
||||||
}
|
}
|
||||||
config.WriteString(fmt.Sprintf("project-id = %s\n", projectID))
|
config.WriteString(fmt.Sprintf("project-id = %s\n", projectID))
|
||||||
config.WriteString("use-metadata-server = false\n")
|
config.WriteString("use-metadata-server = true\n")
|
||||||
|
|
||||||
|
nameParts := strings.Split(instance.Name, "-")
|
||||||
|
config.WriteString("node-tags = constellation-" + nameParts[len(nameParts)-2] + "\n")
|
||||||
|
|
||||||
return resources.ConfigMaps{
|
return resources.ConfigMaps{
|
||||||
&k8s.ConfigMap{
|
&k8s.ConfigMap{
|
||||||
@ -72,7 +79,7 @@ func (c *CloudControllerManager) ConfigMaps(instance core.Instance) (resources.C
|
|||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||||
func (c *CloudControllerManager) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
serviceAccountKey, err := getServiceAccountKey(cloudServiceAccountURI)
|
serviceAccountKey, err := getServiceAccountKey(cloudServiceAccountURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resources.Secrets{}, err
|
return resources.Secrets{}, err
|
||||||
@ -150,12 +157,6 @@ func (c *CloudControllerManager) Env() []k8s.EnvVar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
|
||||||
// Allows for cloud-provider specific hooks.
|
|
||||||
func (c *CloudControllerManager) PrepareInstance(instance core.Instance, vpnIP string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
||||||
func (c *CloudControllerManager) Supported() bool {
|
func (c *CloudControllerManager) Supported() bool {
|
||||||
return true
|
return true
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package gcp
|
package gcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/cli/gcp/client"
|
"github.com/edgelesssys/constellation/cli/gcp/client"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -15,12 +16,12 @@ import (
|
|||||||
|
|
||||||
func TestConfigMaps(t *testing.T) {
|
func TestConfigMaps(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
instance core.Instance
|
instance cloudtypes.Instance
|
||||||
wantConfigMaps resources.ConfigMaps
|
wantConfigMaps resources.ConfigMaps
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"ConfigMaps works": {
|
"ConfigMaps works": {
|
||||||
instance: core.Instance{ProviderID: "gce://project-id/zone/instance-name"},
|
instance: cloudtypes.Instance{ProviderID: "gce://project-id/zone/instanceName-UID-0", Name: "instanceName-UID-0"},
|
||||||
wantConfigMaps: resources.ConfigMaps{
|
wantConfigMaps: resources.ConfigMaps{
|
||||||
&k8s.ConfigMap{
|
&k8s.ConfigMap{
|
||||||
TypeMeta: v1.TypeMeta{
|
TypeMeta: v1.TypeMeta{
|
||||||
@ -34,14 +35,15 @@ func TestConfigMaps(t *testing.T) {
|
|||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"gce.conf": `[global]
|
"gce.conf": `[global]
|
||||||
project-id = project-id
|
project-id = project-id
|
||||||
use-metadata-server = false
|
use-metadata-server = true
|
||||||
|
node-tags = constellation-UID
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"invalid providerID fails": {
|
"invalid providerID fails": {
|
||||||
instance: core.Instance{ProviderID: "invalid"},
|
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -80,7 +82,7 @@ func TestSecrets(t *testing.T) {
|
|||||||
rawKey, err := json.Marshal(serviceAccountKey)
|
rawKey, err := json.Marshal(serviceAccountKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
instance core.Instance
|
instance cloudtypes.Instance
|
||||||
cloudServiceAccountURI string
|
cloudServiceAccountURI string
|
||||||
wantSecrets resources.Secrets
|
wantSecrets resources.Secrets
|
||||||
wantErr bool
|
wantErr bool
|
||||||
@ -115,7 +117,7 @@ func TestSecrets(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
cloud := CloudControllerManager{}
|
cloud := CloudControllerManager{}
|
||||||
secrets, err := cloud.Secrets(tc.instance, tc.cloudServiceAccountURI)
|
secrets, err := cloud.Secrets(context.Background(), tc.instance, tc.cloudServiceAccountURI)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
@ -137,6 +139,5 @@ func TestTrivialCCMFunctions(t *testing.T) {
|
|||||||
assert.NotEmpty(cloud.Volumes())
|
assert.NotEmpty(cloud.Volumes())
|
||||||
assert.NotEmpty(cloud.VolumeMounts())
|
assert.NotEmpty(cloud.VolumeMounts())
|
||||||
assert.NotEmpty(cloud.Env())
|
assert.NotEmpty(cloud.Env())
|
||||||
assert.NoError(cloud.PrepareInstance(core.Instance{}, "192.0.2.0"))
|
|
||||||
assert.True(cloud.Supported())
|
assert.True(cloud.Supported())
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
compute "cloud.google.com/go/compute/apiv1"
|
compute "cloud.google.com/go/compute/apiv1"
|
||||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"google.golang.org/api/iterator"
|
"google.golang.org/api/iterator"
|
||||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||||
@ -16,9 +17,12 @@ import (
|
|||||||
|
|
||||||
const gcpSSHMetadataKey = "ssh-keys"
|
const gcpSSHMetadataKey = "ssh-keys"
|
||||||
|
|
||||||
|
var providerIDRegex = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
|
||||||
|
|
||||||
// Client implements the gcp.API interface.
|
// Client implements the gcp.API interface.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
instanceAPI
|
instanceAPI
|
||||||
|
subnetworkAPI
|
||||||
metadataAPI
|
metadataAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +32,15 @@ func NewClient(ctx context.Context) (*Client, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Client{instanceAPI: &instanceClient{insAPI}, metadataAPI: &metadataClient{}}, nil
|
subnetAPI, err := compute.NewSubnetworksRESTClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Client{instanceAPI: &instanceClient{insAPI}, subnetworkAPI: &subnetworkClient{subnetAPI}, metadataAPI: &metadataClient{}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveInstances returns list of instances including their ips and metadata.
|
// RetrieveInstances returns list of instances including their ips and metadata.
|
||||||
func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([]core.Instance, error) {
|
func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([]cloudtypes.Instance, error) {
|
||||||
uid, err := c.uid()
|
uid, err := c.uid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -43,7 +51,7 @@ func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([
|
|||||||
}
|
}
|
||||||
instanceIterator := c.instanceAPI.List(ctx, req)
|
instanceIterator := c.instanceAPI.List(ctx, req)
|
||||||
|
|
||||||
instances := []core.Instance{}
|
instances := []cloudtypes.Instance{}
|
||||||
for {
|
for {
|
||||||
resp, err := instanceIterator.Next()
|
resp, err := instanceIterator.Next()
|
||||||
if err == iterator.Done {
|
if err == iterator.Done {
|
||||||
@ -68,10 +76,10 @@ func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveInstance returns a an instance including ips and metadata.
|
// RetrieveInstance returns a an instance including ips and metadata.
|
||||||
func (c *Client) RetrieveInstance(ctx context.Context, project, zone, instanceName string) (core.Instance, error) {
|
func (c *Client) RetrieveInstance(ctx context.Context, project, zone, instanceName string) (cloudtypes.Instance, error) {
|
||||||
instance, err := c.getComputeInstance(ctx, project, zone, instanceName)
|
instance, err := c.getComputeInstance(ctx, project, zone, instanceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertToCoreInstance(instance, project, zone)
|
return convertToCoreInstance(instance, project, zone)
|
||||||
@ -156,8 +164,45 @@ func (c *Client) UnsetInstanceMetadata(ctx context.Context, project, zone, insta
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RetrieveSubnetworkAliasCIDR returns the alias CIDR of the subnetwork specified by project, zone and subnetworkName.
|
||||||
|
func (c *Client) RetrieveSubnetworkAliasCIDR(ctx context.Context, project, zone, instanceName string) (string, error) {
|
||||||
|
instance, err := c.getComputeInstance(ctx, project, zone, instanceName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if instance == nil || instance.NetworkInterfaces == nil || len(instance.NetworkInterfaces) == 0 || instance.NetworkInterfaces[0].Subnetwork == nil {
|
||||||
|
return "", fmt.Errorf("retrieving instance network interfaces failed")
|
||||||
|
}
|
||||||
|
subnetworkURL := *instance.NetworkInterfaces[0].Subnetwork
|
||||||
|
subnetworkURLFragments := strings.Split(subnetworkURL, "/")
|
||||||
|
subnetworkName := subnetworkURLFragments[len(subnetworkURLFragments)-1]
|
||||||
|
|
||||||
|
// convert:
|
||||||
|
// zone --> region
|
||||||
|
// europe-west3-b --> europe-west3
|
||||||
|
regionParts := strings.Split(zone, "-")
|
||||||
|
region := strings.TrimSuffix(zone, "-"+regionParts[len(regionParts)-1])
|
||||||
|
|
||||||
|
req := &computepb.GetSubnetworkRequest{
|
||||||
|
Project: project,
|
||||||
|
Region: region,
|
||||||
|
Subnetwork: subnetworkName,
|
||||||
|
}
|
||||||
|
subnetwork, err := c.subnetworkAPI.Get(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("retrieving subnetwork alias CIDR failed: %w", err)
|
||||||
|
}
|
||||||
|
if subnetwork == nil || subnetwork.IpCidrRange == nil || *subnetwork.IpCidrRange == "" {
|
||||||
|
return "", fmt.Errorf("retrieving subnetwork alias CIDR returned invalid results")
|
||||||
|
}
|
||||||
|
return *subnetwork.IpCidrRange, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the instanceAPI client.
|
// Close closes the instanceAPI client.
|
||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
|
if err := c.subnetworkAPI.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return c.instanceAPI.Close()
|
return c.instanceAPI.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +244,8 @@ func (c *Client) uid() (string, error) {
|
|||||||
return uid, nil
|
return uid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractIPs extracts private interface IPs from a list of interfaces.
|
// extractPrivateIPs extracts private interface IPs from a list of interfaces.
|
||||||
func extractIPs(interfaces []*computepb.NetworkInterface) []string {
|
func extractPrivateIPs(interfaces []*computepb.NetworkInterface) []string {
|
||||||
ips := []string{}
|
ips := []string{}
|
||||||
for _, interf := range interfaces {
|
for _, interf := range interfaces {
|
||||||
if interf == nil || interf.NetworkIP == nil {
|
if interf == nil || interf.NetworkIP == nil {
|
||||||
@ -211,6 +256,40 @@ func extractIPs(interfaces []*computepb.NetworkInterface) []string {
|
|||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractPublicIPs extracts public interface IPs from a list of interfaces.
|
||||||
|
func extractPublicIPs(interfaces []*computepb.NetworkInterface) []string {
|
||||||
|
ips := []string{}
|
||||||
|
for _, interf := range interfaces {
|
||||||
|
if interf == nil || interf.AccessConfigs == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, accessConfig := range interf.AccessConfigs {
|
||||||
|
if accessConfig == nil || accessConfig.NatIP == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips = append(ips, *accessConfig.NatIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractAliasIPRanges extracts alias interface IPs from a list of interfaces.
|
||||||
|
func extractAliasIPRanges(interfaces []*computepb.NetworkInterface) []string {
|
||||||
|
ips := []string{}
|
||||||
|
for _, interf := range interfaces {
|
||||||
|
if interf == nil || interf.AliasIpRanges == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, aliasIP := range interf.AliasIpRanges {
|
||||||
|
if aliasIP == nil || aliasIP.IpCidrRange == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips = append(ips, *aliasIP.IpCidrRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
// extractSSHKeys extracts SSH keys from GCP instance metadata.
|
// extractSSHKeys extracts SSH keys from GCP instance metadata.
|
||||||
// reference: https://cloud.google.com/compute/docs/connect/add-ssh-keys .
|
// reference: https://cloud.google.com/compute/docs/connect/add-ssh-keys .
|
||||||
func extractSSHKeys(metadata map[string]string) map[string][]string {
|
func extractSSHKeys(metadata map[string]string) map[string][]string {
|
||||||
@ -239,17 +318,19 @@ func extractSSHKeys(metadata map[string]string) map[string][]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convertToCoreInstance converts a *computepb.Instance to a core.Instance.
|
// convertToCoreInstance converts a *computepb.Instance to a core.Instance.
|
||||||
func convertToCoreInstance(in *computepb.Instance, project string, zone string) (core.Instance, error) {
|
func convertToCoreInstance(in *computepb.Instance, project string, zone string) (cloudtypes.Instance, error) {
|
||||||
if in.Name == nil {
|
if in.Name == nil {
|
||||||
return core.Instance{}, fmt.Errorf("retrieving instance from compute API client returned invalid instance Name: %v", in.Name)
|
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance from compute API client returned invalid instance Name: %v", in.Name)
|
||||||
}
|
}
|
||||||
metadata := extractInstanceMetadata(in.Metadata, "", false)
|
metadata := extractInstanceMetadata(in.Metadata, "", false)
|
||||||
return core.Instance{
|
return cloudtypes.Instance{
|
||||||
Name: *in.Name,
|
Name: *in.Name,
|
||||||
ProviderID: joinProviderID(project, zone, *in.Name),
|
ProviderID: joinProviderID(project, zone, *in.Name),
|
||||||
Role: cloudprovider.ExtractRole(metadata),
|
Role: cloudprovider.ExtractRole(metadata),
|
||||||
IPs: extractIPs(in.NetworkInterfaces),
|
PrivateIPs: extractPrivateIPs(in.NetworkInterfaces),
|
||||||
SSHKeys: extractSSHKeys(metadata),
|
PublicIPs: extractPublicIPs(in.NetworkInterfaces),
|
||||||
|
AliasIPRanges: extractAliasIPRanges(in.NetworkInterfaces),
|
||||||
|
SSHKeys: extractSSHKeys(metadata),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,9 +343,7 @@ func joinProviderID(project, zone, instanceName string) string {
|
|||||||
// splitProviderID splits a provider's id into core components.
|
// splitProviderID splits a provider's id into core components.
|
||||||
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
|
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
|
||||||
func splitProviderID(providerID string) (project, zone, instance string, err error) {
|
func splitProviderID(providerID string) (project, zone, instance string, err error) {
|
||||||
// providerIDregex is a regex matching a gce providerID with each part of the URI being a submatch.
|
matches := providerIDRegex.FindStringSubmatch(providerID)
|
||||||
providerIDregex := regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
|
|
||||||
matches := providerIDregex.FindStringSubmatch(providerID)
|
|
||||||
if len(matches) != 4 {
|
if len(matches) != 4 {
|
||||||
return "", "", "", fmt.Errorf("error splitting providerID: %v", providerID)
|
return "", "", "", fmt.Errorf("error splitting providerID: %v", providerID)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
compute "cloud.google.com/go/compute/apiv1"
|
compute "cloud.google.com/go/compute/apiv1"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
gax "github.com/googleapis/gax-go/v2"
|
gax "github.com/googleapis/gax-go/v2"
|
||||||
@ -53,7 +54,11 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
NetworkInterfaces: []*computepb.NetworkInterface{
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||||
{NetworkIP: proto.String("192.0.2.0")},
|
{
|
||||||
|
NetworkIP: proto.String("192.0.2.0"),
|
||||||
|
AliasIpRanges: []*computepb.AliasIpRange{{IpCidrRange: proto.String("192.0.2.0/16")}},
|
||||||
|
AccessConfigs: []*computepb.AccessConfig{{NatIP: proto.String("192.0.2.1")}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -65,20 +70,22 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
metadata stubMetadataClient
|
metadata stubMetadataClient
|
||||||
instanceIter *stubInstanceIterator
|
instanceIter *stubInstanceIterator
|
||||||
instanceIterMutator func(*stubInstanceIterator)
|
instanceIterMutator func(*stubInstanceIterator)
|
||||||
wantInstances []core.Instance
|
wantInstances []cloudtypes.Instance
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"retrieve works": {
|
"retrieve works": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
metadata: stubMetadataClient{InstanceValue: uid},
|
metadata: stubMetadataClient{InstanceValue: uid},
|
||||||
instanceIter: newTestIter(),
|
instanceIter: newTestIter(),
|
||||||
wantInstances: []core.Instance{
|
wantInstances: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -94,13 +101,15 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
metadata: stubMetadataClient{InstanceValue: uid},
|
metadata: stubMetadataClient{InstanceValue: uid},
|
||||||
instanceIter: newTestIter(),
|
instanceIter: newTestIter(),
|
||||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces = nil },
|
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces = nil },
|
||||||
wantInstances: []core.Instance{
|
wantInstances: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
IPs: []string{},
|
AliasIPRanges: []string{},
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
PublicIPs: []string{},
|
||||||
|
PrivateIPs: []string{},
|
||||||
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -109,13 +118,15 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
metadata: stubMetadataClient{InstanceValue: uid},
|
metadata: stubMetadataClient{InstanceValue: uid},
|
||||||
instanceIter: newTestIter(),
|
instanceIter: newTestIter(),
|
||||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces[0].NetworkIP = nil },
|
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces[0].NetworkIP = nil },
|
||||||
wantInstances: []core.Instance{
|
wantInstances: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
IPs: []string{},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{},
|
||||||
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -124,7 +135,7 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
metadata: stubMetadataClient{InstanceValue: uid},
|
metadata: stubMetadataClient{InstanceValue: uid},
|
||||||
instanceIter: newTestIter(),
|
instanceIter: newTestIter(),
|
||||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[2].Key = proto.String("") },
|
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[2].Key = proto.String("") },
|
||||||
wantInstances: []core.Instance{},
|
wantInstances: []cloudtypes.Instance{},
|
||||||
},
|
},
|
||||||
"constellation retrieval fails": {
|
"constellation retrieval fails": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
@ -137,13 +148,15 @@ func TestRetrieveInstances(t *testing.T) {
|
|||||||
metadata: stubMetadataClient{InstanceValue: uid},
|
metadata: stubMetadataClient{InstanceValue: uid},
|
||||||
instanceIter: newTestIter(),
|
instanceIter: newTestIter(),
|
||||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[3].Key = proto.String("") },
|
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[3].Key = proto.String("") },
|
||||||
wantInstances: []core.Instance{
|
wantInstances: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
Role: role.Unknown,
|
Role: role.Unknown,
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -198,7 +211,11 @@ func TestRetrieveInstance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
NetworkInterfaces: []*computepb.NetworkInterface{
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||||
{NetworkIP: proto.String("192.0.2.0")},
|
{
|
||||||
|
NetworkIP: proto.String("192.0.2.0"),
|
||||||
|
AliasIpRanges: []*computepb.AliasIpRange{{IpCidrRange: proto.String("192.0.2.0/16")}},
|
||||||
|
AccessConfigs: []*computepb.AccessConfig{{NatIP: proto.String("192.0.2.1")}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,17 +224,19 @@ func TestRetrieveInstance(t *testing.T) {
|
|||||||
client stubInstancesClient
|
client stubInstancesClient
|
||||||
clientInstance *computepb.Instance
|
clientInstance *computepb.Instance
|
||||||
clientInstanceMutator func(*computepb.Instance)
|
clientInstanceMutator func(*computepb.Instance)
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"retrieve works": {
|
"retrieve works": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"retrieve with SSH key works": {
|
"retrieve with SSH key works": {
|
||||||
@ -227,11 +246,13 @@ func TestRetrieveInstance(t *testing.T) {
|
|||||||
i.Metadata.Items[0].Key = proto.String("ssh-keys")
|
i.Metadata.Items[0].Key = proto.String("ssh-keys")
|
||||||
i.Metadata.Items[0].Value = proto.String("bob:ssh-rsa bobskey")
|
i.Metadata.Items[0].Value = proto.String("bob:ssh-rsa bobskey")
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"retrieve with Role works": {
|
"retrieve with Role works": {
|
||||||
@ -241,12 +262,14 @@ func TestRetrieveInstance(t *testing.T) {
|
|||||||
i.Metadata.Items[0].Key = proto.String(core.RoleMetadataKey)
|
i.Metadata.Items[0].Key = proto.String(core.RoleMetadataKey)
|
||||||
i.Metadata.Items[0].Value = proto.String(role.Coordinator.String())
|
i.Metadata.Items[0].Value = proto.String(role.Coordinator.String())
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
Role: role.Coordinator,
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
IPs: []string{"192.0.2.0"},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
SSHKeys: map[string][]string{},
|
Role: role.Coordinator,
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"retrieve fails": {
|
"retrieve fails": {
|
||||||
@ -260,55 +283,91 @@ func TestRetrieveInstance(t *testing.T) {
|
|||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0] = nil },
|
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0] = nil },
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"metadata key is null": {
|
"metadata key is null": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Key = nil },
|
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Key = nil },
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"metadata value is null": {
|
"metadata value is null": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Value = nil },
|
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Value = nil },
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"instance without network ip": {
|
"instance without network ip": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0] = nil },
|
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0] = nil },
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{},
|
AliasIPRanges: []string{},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{},
|
||||||
|
PrivateIPs: []string{},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"network ip is nil": {
|
"network ip is nil": {
|
||||||
client: stubInstancesClient{},
|
client: stubInstancesClient{},
|
||||||
clientInstance: newTestInstance(),
|
clientInstance: newTestInstance(),
|
||||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].NetworkIP = nil },
|
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].NetworkIP = nil },
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{},
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
SSHKeys: map[string][]string{},
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"network alias cidr is nil": {
|
||||||
|
client: stubInstancesClient{},
|
||||||
|
clientInstance: newTestInstance(),
|
||||||
|
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].AliasIpRanges[0].IpCidrRange = nil },
|
||||||
|
wantInstance: cloudtypes.Instance{
|
||||||
|
Name: "someInstance",
|
||||||
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
|
AliasIPRanges: []string{},
|
||||||
|
PublicIPs: []string{"192.0.2.1"},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"network public ip is nil": {
|
||||||
|
client: stubInstancesClient{},
|
||||||
|
clientInstance: newTestInstance(),
|
||||||
|
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].AccessConfigs[0].NatIP = nil },
|
||||||
|
wantInstance: cloudtypes.Instance{
|
||||||
|
Name: "someInstance",
|
||||||
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
|
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||||
|
PublicIPs: []string{},
|
||||||
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
|
SSHKeys: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -620,15 +679,107 @@ func TestUnsetInstanceMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRetrieveSubnetworkAliasCIDR(t *testing.T) {
|
||||||
|
aliasCIDR := "192.0.2.1/24"
|
||||||
|
someErr := errors.New("some error")
|
||||||
|
testCases := map[string]struct {
|
||||||
|
stubInstancesClient stubInstancesClient
|
||||||
|
stubSubnetworksClient stubSubnetworksClient
|
||||||
|
wantAliasCIDR string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"RetrieveSubnetworkAliasCIDR works": {
|
||||||
|
stubInstancesClient: stubInstancesClient{
|
||||||
|
GetInstance: &computepb.Instance{
|
||||||
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||||
|
{
|
||||||
|
Subnetwork: proto.String("projects/project/regions/region/subnetworks/subnetwork"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stubSubnetworksClient: stubSubnetworksClient{
|
||||||
|
GetSubnetwork: &computepb.Subnetwork{
|
||||||
|
IpCidrRange: &aliasCIDR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantAliasCIDR: aliasCIDR,
|
||||||
|
},
|
||||||
|
"instance has no network interface": {
|
||||||
|
stubInstancesClient: stubInstancesClient{
|
||||||
|
GetInstance: &computepb.Instance{
|
||||||
|
NetworkInterfaces: []*computepb.NetworkInterface{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"cannot get instance": {
|
||||||
|
stubInstancesClient: stubInstancesClient{
|
||||||
|
GetErr: someErr,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"cannot get subnetwork": {
|
||||||
|
stubInstancesClient: stubInstancesClient{
|
||||||
|
GetInstance: &computepb.Instance{
|
||||||
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||||
|
{
|
||||||
|
Subnetwork: proto.String("projects/project/regions/region/subnetworks/subnetwork"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stubSubnetworksClient: stubSubnetworksClient{
|
||||||
|
GetErr: someErr,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"subnetwork has no cidr range": {
|
||||||
|
stubInstancesClient: stubInstancesClient{
|
||||||
|
GetInstance: &computepb.Instance{
|
||||||
|
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||||
|
{
|
||||||
|
Subnetwork: proto.String("projects/project/regions/region/subnetworks/subnetwork"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stubSubnetworksClient: stubSubnetworksClient{
|
||||||
|
GetSubnetwork: &computepb.Subnetwork{},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
client := Client{instanceAPI: tc.stubInstancesClient, subnetworkAPI: tc.stubSubnetworksClient}
|
||||||
|
aliasCIDR, err := client.RetrieveSubnetworkAliasCIDR(context.Background(), "project", "zone", "subnetwork")
|
||||||
|
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tc.wantAliasCIDR, aliasCIDR)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClose(t *testing.T) {
|
func TestClose(t *testing.T) {
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
client := Client{instanceAPI: stubInstancesClient{}}
|
client := Client{instanceAPI: stubInstancesClient{}, subnetworkAPI: stubSubnetworksClient{}}
|
||||||
assert.NoError(client.Close())
|
assert.NoError(client.Close())
|
||||||
|
|
||||||
client = Client{instanceAPI: stubInstancesClient{CloseErr: someErr}}
|
client = Client{instanceAPI: stubInstancesClient{CloseErr: someErr}, subnetworkAPI: stubSubnetworksClient{}}
|
||||||
|
assert.Error(client.Close())
|
||||||
|
|
||||||
|
client = Client{instanceAPI: stubInstancesClient{}, subnetworkAPI: stubSubnetworksClient{CloseErr: someErr}}
|
||||||
assert.Error(client.Close())
|
assert.Error(client.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,6 +876,25 @@ func (s stubInstancesClient) Close() error {
|
|||||||
return s.CloseErr
|
return s.CloseErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stubSubnetworksClient struct {
|
||||||
|
GetSubnetwork *computepb.Subnetwork
|
||||||
|
GetErr error
|
||||||
|
SubnetworkIterator SubnetworkIterator
|
||||||
|
CloseErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s stubSubnetworksClient) Get(ctx context.Context, req *computepb.GetSubnetworkRequest, opts ...gax.CallOption) (*computepb.Subnetwork, error) {
|
||||||
|
return s.GetSubnetwork, s.GetErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s stubSubnetworksClient) List(ctx context.Context, req *computepb.ListSubnetworksRequest, opts ...gax.CallOption) SubnetworkIterator {
|
||||||
|
return s.SubnetworkIterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s stubSubnetworksClient) Close() error {
|
||||||
|
return s.CloseErr
|
||||||
|
}
|
||||||
|
|
||||||
type stubMetadataClient struct {
|
type stubMetadataClient struct {
|
||||||
InstanceValue string
|
InstanceValue string
|
||||||
InstanceErr error
|
InstanceErr error
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
)
|
)
|
||||||
@ -11,9 +12,9 @@ import (
|
|||||||
// API handles all GCP API requests.
|
// API handles all GCP API requests.
|
||||||
type API interface {
|
type API interface {
|
||||||
// RetrieveInstances retrieves a list of all accessible GCP instances with their metadata.
|
// RetrieveInstances retrieves a list of all accessible GCP instances with their metadata.
|
||||||
RetrieveInstances(ctx context.Context, project, zone string) ([]core.Instance, error)
|
RetrieveInstances(ctx context.Context, project, zone string) ([]cloudtypes.Instance, error)
|
||||||
// RetrieveInstances retrieves a single GCP instances with its metadata.
|
// RetrieveInstances retrieves a single GCP instances with its metadata.
|
||||||
RetrieveInstance(ctx context.Context, project, zone, instanceName string) (core.Instance, error)
|
RetrieveInstance(ctx context.Context, project, zone, instanceName string) (cloudtypes.Instance, error)
|
||||||
// RetrieveInstanceMetadata retrieves the GCP instance metadata of the current instance.
|
// RetrieveInstanceMetadata retrieves the GCP instance metadata of the current instance.
|
||||||
RetrieveInstanceMetadata(attr string) (string, error)
|
RetrieveInstanceMetadata(attr string) (string, error)
|
||||||
// RetrieveProjectID retrieves the GCP projectID containing the current instance.
|
// RetrieveProjectID retrieves the GCP projectID containing the current instance.
|
||||||
@ -22,6 +23,8 @@ type API interface {
|
|||||||
RetrieveZone() (string, error)
|
RetrieveZone() (string, error)
|
||||||
// RetrieveInstanceName retrieves the instance name of the current instance.
|
// RetrieveInstanceName retrieves the instance name of the current instance.
|
||||||
RetrieveInstanceName() (string, error)
|
RetrieveInstanceName() (string, error)
|
||||||
|
// RetrieveSubnetworkAliasCIDR retrieves the subnetwork CIDR of the current instance.
|
||||||
|
RetrieveSubnetworkAliasCIDR(ctx context.Context, project, zone, instanceName string) (string, error)
|
||||||
// SetInstanceMetadata sets metadata key: value of the instance specified by project, zone and instanceName.
|
// SetInstanceMetadata sets metadata key: value of the instance specified by project, zone and instanceName.
|
||||||
SetInstanceMetadata(ctx context.Context, project, zone, instanceName, key, value string) error
|
SetInstanceMetadata(ctx context.Context, project, zone, instanceName, key, value string) error
|
||||||
// UnsetInstanceMetadata removes a metadata key-value pair of the instance specified by project, zone and instanceName.
|
// UnsetInstanceMetadata removes a metadata key-value pair of the instance specified by project, zone and instanceName.
|
||||||
@ -41,7 +44,7 @@ func New(api API) *Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
func (m *Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
project, err := m.api.RetrieveProjectID()
|
project, err := m.api.RetrieveProjectID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -58,27 +61,27 @@ func (m *Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
func (m *Metadata) Self(ctx context.Context) (core.Instance, error) {
|
func (m *Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
project, err := m.api.RetrieveProjectID()
|
project, err := m.api.RetrieveProjectID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
zone, err := m.api.RetrieveZone()
|
zone, err := m.api.RetrieveZone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
instanceName, err := m.api.RetrieveInstanceName()
|
instanceName, err := m.api.RetrieveInstanceName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, err
|
return cloudtypes.Instance{}, err
|
||||||
}
|
}
|
||||||
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance retrieves an instance using its providerID.
|
// GetInstance retrieves an instance using its providerID.
|
||||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
project, zone, instanceName, err := splitProviderID(providerID)
|
project, zone, instanceName, err := splitProviderID(providerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Instance{}, fmt.Errorf("invalid providerID: %w", err)
|
return cloudtypes.Instance{}, fmt.Errorf("invalid providerID: %w", err)
|
||||||
}
|
}
|
||||||
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
||||||
}
|
}
|
||||||
@ -117,6 +120,33 @@ func (m *Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
|||||||
return m.api.SetInstanceMetadata(ctx, project, zone, instanceName, core.VPNIPMetadataKey, vpnIP)
|
return m.api.SetInstanceMetadata(ctx, project, zone, instanceName, core.VPNIPMetadataKey, vpnIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSubnetworkCIDR returns the subnetwork CIDR of the current instance.
|
||||||
|
func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||||
|
project, err := m.api.RetrieveProjectID()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
zone, err := m.api.RetrieveZone()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
instanceName, err := m.api.RetrieveInstanceName()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return m.api.RetrieveSubnetworkAliasCIDR(ctx, project, zone, instanceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
|
||||||
|
func (m *Metadata) SupportsLoadBalancer() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoadBalancerIP returns the IP of the load balancer.
|
||||||
|
func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
// Supported is used to determine if metadata API is implemented for this cloud provider.
|
// Supported is used to determine if metadata API is implemented for this cloud provider.
|
||||||
func (m *Metadata) Supported() bool {
|
func (m *Metadata) Supported() bool {
|
||||||
return true
|
return true
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -14,22 +15,22 @@ import (
|
|||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
err := errors.New("some err")
|
err := errors.New("some err")
|
||||||
uid := "1234"
|
uid := "1234"
|
||||||
instancesGenerator := func() *[]core.Instance {
|
instancesGenerator := func() *[]cloudtypes.Instance {
|
||||||
return &[]core.Instance{
|
return &[]cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
client stubGCPClient
|
client stubGCPClient
|
||||||
instancesGenerator func() *[]core.Instance
|
instancesGenerator func() *[]cloudtypes.Instance
|
||||||
instancesMutator func(*[]core.Instance)
|
instancesMutator func(*[]cloudtypes.Instance)
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstances []core.Instance
|
wantInstances []cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"retrieve works": {
|
"retrieve works": {
|
||||||
client: stubGCPClient{
|
client: stubGCPClient{
|
||||||
@ -40,11 +41,11 @@ func TestList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
instancesGenerator: instancesGenerator,
|
instancesGenerator: instancesGenerator,
|
||||||
wantInstances: []core.Instance{
|
wantInstances: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -105,22 +106,22 @@ func TestSelf(t *testing.T) {
|
|||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
client stubGCPClient
|
client stubGCPClient
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"retrieve works": {
|
"retrieve works": {
|
||||||
client: stubGCPClient{
|
client: stubGCPClient{
|
||||||
projectID: "someProjectID",
|
projectID: "someProjectID",
|
||||||
zone: "someZone",
|
zone: "someZone",
|
||||||
retrieveInstanceValue: core.Instance{
|
retrieveInstanceValue: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"retrieve error is detected": {
|
"retrieve error is detected": {
|
||||||
@ -179,21 +180,21 @@ func TestGetInstance(t *testing.T) {
|
|||||||
providerID string
|
providerID string
|
||||||
client stubGCPClient
|
client stubGCPClient
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantInstance core.Instance
|
wantInstance cloudtypes.Instance
|
||||||
}{
|
}{
|
||||||
"retrieve works": {
|
"retrieve works": {
|
||||||
providerID: "gce://someProject/someZone/someInstance",
|
providerID: "gce://someProject/someZone/someInstance",
|
||||||
client: stubGCPClient{
|
client: stubGCPClient{
|
||||||
retrieveInstanceValue: core.Instance{
|
retrieveInstanceValue: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantInstance: core.Instance{
|
wantInstance: cloudtypes.Instance{
|
||||||
Name: "someInstance",
|
Name: "someInstance",
|
||||||
ProviderID: "gce://someProject/someZone/someInstance",
|
ProviderID: "gce://someProject/someZone/someInstance",
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"retrieve error is detected": {
|
"retrieve error is detected": {
|
||||||
@ -357,12 +358,13 @@ func TestTrivialMetadataFunctions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stubGCPClient struct {
|
type stubGCPClient struct {
|
||||||
retrieveInstanceValue core.Instance
|
retrieveInstanceValue cloudtypes.Instance
|
||||||
retrieveInstanceErr error
|
retrieveInstanceErr error
|
||||||
retrieveInstancesValues []core.Instance
|
retrieveInstancesValues []cloudtypes.Instance
|
||||||
retrieveInstancesErr error
|
retrieveInstancesErr error
|
||||||
retrieveInstanceMetadaValues map[string]string
|
retrieveInstanceMetadaValues map[string]string
|
||||||
retrieveInstanceMetadataErr error
|
retrieveInstanceMetadataErr error
|
||||||
|
retrieveSubentworkAliasErr error
|
||||||
projectID string
|
projectID string
|
||||||
zone string
|
zone string
|
||||||
instanceName string
|
instanceName string
|
||||||
@ -384,11 +386,11 @@ type stubGCPClient struct {
|
|||||||
unsetMetadataKeys []string
|
unsetMetadataKeys []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubGCPClient) RetrieveInstances(ctx context.Context, project, zone string) ([]core.Instance, error) {
|
func (s *stubGCPClient) RetrieveInstances(ctx context.Context, project, zone string) ([]cloudtypes.Instance, error) {
|
||||||
return s.retrieveInstancesValues, s.retrieveInstancesErr
|
return s.retrieveInstancesValues, s.retrieveInstancesErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubGCPClient) RetrieveInstance(ctx context.Context, project, zone string, instanceName string) (core.Instance, error) {
|
func (s *stubGCPClient) RetrieveInstance(ctx context.Context, project, zone string, instanceName string) (cloudtypes.Instance, error) {
|
||||||
return s.retrieveInstanceValue, s.retrieveInstanceErr
|
return s.retrieveInstanceValue, s.retrieveInstanceErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,3 +428,7 @@ func (s *stubGCPClient) UnsetInstanceMetadata(ctx context.Context, project, zone
|
|||||||
|
|
||||||
return s.unsetInstanceMetadataErr
|
return s.unsetInstanceMetadataErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubGCPClient) RetrieveSubnetworkAliasCIDR(ctx context.Context, project, zone, instanceName string) (string, error) {
|
||||||
|
return "", s.retrieveSubentworkAliasErr
|
||||||
|
}
|
||||||
|
@ -23,6 +23,26 @@ func (c *instanceClient) List(ctx context.Context, req *computepb.ListInstancesR
|
|||||||
return c.InstancesClient.List(ctx, req)
|
return c.InstancesClient.List(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type subnetworkClient struct {
|
||||||
|
*compute.SubnetworksClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *subnetworkClient) Close() error {
|
||||||
|
return c.SubnetworksClient.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *subnetworkClient) List(ctx context.Context, req *computepb.ListSubnetworksRequest,
|
||||||
|
opts ...gax.CallOption,
|
||||||
|
) SubnetworkIterator {
|
||||||
|
return c.SubnetworksClient.List(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *subnetworkClient) Get(ctx context.Context, req *computepb.GetSubnetworkRequest,
|
||||||
|
opts ...gax.CallOption,
|
||||||
|
) (*computepb.Subnetwork, error) {
|
||||||
|
return c.SubnetworksClient.Get(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
type metadataClient struct{}
|
type metadataClient struct{}
|
||||||
|
|
||||||
func (c *metadataClient) InstanceAttributeValue(attr string) (string, error) {
|
func (c *metadataClient) InstanceAttributeValue(attr string) (string, error) {
|
||||||
|
@ -5,9 +5,9 @@ const (
|
|||||||
CloudControllerManagerImageAWS = "us.gcr.io/k8s-artifacts-prod/provider-aws/cloud-controller-manager:v1.22.0-alpha.0"
|
CloudControllerManagerImageAWS = "us.gcr.io/k8s-artifacts-prod/provider-aws/cloud-controller-manager:v1.22.0-alpha.0"
|
||||||
// CloudControllerManagerImageGCP is the CCM image used on GCP.
|
// CloudControllerManagerImageGCP is the CCM image used on GCP.
|
||||||
// TODO: use newer "cloud-provider-gcp" from https://github.com/kubernetes/cloud-provider-gcp when newer releases are available.
|
// TODO: use newer "cloud-provider-gcp" from https://github.com/kubernetes/cloud-provider-gcp when newer releases are available.
|
||||||
CloudControllerManagerImageGCP = "ghcr.io/malt3/cloud-provider-gcp:latest"
|
CloudControllerManagerImageGCP = "ghcr.io/edgelesssys/cloud-provider-gcp:sha-2f6a5b07fc2d37f24f8ff725132f87584d627d8f"
|
||||||
// CloudControllerManagerImageAzure is the CCM image used on Azure.
|
// CloudControllerManagerImageAzure is the CCM image used on Azure.
|
||||||
CloudControllerManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-controller-manager:v1.23.5"
|
CloudControllerManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-controller-manager:v1.23.11"
|
||||||
// CloudNodeManagerImageAzure is the cloud-node-manager image used on Azure.
|
// CloudNodeManagerImageAzure is the cloud-node-manager image used on Azure.
|
||||||
CloudNodeManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-node-manager:v1.23.5"
|
CloudNodeManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-node-manager:v1.23.11"
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
@ -15,7 +15,7 @@ func (a Autoscaler) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
func (a Autoscaler) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (a Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
return resources.Secrets{}, nil
|
return resources.Secrets{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"context"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
k8s "k8s.io/api/core/v1"
|
k8s "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
@ -31,13 +33,13 @@ func (c CloudControllerManager) ExtraArgs() []string {
|
|||||||
|
|
||||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||||
func (c CloudControllerManager) ConfigMaps(instance core.Instance) (resources.ConfigMaps, error) {
|
func (c CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||||
return resources.ConfigMaps{}, nil
|
return resources.ConfigMaps{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||||
func (c CloudControllerManager) Secrets(instance core.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
func (c CloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
return resources.Secrets{}, nil
|
return resources.Secrets{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ func (c CloudControllerManager) Env() []k8s.EnvVar {
|
|||||||
|
|
||||||
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
||||||
// Allows for cloud-provider specific hooks.
|
// Allows for cloud-provider specific hooks.
|
||||||
func (c CloudControllerManager) PrepareInstance(instance core.Instance, vpnIP string) error {
|
func (c CloudControllerManager) PrepareInstance(instance cloudtypes.Instance, vpnIP string) error {
|
||||||
// no specific hook required.
|
// no specific hook required.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package qemu
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,17 +16,17 @@ func (m *Metadata) Supported() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
func (m *Metadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
panic("function *Metadata.List not implemented")
|
panic("function *Metadata.List not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
func (m *Metadata) Self(ctx context.Context) (core.Instance, error) {
|
func (m *Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
panic("function *Metdata.Self not implemented")
|
panic("function *Metdata.Self not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance retrieves an instance using its providerID.
|
// GetInstance retrieves an instance using its providerID.
|
||||||
func (m Metadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
panic("function *Metadata.GetInstance not implemented")
|
panic("function *Metadata.GetInstance not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,3 +39,18 @@ func (m Metadata) SignalRole(ctx context.Context, role role.Role) error {
|
|||||||
func (m Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
func (m Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
||||||
panic("function *Metadata.SetVPNIP not implemented")
|
panic("function *Metadata.SetVPNIP not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
|
||||||
|
func (m Metadata) SupportsLoadBalancer() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoadBalancerIP returns the IP of the load balancer.
|
||||||
|
func (m Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||||
|
panic("function *Metadata.GetLoadBalancerIP not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubnetworkCIDR retrieves the subnetwork CIDR from cloud provider metadata.
|
||||||
|
func (m Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||||
|
panic("function *Metadata.GetSubnetworkCIDR not implemented")
|
||||||
|
}
|
||||||
|
@ -42,10 +42,7 @@ func main() {
|
|||||||
var bindIP, bindPort, etcdEndpoint string
|
var bindIP, bindPort, etcdEndpoint string
|
||||||
var enforceEtcdTls bool
|
var enforceEtcdTls bool
|
||||||
var kube core.Cluster
|
var kube core.Cluster
|
||||||
var metadata core.ProviderMetadata
|
var coreMetadata core.ProviderMetadata
|
||||||
var cloudControllerManager core.CloudControllerManager
|
|
||||||
var cloudNodeManager core.CloudNodeManager
|
|
||||||
var autoscaler core.ClusterAutoscaler
|
|
||||||
var encryptedDisk core.EncryptedDisk
|
var encryptedDisk core.EncryptedDisk
|
||||||
cfg := zap.NewDevelopmentConfig()
|
cfg := zap.NewDevelopmentConfig()
|
||||||
|
|
||||||
@ -87,15 +84,13 @@ func main() {
|
|||||||
issuer = gcp.NewIssuer()
|
issuer = gcp.NewIssuer()
|
||||||
validator = gcp.NewValidator(pcrs)
|
validator = gcp.NewValidator(pcrs)
|
||||||
|
|
||||||
kube = kubernetes.New(k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New())
|
|
||||||
gcpClient, err := gcpcloud.NewClient(context.Background())
|
gcpClient, err := gcpcloud.NewClient(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("creating GCP client failed: %v\n", err)
|
log.Fatalf("creating GCP client failed: %v\n", err)
|
||||||
}
|
}
|
||||||
metadata = gcpcloud.New(gcpClient)
|
metadata := gcpcloud.New(gcpClient)
|
||||||
cloudControllerManager = &gcpcloud.CloudControllerManager{}
|
coreMetadata = metadata
|
||||||
cloudNodeManager = &gcpcloud.CloudNodeManager{}
|
kube = kubernetes.New("gcp", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &gcpcloud.CloudControllerManager{}, &gcpcloud.CloudNodeManager{}, &gcpcloud.Autoscaler{}, metadata)
|
||||||
autoscaler = &gcpcloud.Autoscaler{}
|
|
||||||
encryptedDisk = diskencryption.New()
|
encryptedDisk = diskencryption.New()
|
||||||
bindIP = defaultIP
|
bindIP = defaultIP
|
||||||
bindPort = defaultPort
|
bindPort = defaultPort
|
||||||
@ -112,14 +107,13 @@ func main() {
|
|||||||
issuer = azure.NewIssuer()
|
issuer = azure.NewIssuer()
|
||||||
validator = azure.NewValidator(pcrs)
|
validator = azure.NewValidator(pcrs)
|
||||||
|
|
||||||
kube = kubernetes.New(k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New())
|
metadata, err := azurecloud.NewMetadata(context.Background())
|
||||||
metadata, err = azurecloud.NewMetadata(context.Background())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
cloudControllerManager = &azurecloud.CloudControllerManager{}
|
coreMetadata = metadata
|
||||||
cloudNodeManager = &azurecloud.CloudNodeManager{}
|
kube = kubernetes.New("azure", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), azurecloud.NewCloudControllerManager(metadata), &azurecloud.CloudNodeManager{}, &azurecloud.Autoscaler{}, metadata)
|
||||||
autoscaler = &azurecloud.Autoscaler{}
|
|
||||||
encryptedDisk = diskencryption.New()
|
encryptedDisk = diskencryption.New()
|
||||||
bindIP = defaultIP
|
bindIP = defaultIP
|
||||||
bindPort = defaultPort
|
bindPort = defaultPort
|
||||||
@ -136,13 +130,10 @@ func main() {
|
|||||||
issuer = qemu.NewIssuer()
|
issuer = qemu.NewIssuer()
|
||||||
validator = qemu.NewValidator(pcrs)
|
validator = qemu.NewValidator(pcrs)
|
||||||
|
|
||||||
kube = kubernetes.New(k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New())
|
|
||||||
|
|
||||||
// no support for cloud services in qemu
|
// no support for cloud services in qemu
|
||||||
metadata = &qemucloud.Metadata{}
|
metadata := &qemucloud.Metadata{}
|
||||||
cloudControllerManager = &qemucloud.CloudControllerManager{}
|
kube = kubernetes.New("qemu", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &qemucloud.CloudControllerManager{}, &qemucloud.CloudNodeManager{}, &qemucloud.Autoscaler{}, metadata)
|
||||||
cloudNodeManager = &qemucloud.CloudNodeManager{}
|
coreMetadata = metadata
|
||||||
autoscaler = &qemucloud.Autoscaler{}
|
|
||||||
|
|
||||||
encryptedDisk = diskencryption.New()
|
encryptedDisk = diskencryption.New()
|
||||||
bindIP = defaultIP
|
bindIP = defaultIP
|
||||||
@ -155,10 +146,7 @@ func main() {
|
|||||||
issuer = core.NewMockIssuer()
|
issuer = core.NewMockIssuer()
|
||||||
validator = core.NewMockValidator()
|
validator = core.NewMockValidator()
|
||||||
kube = &core.ClusterFake{}
|
kube = &core.ClusterFake{}
|
||||||
metadata = &core.ProviderMetadataFake{}
|
coreMetadata = &core.ProviderMetadataFake{}
|
||||||
cloudControllerManager = &core.CloudControllerManagerFake{}
|
|
||||||
cloudNodeManager = &core.CloudNodeManagerFake{}
|
|
||||||
autoscaler = &core.ClusterAutoscalerFake{}
|
|
||||||
encryptedDisk = &core.EncryptedDiskFake{}
|
encryptedDisk = &core.EncryptedDiskFake{}
|
||||||
bindIP = defaultIP
|
bindIP = defaultIP
|
||||||
bindPort = defaultPort
|
bindPort = defaultPort
|
||||||
@ -174,5 +162,5 @@ func main() {
|
|||||||
netDialer := &net.Dialer{}
|
netDialer := &net.Dialer{}
|
||||||
dialer := grpcutil.NewDialer(validator, netDialer)
|
dialer := grpcutil.NewDialer(validator, netDialer)
|
||||||
run(issuer, wg, openTPM, util.GetIPAddr, dialer, fileHandler, kube,
|
run(issuer, wg, openTPM, util.GetIPAddr, dialer, fileHandler, kube,
|
||||||
metadata, cloudControllerManager, cloudNodeManager, autoscaler, encryptedDisk, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore, fs)
|
coreMetadata, encryptedDisk, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore, fs)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import (
|
|||||||
var version = "0.0.0"
|
var version = "0.0.0"
|
||||||
|
|
||||||
func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPublicIPAddr func() (string, error), dialer *grpcutil.Dialer, fileHandler file.Handler,
|
func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPublicIPAddr func() (string, error), dialer *grpcutil.Dialer, fileHandler file.Handler,
|
||||||
kube core.Cluster, metadata core.ProviderMetadata, cloudControllerManager core.CloudControllerManager, cloudNodeManager core.CloudNodeManager, clusterAutoscaler core.ClusterAutoscaler, encryptedDisk core.EncryptedDisk, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger,
|
kube core.Cluster, metadata core.ProviderMetadata, encryptedDisk core.EncryptedDisk, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger,
|
||||||
fs afero.Fs,
|
fs afero.Fs,
|
||||||
) {
|
) {
|
||||||
defer zapLoggerCore.Sync()
|
defer zapLoggerCore.Sync()
|
||||||
@ -47,7 +47,7 @@ func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPub
|
|||||||
Logger: zapLoggerCore.WithOptions(zap.IncreaseLevel(zap.WarnLevel)).Named("etcd"),
|
Logger: zapLoggerCore.WithOptions(zap.IncreaseLevel(zap.WarnLevel)).Named("etcd"),
|
||||||
}
|
}
|
||||||
linuxUserManager := user.NewLinuxUserManager(fs)
|
linuxUserManager := user.NewLinuxUserManager(fs)
|
||||||
core, err := core.NewCore(vpn, kube, metadata, cloudControllerManager, cloudNodeManager, clusterAutoscaler, encryptedDisk, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler, linuxUserManager)
|
core, err := core.NewCore(vpn, kube, metadata, encryptedDisk, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler, linuxUserManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zapLoggerCore.Fatal("failed to create core", zap.Error(err))
|
zapLoggerCore.Fatal("failed to create core", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ func TestConcurrent(t *testing.T) {
|
|||||||
func spawnPeer(require *require.Assertions, logger *zap.Logger, netDialer *testdialer.BufconnDialer, netw *network, endpoint string) (*grpc.Server, *pubapi.API, *fakeVPN) {
|
func spawnPeer(require *require.Assertions, logger *zap.Logger, netDialer *testdialer.BufconnDialer, netw *network, endpoint string) (*grpc.Server, *pubapi.API, *fakeVPN) {
|
||||||
vpn := newVPN(netw, endpoint)
|
vpn := newVPN(netw, endpoint)
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.CloudControllerManagerFake{}, &core.CloudNodeManagerFake{}, &core.ClusterAutoscalerFake{}, &core.EncryptedDiskFake{}, logger, simulator.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.EncryptedDiskFake{}, logger, simulator.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NoError(cor.AdvanceState(state.AcceptingInit, nil, nil))
|
require.NoError(cor.AdvanceState(state.AcceptingInit, nil, nil))
|
||||||
|
|
||||||
|
@ -6,9 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
k8s "k8s.io/api/core/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnimplemented = errors.New("unimplemented")
|
var ErrUnimplemented = errors.New("unimplemented")
|
||||||
@ -20,24 +19,12 @@ const (
|
|||||||
VPNIPMetadataKey = "constellation-vpn-ip"
|
VPNIPMetadataKey = "constellation-vpn-ip"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Instance describes a cloud-provider instance including name, providerID, ip addresses and instance metadata.
|
|
||||||
type Instance struct {
|
|
||||||
Name string
|
|
||||||
ProviderID string
|
|
||||||
Role role.Role
|
|
||||||
IPs []string
|
|
||||||
// SSHKeys maps usernames to ssh public keys.
|
|
||||||
SSHKeys map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderMetadata implementers read/write cloud provider metadata.
|
// ProviderMetadata implementers read/write cloud provider metadata.
|
||||||
type ProviderMetadata interface {
|
type ProviderMetadata interface {
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
List(ctx context.Context) ([]Instance, error)
|
List(ctx context.Context) ([]cloudtypes.Instance, error)
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
Self(ctx context.Context) (Instance, error)
|
Self(ctx context.Context) (cloudtypes.Instance, error)
|
||||||
// GetInstance retrieves an instance using its providerID.
|
|
||||||
GetInstance(ctx context.Context, providerID string) (Instance, error)
|
|
||||||
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
|
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
|
||||||
SignalRole(ctx context.Context, role role.Role) error
|
SignalRole(ctx context.Context, role role.Role) error
|
||||||
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
|
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
|
||||||
@ -46,121 +33,19 @@ type ProviderMetadata interface {
|
|||||||
Supported() bool
|
Supported() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloudControllerManager implementers provide configuration for the k8s cloud-controller-manager.
|
|
||||||
type CloudControllerManager interface {
|
|
||||||
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
|
|
||||||
Image() string
|
|
||||||
// Path returns the path used by cloud-controller-manager executable within the container image.
|
|
||||||
Path() string
|
|
||||||
// Name returns the cloud-provider name as used by k8s cloud-controller-manager (k8s.gcr.io/cloud-controller-manager).
|
|
||||||
Name() string
|
|
||||||
// ExtraArgs returns a list of arguments to append to the cloud-controller-manager command.
|
|
||||||
ExtraArgs() []string
|
|
||||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
|
||||||
ConfigMaps(instance Instance) (resources.ConfigMaps, error)
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
|
||||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
|
||||||
Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error)
|
|
||||||
// Volumes returns a list of volumes to deploy together with the k8s cloud-controller-manager.
|
|
||||||
// Reference: https://kubernetes.io/docs/concepts/storage/volumes/ .
|
|
||||||
Volumes() []k8s.Volume
|
|
||||||
// VolumeMounts a list of of volume mounts to deploy together with the k8s cloud-controller-manager.
|
|
||||||
VolumeMounts() []k8s.VolumeMount
|
|
||||||
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cloud-controller-manager.
|
|
||||||
Env() []k8s.EnvVar
|
|
||||||
// PrepareInstance is called on every instance before deploying the cloud-controller-manager.
|
|
||||||
// Allows for cloud-provider specific hooks.
|
|
||||||
PrepareInstance(instance Instance, vpnIP string) error
|
|
||||||
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
|
||||||
Supported() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloudNodeManager implementers provide configuration for the k8s cloud-node-manager.
|
|
||||||
type CloudNodeManager interface {
|
|
||||||
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
|
|
||||||
Image() string
|
|
||||||
// Path returns the path used by cloud-node-manager executable within the container image.
|
|
||||||
Path() string
|
|
||||||
// ExtraArgs returns a list of arguments to append to the cloud-node-manager command.
|
|
||||||
ExtraArgs() []string
|
|
||||||
// Supported is used to determine if cloud node manager is implemented for this cloud provider.
|
|
||||||
Supported() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClusterAutoscaler implementers provide configuration for the k8s cluster-autoscaler.
|
|
||||||
type ClusterAutoscaler interface {
|
|
||||||
// Name returns the cloud-provider name as used by k8s cluster-autoscaler.
|
|
||||||
Name() string
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
|
||||||
Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error)
|
|
||||||
// Volumes returns a list of volumes to deploy together with the k8s cluster-autoscaler.
|
|
||||||
Volumes() []k8s.Volume
|
|
||||||
// VolumeMounts returns a list of volume mounts to deploy together with the k8s cluster-autoscaler.
|
|
||||||
VolumeMounts() []k8s.VolumeMount
|
|
||||||
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cluster-autoscaler.
|
|
||||||
Env() []k8s.EnvVar
|
|
||||||
// Supported is used to determine if cluster autoscaler is implemented for this cloud provider.
|
|
||||||
Supported() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoordinatorEndpoints retrieves a list of constellation coordinator endpoint candidates from the cloud provider API.
|
|
||||||
func CoordinatorEndpoints(ctx context.Context, metadata ProviderMetadata) ([]string, error) {
|
|
||||||
if !metadata.Supported() {
|
|
||||||
return nil, errors.New("retrieving instances list from cloud provider is not yet supported")
|
|
||||||
}
|
|
||||||
instances, err := metadata.List(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("retrieving instances list from cloud provider failed: %w", err)
|
|
||||||
}
|
|
||||||
coordinatorEndpoints := []string{}
|
|
||||||
for _, instance := range instances {
|
|
||||||
// check if role of instance is "Coordinator"
|
|
||||||
if instance.Role == role.Coordinator {
|
|
||||||
for _, ip := range instance.IPs {
|
|
||||||
coordinatorEndpoints = append(coordinatorEndpoints, net.JoinHostPort(ip, coordinatorPort))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coordinatorEndpoints, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareInstanceForCCM sets the vpn IP in cloud provider metadata.
|
|
||||||
func PrepareInstanceForCCM(ctx context.Context, metadata ProviderMetadata, cloudControllerManager CloudControllerManager, vpnIP string) error {
|
|
||||||
if err := metadata.SetVPNIP(ctx, vpnIP); err != nil {
|
|
||||||
return fmt.Errorf("setting VPN IP for cloud-controller-manager failed: %w", err)
|
|
||||||
}
|
|
||||||
instance, err := metadata.Self(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("retrieving instance metadata for cloud-controller-manager failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cloudControllerManager.PrepareInstance(instance, vpnIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProviderMetadataFake struct{}
|
type ProviderMetadataFake struct{}
|
||||||
|
|
||||||
func (f *ProviderMetadataFake) List(ctx context.Context) ([]Instance, error) {
|
func (f *ProviderMetadataFake) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
self, err := f.Self(ctx)
|
self, err := f.Self(ctx)
|
||||||
return []Instance{self}, err
|
return []cloudtypes.Instance{self}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ProviderMetadataFake) Self(ctx context.Context) (Instance, error) {
|
func (f *ProviderMetadataFake) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
return Instance{
|
return cloudtypes.Instance{
|
||||||
Name: "instanceName",
|
Name: "instanceName",
|
||||||
ProviderID: "fake://instance-id",
|
ProviderID: "fake://instance-id",
|
||||||
Role: role.Unknown,
|
Role: role.Unknown,
|
||||||
IPs: []string{"192.0.2.1"},
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ProviderMetadataFake) GetInstance(ctx context.Context, providerID string) (Instance, error) {
|
|
||||||
return Instance{
|
|
||||||
Name: "instanceName",
|
|
||||||
ProviderID: providerID,
|
|
||||||
Role: role.Unknown,
|
|
||||||
IPs: []string{"192.0.2.1"},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,96 +61,24 @@ func (f *ProviderMetadataFake) Supported() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type CloudControllerManagerFake struct{}
|
// CoordinatorEndpoints retrieves a list of constellation coordinator endpoint candidates from the cloud provider API.
|
||||||
|
func CoordinatorEndpoints(ctx context.Context, metadata ProviderMetadata) ([]string, error) {
|
||||||
|
if !metadata.Supported() {
|
||||||
|
return nil, errors.New("retrieving instances list from cloud provider is not yet supported")
|
||||||
|
}
|
||||||
|
instances, err := metadata.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("retrieving instances list from cloud provider failed: %w", err)
|
||||||
|
}
|
||||||
|
coordinatorEndpoints := []string{}
|
||||||
|
for _, instance := range instances {
|
||||||
|
// check if role of instance is "Coordinator"
|
||||||
|
if instance.Role == role.Coordinator {
|
||||||
|
for _, ip := range instance.PrivateIPs {
|
||||||
|
coordinatorEndpoints = append(coordinatorEndpoints, net.JoinHostPort(ip, coordinatorPort))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Image() string {
|
return coordinatorEndpoints, nil
|
||||||
return "fake-image:latest"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Path() string {
|
|
||||||
return "/fake-controller-manager"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Name() string {
|
|
||||||
return "fake"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) ExtraArgs() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) ConfigMaps(instance Instance) (resources.ConfigMaps, error) {
|
|
||||||
return []*k8s.ConfigMap{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
|
||||||
return []*k8s.Secret{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Volumes() []k8s.Volume {
|
|
||||||
return []k8s.Volume{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) VolumeMounts() []k8s.VolumeMount {
|
|
||||||
return []k8s.VolumeMount{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Env() []k8s.EnvVar {
|
|
||||||
return []k8s.EnvVar{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) PrepareInstance(instance Instance, vpnIP string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudControllerManagerFake) Supported() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type CloudNodeManagerFake struct{}
|
|
||||||
|
|
||||||
func (f *CloudNodeManagerFake) Image() string {
|
|
||||||
return "fake-image:latest"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudNodeManagerFake) Path() string {
|
|
||||||
return "/fake-cloud-node-manager"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudNodeManagerFake) ExtraArgs() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *CloudNodeManagerFake) Supported() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClusterAutoscalerFake struct{}
|
|
||||||
|
|
||||||
func (f *ClusterAutoscalerFake) Name() string {
|
|
||||||
return "fake"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
|
||||||
func (f *ClusterAutoscalerFake) Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
|
||||||
return resources.Secrets{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volumes returns a list of volumes to deploy together with the k8s cluster-autoscaler.
|
|
||||||
func (f *ClusterAutoscalerFake) Volumes() []k8s.Volume {
|
|
||||||
return []k8s.Volume{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeMounts returns a list of volume mounts to deploy together with the k8s cluster-autoscaler.
|
|
||||||
func (f *ClusterAutoscalerFake) VolumeMounts() []k8s.VolumeMount {
|
|
||||||
return []k8s.VolumeMount{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cluster-autoscaler.
|
|
||||||
func (f *ClusterAutoscalerFake) Env() []k8s.EnvVar {
|
|
||||||
return []k8s.EnvVar{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ClusterAutoscalerFake) Supported() bool {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -20,18 +21,18 @@ func TestCoordinatorEndpoints(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"getting coordinator endpoints works and role is checked": {
|
"getting coordinator endpoints works and role is checked": {
|
||||||
metadata: stubMetadata{
|
metadata: stubMetadata{
|
||||||
listRes: []Instance{
|
listRes: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
Name: "someInstanceA",
|
Name: "someInstanceA",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
ProviderID: "provider://somePath/someInstanceA",
|
ProviderID: "provider://somePath/someInstanceA",
|
||||||
IPs: []string{"192.0.2.1"},
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "someInstanceB",
|
Name: "someInstanceB",
|
||||||
Role: role.Node,
|
Role: role.Node,
|
||||||
ProviderID: "provider://somePath/someInstanceB",
|
ProviderID: "provider://somePath/someInstanceB",
|
||||||
IPs: []string{"192.0.2.2"},
|
PrivateIPs: []string{"192.0.2.2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
supportedRes: true,
|
supportedRes: true,
|
||||||
@ -70,64 +71,27 @@ func TestCoordinatorEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrepareInstanceForCCM(t *testing.T) {
|
|
||||||
err := errors.New("some err")
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
metadata stubMetadata
|
|
||||||
vpnIP string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"updating role works": {
|
|
||||||
metadata: stubMetadata{},
|
|
||||||
vpnIP: "192.0.2.1",
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"setting VPN IP fails": {
|
|
||||||
metadata: stubMetadata{
|
|
||||||
setVPNIPErr: err,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
err := PrepareInstanceForCCM(context.Background(), &tc.metadata, &CloudControllerManagerFake{}, tc.vpnIP)
|
|
||||||
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubMetadata struct {
|
type stubMetadata struct {
|
||||||
listRes []Instance
|
listRes []cloudtypes.Instance
|
||||||
listErr error
|
listErr error
|
||||||
selfRes Instance
|
selfRes cloudtypes.Instance
|
||||||
selfErr error
|
selfErr error
|
||||||
getInstanceRes Instance
|
getInstanceRes cloudtypes.Instance
|
||||||
getInstanceErr error
|
getInstanceErr error
|
||||||
signalRoleErr error
|
signalRoleErr error
|
||||||
setVPNIPErr error
|
setVPNIPErr error
|
||||||
supportedRes bool
|
supportedRes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) List(ctx context.Context) ([]Instance, error) {
|
func (m *stubMetadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
return m.listRes, m.listErr
|
return m.listRes, m.listErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) Self(ctx context.Context) (Instance, error) {
|
func (m *stubMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
return m.selfRes, m.selfErr
|
return m.selfRes, m.selfErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (Instance, error) {
|
func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
return m.getInstanceRes, m.getInstanceErr
|
return m.getInstanceRes, m.getInstanceErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,3 +106,11 @@ func (m *stubMetadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
|||||||
func (m *stubMetadata) Supported() bool {
|
func (m *stubMetadata) Supported() bool {
|
||||||
return m.supportedRes
|
return m.supportedRes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *stubMetadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubMetadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
@ -2,11 +2,8 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes"
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/edgelesssys/constellation/internal/constants"
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@ -14,93 +11,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetK8sJoinArgs returns the args needed by a Node to join the cluster.
|
// GetK8sJoinArgs returns the args needed by a Node to join the cluster.
|
||||||
func (c *Core) GetK8sJoinArgs() (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (c *Core) GetK8sJoinArgs(ctx context.Context) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return c.kube.GetJoinToken(constants.KubernetesJoinTokenTTL)
|
return c.kube.GetJoinToken(ctx, constants.KubernetesJoinTokenTTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetK8SCertificateKey returns the key needed by a Coordinator to join the cluster.
|
// GetK8SCertificateKey returns the key needed by a Coordinator to join the cluster.
|
||||||
func (c *Core) GetK8SCertificateKey() (string, error) {
|
func (c *Core) GetK8SCertificateKey(ctx context.Context) (string, error) {
|
||||||
return c.kube.GetKubeadmCertificateKey()
|
return c.kube.GetKubeadmCertificateKey(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitCluster initializes the cluster, stores the join args, and returns the kubeconfig.
|
// InitCluster initializes the cluster, stores the join args, and returns the kubeconfig.
|
||||||
func (c *Core) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error) {
|
func (c *Core) InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error) {
|
||||||
var nodeName string
|
|
||||||
var providerID string
|
|
||||||
var instance Instance
|
|
||||||
var ccmConfigMaps resources.ConfigMaps
|
|
||||||
var ccmSecrets resources.Secrets
|
|
||||||
var caSecrets resources.Secrets
|
|
||||||
var err error
|
|
||||||
nodeIP := coordinatorVPNIP.String()
|
|
||||||
if c.metadata.Supported() {
|
|
||||||
instance, err = c.metadata.Self(context.TODO())
|
|
||||||
if err != nil {
|
|
||||||
c.zaplogger.Error("Retrieving own instance metadata failed", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nodeName = instance.Name
|
|
||||||
providerID = instance.ProviderID
|
|
||||||
if len(instance.IPs) > 0 {
|
|
||||||
nodeIP = instance.IPs[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nodeName = coordinatorVPNIP.String()
|
|
||||||
}
|
|
||||||
if c.cloudControllerManager.Supported() && c.metadata.Supported() {
|
|
||||||
c.zaplogger.Info("Preparing node for cloud-controller-manager")
|
|
||||||
if err := PrepareInstanceForCCM(context.TODO(), c.metadata, c.cloudControllerManager, coordinatorVPNIP.String()); err != nil {
|
|
||||||
c.zaplogger.Error("Preparing node for CCM failed", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ccmConfigMaps, err = c.cloudControllerManager.ConfigMaps(instance)
|
|
||||||
if err != nil {
|
|
||||||
c.zaplogger.Error("Defining ConfigMaps for CCM failed", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ccmSecrets, err = c.cloudControllerManager.Secrets(instance, cloudServiceAccountURI)
|
|
||||||
if err != nil {
|
|
||||||
c.zaplogger.Error("Defining Secrets for CCM failed", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.clusterAutoscaler.Supported() {
|
|
||||||
caSecrets, err = c.clusterAutoscaler.Secrets(instance, cloudServiceAccountURI)
|
|
||||||
if err != nil {
|
|
||||||
c.zaplogger.Error("Defining Secrets for cluster-autoscaler failed", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.zaplogger.Info("Initializing cluster")
|
c.zaplogger.Info("Initializing cluster")
|
||||||
if err := c.kube.InitCluster(kubernetes.InitClusterInput{
|
vpnIP, err := c.GetVPNIP()
|
||||||
APIServerAdvertiseIP: coordinatorVPNIP.String(),
|
if err != nil {
|
||||||
NodeIP: nodeIP,
|
c.zaplogger.Error("Retrieving vpn ip failed", zap.Error(err))
|
||||||
NodeName: k8sCompliantHostname(nodeName),
|
return nil, err
|
||||||
ProviderID: providerID,
|
}
|
||||||
SupportClusterAutoscaler: c.clusterAutoscaler.Supported(),
|
if err := c.kube.InitCluster(ctx, autoscalingNodeGroups, cloudServiceAccountURI, vpnIP, masterSecret); err != nil {
|
||||||
AutoscalingCloudprovider: c.clusterAutoscaler.Name(),
|
|
||||||
AutoscalingSecrets: caSecrets,
|
|
||||||
AutoscalingVolumes: c.clusterAutoscaler.Volumes(),
|
|
||||||
AutoscalingVolumeMounts: c.clusterAutoscaler.VolumeMounts(),
|
|
||||||
AutoscalingEnv: c.clusterAutoscaler.Env(),
|
|
||||||
AutoscalingNodeGroups: autoscalingNodeGroups,
|
|
||||||
SupportsCloudControllerManager: c.cloudControllerManager.Supported(),
|
|
||||||
CloudControllerManagerName: c.cloudControllerManager.Name(),
|
|
||||||
CloudControllerManagerImage: c.cloudControllerManager.Image(),
|
|
||||||
CloudControllerManagerPath: c.cloudControllerManager.Path(),
|
|
||||||
CloudControllerManagerExtraArgs: c.cloudControllerManager.ExtraArgs(),
|
|
||||||
CloudControllerManagerConfigMaps: ccmConfigMaps,
|
|
||||||
CloudControllerManagerSecrets: ccmSecrets,
|
|
||||||
CloudControllerManagerVolumes: c.cloudControllerManager.Volumes(),
|
|
||||||
CloudControllerManagerVolumeMounts: c.cloudControllerManager.VolumeMounts(),
|
|
||||||
CloudControllerManagerEnv: c.cloudControllerManager.Env(),
|
|
||||||
SupportsCloudNodeManager: c.cloudNodeManager.Supported(),
|
|
||||||
CloudNodeManagerImage: c.cloudNodeManager.Image(),
|
|
||||||
CloudNodeManagerPath: c.cloudNodeManager.Path(),
|
|
||||||
CloudNodeManagerExtraArgs: c.cloudNodeManager.ExtraArgs(),
|
|
||||||
MasterSecret: masterSecret,
|
|
||||||
}); err != nil {
|
|
||||||
c.zaplogger.Error("Initializing cluster failed", zap.Error(err))
|
c.zaplogger.Error("Initializing cluster failed", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -125,41 +53,16 @@ func (c *Core) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountUR
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JoinCluster lets a Node join the cluster.
|
// JoinCluster lets a Node join the cluster.
|
||||||
func (c *Core) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, certKey string, peerRole role.Role) error {
|
func (c *Core) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, certKey string, peerRole role.Role) error {
|
||||||
c.zaplogger.Info("Joining Kubernetes cluster")
|
c.zaplogger.Info("Joining Kubernetes cluster")
|
||||||
nodeVPNIP, err := c.vpn.GetInterfaceIP()
|
nodeVPNIP, err := c.vpn.GetInterfaceIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.zaplogger.Error("Retrieving vpn ip failed", zap.Error(err))
|
c.zaplogger.Error("Retrieving vpn ip failed", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var nodeName string
|
|
||||||
var providerID string
|
|
||||||
nodeIP := nodeVPNIP
|
|
||||||
if c.metadata.Supported() {
|
|
||||||
instance, err := c.metadata.Self(context.TODO())
|
|
||||||
if err != nil {
|
|
||||||
c.zaplogger.Error("Retrieving own instance metadata failed", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
providerID = instance.ProviderID
|
|
||||||
nodeName = instance.Name
|
|
||||||
if len(instance.IPs) > 0 {
|
|
||||||
nodeIP = instance.IPs[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nodeName = nodeVPNIP
|
|
||||||
}
|
|
||||||
if c.cloudControllerManager.Supported() && c.metadata.Supported() {
|
|
||||||
c.zaplogger.Info("Preparing node for cloud-controller-manager")
|
|
||||||
if err := PrepareInstanceForCCM(context.TODO(), c.metadata, c.cloudControllerManager, nodeVPNIP); err != nil {
|
|
||||||
c.zaplogger.Error("Preparing node for CCM failed", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.zaplogger.Info("k8s Join data", zap.String("nodename", nodeName), zap.String("nodeIP", nodeIP), zap.String("nodeVPNIP", nodeVPNIP), zap.String("provid", providerID))
|
|
||||||
// we need to pass the VPNIP for another control-plane, otherwise etcd will bind itself to the wrong IP address and fails
|
// we need to pass the VPNIP for another control-plane, otherwise etcd will bind itself to the wrong IP address and fails
|
||||||
if err := c.kube.JoinCluster(args, k8sCompliantHostname(nodeName), nodeIP, nodeVPNIP, providerID, certKey, c.cloudControllerManager.Supported(), peerRole); err != nil {
|
if err := c.kube.JoinCluster(ctx, args, nodeVPNIP, certKey, peerRole); err != nil {
|
||||||
c.zaplogger.Error("Joining Kubernetes cluster failed", zap.Error(err))
|
c.zaplogger.Error("Joining Kubernetes cluster failed", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -177,15 +80,15 @@ func (c *Core) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, certKey string
|
|||||||
// Cluster manages the overall cluster lifecycle (init, join).
|
// Cluster manages the overall cluster lifecycle (init, join).
|
||||||
type Cluster interface {
|
type Cluster interface {
|
||||||
// InitCluster bootstraps a new cluster with the current node being the master, returning the arguments required to join the cluster.
|
// InitCluster bootstraps a new cluster with the current node being the master, returning the arguments required to join the cluster.
|
||||||
InitCluster(kubernetes.InitClusterInput) error
|
InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, vpnIP string, masterSecret []byte) error
|
||||||
// JoinCluster will join the current node to an existing cluster.
|
// JoinCluster will join the current node to an existing cluster.
|
||||||
JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, ccmSupported bool, peerRole role.Role) error
|
JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, nodeVPNIP, certKey string, peerRole role.Role) error
|
||||||
// GetKubeconfig reads the kubeconfig from the filesystem. Only succeeds after cluster is initialized.
|
// GetKubeconfig reads the kubeconfig from the filesystem. Only succeeds after cluster is initialized.
|
||||||
GetKubeconfig() ([]byte, error)
|
GetKubeconfig() ([]byte, error)
|
||||||
// GetKubeadmCertificateKey returns the 64-byte hex string key needed to join the cluster as control-plane. This function must be executed on a control-plane.
|
// GetKubeadmCertificateKey returns the 64-byte hex string key needed to join the cluster as control-plane. This function must be executed on a control-plane.
|
||||||
GetKubeadmCertificateKey() (string, error)
|
GetKubeadmCertificateKey(ctx context.Context) (string, error)
|
||||||
// GetJoinToken returns a bootstrap (join) token.
|
// GetJoinToken returns a bootstrap (join) token.
|
||||||
GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
|
GetJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
|
||||||
// StartKubelet starts the kubelet service.
|
// StartKubelet starts the kubelet service.
|
||||||
StartKubelet() error
|
StartKubelet() error
|
||||||
}
|
}
|
||||||
@ -194,12 +97,12 @@ type Cluster interface {
|
|||||||
type ClusterFake struct{}
|
type ClusterFake struct{}
|
||||||
|
|
||||||
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
|
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
|
||||||
func (c *ClusterFake) InitCluster(kubernetes.InitClusterInput) error {
|
func (c *ClusterFake) InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, vpnIP string, masterSecret []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinCluster will fake joining the current node to an existing cluster.
|
// JoinCluster will fake joining the current node to an existing cluster.
|
||||||
func (c *ClusterFake) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, _ bool, _ role.Role) error {
|
func (c *ClusterFake) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, nodeVPNIP, certKey string, peerRole role.Role) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,12 +112,12 @@ func (c *ClusterFake) GetKubeconfig() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetKubeadmCertificateKey fakes generating a certificateKey.
|
// GetKubeadmCertificateKey fakes generating a certificateKey.
|
||||||
func (c *ClusterFake) GetKubeadmCertificateKey() (string, error) {
|
func (c *ClusterFake) GetKubeadmCertificateKey(context.Context) (string, error) {
|
||||||
return "controlPlaneCertficateKey", nil
|
return "controlPlaneCertficateKey", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetJoinToken returns a bootstrap (join) token.
|
// GetJoinToken returns a bootstrap (join) token.
|
||||||
func (c *ClusterFake) GetJoinToken(_ time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (c *ClusterFake) GetJoinToken(ctx context.Context, _ time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return &kubeadm.BootstrapTokenDiscovery{
|
return &kubeadm.BootstrapTokenDiscovery{
|
||||||
APIServerEndpoint: "0.0.0.0",
|
APIServerEndpoint: "0.0.0.0",
|
||||||
Token: "kube-fake-token",
|
Token: "kube-fake-token",
|
||||||
@ -226,12 +129,3 @@ func (c *ClusterFake) GetJoinToken(_ time.Duration) (*kubeadm.BootstrapTokenDisc
|
|||||||
func (c *ClusterFake) StartKubelet() error {
|
func (c *ClusterFake) StartKubelet() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// k8sCompliantHostname transforms a hostname to an RFC 1123 compliant, lowercase subdomain as required by Kubernetes node names.
|
|
||||||
// The following regex is used by k8s for validation: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/ .
|
|
||||||
// Only a simple heuristic is used for now (to lowercase, replace underscores).
|
|
||||||
func k8sCompliantHostname(in string) string {
|
|
||||||
hostname := strings.ToLower(in)
|
|
||||||
hostname = strings.ReplaceAll(hostname, "_", "-")
|
|
||||||
return hostname
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
|
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes"
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
"github.com/edgelesssys/constellation/internal/deploy/user"
|
"github.com/edgelesssys/constellation/internal/deploy/user"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
@ -16,165 +14,65 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
k8s "k8s.io/api/core/v1"
|
|
||||||
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInitCluster(t *testing.T) {
|
func TestInitCluster(t *testing.T) {
|
||||||
someErr := errors.New("someErr")
|
someErr := errors.New("someErr")
|
||||||
|
kubeconfigContent := []byte("kubeconfig")
|
||||||
|
|
||||||
testMS := []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
|
testMS := []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
cluster clusterStub
|
cluster Cluster
|
||||||
metadata stubMetadata
|
vpn VPN
|
||||||
cloudControllerManager stubCloudControllerManager
|
metadata ProviderMetadata
|
||||||
cloudNodeManager stubCloudNodeManager
|
masterSecret []byte
|
||||||
clusterAutoscaler stubClusterAutoscaler
|
autoscalingNodeGroups []string
|
||||||
masterSecret []byte
|
wantErr bool
|
||||||
autoscalingNodeGroups []string
|
|
||||||
wantErr bool
|
|
||||||
wantInitClusterInput kubernetes.InitClusterInput
|
|
||||||
}{
|
}{
|
||||||
"InitCluster works": {
|
"InitCluster works": {
|
||||||
cluster: clusterStub{
|
cluster: &clusterStub{
|
||||||
kubeconfig: []byte("kubeconfig"),
|
kubeconfig: kubeconfigContent,
|
||||||
},
|
},
|
||||||
|
vpn: &stubVPN{interfaceIP: "192.0.2.1"},
|
||||||
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
|
autoscalingNodeGroups: []string{"someNodeGroup"},
|
||||||
|
},
|
||||||
|
"InitCluster works even if signal role fails": {
|
||||||
|
cluster: &clusterStub{
|
||||||
|
kubeconfig: kubeconfigContent,
|
||||||
|
},
|
||||||
|
vpn: &stubVPN{interfaceIP: "192.0.2.1"},
|
||||||
|
metadata: &stubMetadata{supportedRes: true, signalRoleErr: someErr},
|
||||||
autoscalingNodeGroups: []string{"someNodeGroup"},
|
autoscalingNodeGroups: []string{"someNodeGroup"},
|
||||||
masterSecret: testMS,
|
masterSecret: testMS,
|
||||||
wantInitClusterInput: kubernetes.InitClusterInput{
|
|
||||||
APIServerAdvertiseIP: "10.118.0.1",
|
|
||||||
NodeIP: "10.118.0.1",
|
|
||||||
NodeName: "10.118.0.1",
|
|
||||||
SupportsCloudControllerManager: false,
|
|
||||||
SupportClusterAutoscaler: false,
|
|
||||||
AutoscalingNodeGroups: []string{"someNodeGroup"},
|
|
||||||
MasterSecret: testMS,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
},
|
||||||
"Instance metadata is retrieved": {
|
"cannot get VPN IP": {
|
||||||
cluster: clusterStub{
|
cluster: &clusterStub{
|
||||||
kubeconfig: []byte("kubeconfig"),
|
kubeconfig: kubeconfigContent,
|
||||||
},
|
},
|
||||||
masterSecret: testMS,
|
vpn: &stubVPN{getInterfaceIPErr: someErr},
|
||||||
metadata: stubMetadata{
|
|
||||||
selfRes: Instance{
|
|
||||||
Name: "some-name",
|
|
||||||
ProviderID: "fake://providerid",
|
|
||||||
},
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
wantInitClusterInput: kubernetes.InitClusterInput{
|
|
||||||
APIServerAdvertiseIP: "10.118.0.1",
|
|
||||||
NodeIP: "10.118.0.1",
|
|
||||||
NodeName: "some-name",
|
|
||||||
ProviderID: "fake://providerid",
|
|
||||||
SupportsCloudControllerManager: false,
|
|
||||||
SupportClusterAutoscaler: false,
|
|
||||||
MasterSecret: testMS,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"metadata of self retrieval error is checked": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
kubeconfig: []byte("kubeconfig"),
|
|
||||||
},
|
|
||||||
metadata: stubMetadata{
|
|
||||||
supportedRes: true,
|
|
||||||
selfErr: errors.New("metadata retrieval error"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"Autoscaler is prepared when supported": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
kubeconfig: []byte("kubeconfig"),
|
|
||||||
},
|
|
||||||
clusterAutoscaler: stubClusterAutoscaler{
|
|
||||||
nameRes: "some-name",
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
masterSecret: testMS,
|
|
||||||
autoscalingNodeGroups: []string{"someNodeGroup"},
|
autoscalingNodeGroups: []string{"someNodeGroup"},
|
||||||
wantInitClusterInput: kubernetes.InitClusterInput{
|
wantErr: true,
|
||||||
APIServerAdvertiseIP: "10.118.0.1",
|
|
||||||
NodeIP: "10.118.0.1",
|
|
||||||
NodeName: "10.118.0.1",
|
|
||||||
SupportsCloudControllerManager: false,
|
|
||||||
SupportClusterAutoscaler: true,
|
|
||||||
AutoscalingCloudprovider: "some-name",
|
|
||||||
AutoscalingNodeGroups: []string{"someNodeGroup"},
|
|
||||||
MasterSecret: testMS,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
},
|
||||||
"Node is prepared for CCM if supported": {
|
"cannot init kubernetes": {
|
||||||
cluster: clusterStub{
|
cluster: &clusterStub{
|
||||||
kubeconfig: []byte("kubeconfig"),
|
|
||||||
},
|
|
||||||
masterSecret: testMS,
|
|
||||||
cloudControllerManager: stubCloudControllerManager{
|
|
||||||
supportedRes: true,
|
|
||||||
nameRes: "some-name",
|
|
||||||
imageRes: "someImage",
|
|
||||||
pathRes: "/some/path",
|
|
||||||
},
|
|
||||||
wantInitClusterInput: kubernetes.InitClusterInput{
|
|
||||||
APIServerAdvertiseIP: "10.118.0.1",
|
|
||||||
NodeIP: "10.118.0.1",
|
|
||||||
NodeName: "10.118.0.1",
|
|
||||||
SupportsCloudControllerManager: true,
|
|
||||||
SupportClusterAutoscaler: false,
|
|
||||||
CloudControllerManagerName: "some-name",
|
|
||||||
CloudControllerManagerImage: "someImage",
|
|
||||||
CloudControllerManagerPath: "/some/path",
|
|
||||||
MasterSecret: testMS,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"Node preparation for CCM can fail": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
kubeconfig: []byte("kubeconfig"),
|
|
||||||
},
|
|
||||||
metadata: stubMetadata{
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
cloudControllerManager: stubCloudControllerManager{
|
|
||||||
supportedRes: true,
|
|
||||||
nameRes: "some-name",
|
|
||||||
imageRes: "someImage",
|
|
||||||
pathRes: "/some/path",
|
|
||||||
prepareInstanceRes: errors.New("preparing node for CCM failed"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"updating role fails without error": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
kubeconfig: []byte("kubeconfig"),
|
|
||||||
},
|
|
||||||
masterSecret: testMS,
|
|
||||||
metadata: stubMetadata{
|
|
||||||
signalRoleErr: errors.New("updating role fails"),
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantInitClusterInput: kubernetes.InitClusterInput{
|
|
||||||
APIServerAdvertiseIP: "10.118.0.1",
|
|
||||||
NodeIP: "10.118.0.1",
|
|
||||||
MasterSecret: testMS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"getting kubeconfig fail detected": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
getKubeconfigErr: errors.New("getting kubeconfig fails"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"InitCluster fail detected": {
|
|
||||||
cluster: clusterStub{
|
|
||||||
initErr: someErr,
|
initErr: someErr,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
vpn: &stubVPN{interfaceIP: "192.0.2.1"},
|
||||||
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
|
autoscalingNodeGroups: []string{"someNodeGroup"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"cannot get kubeconfig": {
|
||||||
|
cluster: &clusterStub{
|
||||||
|
getKubeconfigErr: someErr,
|
||||||
|
},
|
||||||
|
vpn: &stubVPN{interfaceIP: "192.0.2.1"},
|
||||||
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
|
autoscalingNodeGroups: []string{"someNodeGroup"},
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,19 +84,17 @@ func TestInitCluster(t *testing.T) {
|
|||||||
zapLogger, err := zap.NewDevelopment()
|
zapLogger, err := zap.NewDevelopment()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(tc.vpn, tc.cluster, tc.metadata, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
kubeconfig, err := core.InitCluster(tc.autoscalingNodeGroups, "cloud-service-account-uri", tc.masterSecret)
|
kubeconfig, err := core.InitCluster(context.Background(), tc.autoscalingNodeGroups, "cloud-service-account-uri", tc.masterSecret)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Len(tc.cluster.initInputs, 1)
|
assert.Equal(kubeconfigContent, kubeconfig)
|
||||||
assert.Equal(tc.wantInitClusterInput, tc.cluster.initInputs[0])
|
|
||||||
assert.Equal(tc.cluster.kubeconfig, kubeconfig)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,90 +103,38 @@ func TestJoinCluster(t *testing.T) {
|
|||||||
someErr := errors.New("someErr")
|
someErr := errors.New("someErr")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
cluster clusterStub
|
cluster Cluster
|
||||||
metadata stubMetadata
|
metadata ProviderMetadata
|
||||||
cloudControllerManager stubCloudControllerManager
|
vpn VPN
|
||||||
cloudNodeManager stubCloudNodeManager
|
wantErr bool
|
||||||
clusterAutoscaler stubClusterAutoscaler
|
|
||||||
vpn stubVPN
|
|
||||||
wantErr bool
|
|
||||||
wantJoinClusterArgs joinClusterArgs
|
|
||||||
}{
|
}{
|
||||||
"JoinCluster works": {
|
"JoinCluster works": {
|
||||||
vpn: stubVPN{
|
vpn: &stubVPN{
|
||||||
interfaceIP: "192.0.2.0",
|
interfaceIP: "192.0.2.0",
|
||||||
},
|
},
|
||||||
wantJoinClusterArgs: joinClusterArgs{
|
cluster: &clusterStub{},
|
||||||
args: &kubeadm.BootstrapTokenDiscovery{
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
APIServerEndpoint: "192.0.2.0:6443",
|
|
||||||
Token: "someToken",
|
|
||||||
CACertHashes: []string{"someHash"},
|
|
||||||
},
|
|
||||||
nodeName: "192.0.2.0",
|
|
||||||
nodeIP: "192.0.2.0",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"JoinCluster fail detected": {
|
"JoinCluster works even if signal role fails": {
|
||||||
cluster: clusterStub{
|
vpn: &stubVPN{
|
||||||
joinErr: someErr,
|
interfaceIP: "192.0.2.0",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
cluster: &clusterStub{},
|
||||||
|
metadata: &stubMetadata{supportedRes: true, signalRoleErr: someErr},
|
||||||
},
|
},
|
||||||
"retrieving vpn ip failure detected": {
|
"cannot get VPN IP": {
|
||||||
vpn: stubVPN{
|
vpn: &stubVPN{getInterfaceIPErr: someErr},
|
||||||
getInterfaceIPErr: errors.New("retrieving interface ip error"),
|
cluster: &clusterStub{},
|
||||||
},
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"Instance metadata is retrieved": {
|
"joining kuberentes fails": {
|
||||||
metadata: stubMetadata{
|
vpn: &stubVPN{
|
||||||
selfRes: Instance{
|
interfaceIP: "192.0.2.0",
|
||||||
Name: "some-name",
|
|
||||||
ProviderID: "fake://providerid",
|
|
||||||
},
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
wantJoinClusterArgs: joinClusterArgs{
|
|
||||||
args: &kubeadm.BootstrapTokenDiscovery{
|
|
||||||
APIServerEndpoint: "192.0.2.0:6443",
|
|
||||||
Token: "someToken",
|
|
||||||
CACertHashes: []string{"someHash"},
|
|
||||||
},
|
|
||||||
nodeName: "some-name",
|
|
||||||
providerID: "fake://providerid",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"Instance metadata retrieval can fail": {
|
|
||||||
metadata: stubMetadata{
|
|
||||||
supportedRes: true,
|
|
||||||
selfErr: errors.New("metadata retrieval error"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"CCM preparation failure is detected": {
|
|
||||||
metadata: stubMetadata{
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
cloudControllerManager: stubCloudControllerManager{
|
|
||||||
supportedRes: true,
|
|
||||||
prepareInstanceRes: errors.New("ccm prepare fails"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"updating role fails without error": {
|
|
||||||
metadata: stubMetadata{
|
|
||||||
signalRoleErr: errors.New("updating role fails"),
|
|
||||||
supportedRes: true,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantJoinClusterArgs: joinClusterArgs{
|
|
||||||
args: &kubeadm.BootstrapTokenDiscovery{
|
|
||||||
APIServerEndpoint: "192.0.2.0:6443",
|
|
||||||
Token: "someToken",
|
|
||||||
CACertHashes: []string{"someHash"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
cluster: &clusterStub{joinErr: someErr},
|
||||||
|
metadata: &stubMetadata{supportedRes: true},
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +146,7 @@ func TestJoinCluster(t *testing.T) {
|
|||||||
zapLogger, err := zap.NewDevelopment()
|
zapLogger, err := zap.NewDevelopment()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&tc.vpn, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(tc.vpn, tc.cluster, tc.metadata, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
joinReq := &kubeadm.BootstrapTokenDiscovery{
|
joinReq := &kubeadm.BootstrapTokenDiscovery{
|
||||||
@ -310,47 +154,13 @@ func TestJoinCluster(t *testing.T) {
|
|||||||
Token: "someToken",
|
Token: "someToken",
|
||||||
CACertHashes: []string{"someHash"},
|
CACertHashes: []string{"someHash"},
|
||||||
}
|
}
|
||||||
err = core.JoinCluster(joinReq, "", role.Node)
|
err = core.JoinCluster(context.Background(), joinReq, "", role.Node)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Len(tc.cluster.joinClusterArgs, 1)
|
|
||||||
assert.Equal(tc.wantJoinClusterArgs, tc.cluster.joinClusterArgs[0])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestK8sCompliantHostname(t *testing.T) {
|
|
||||||
compliantHostname := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
|
||||||
testCases := map[string]struct {
|
|
||||||
hostname string
|
|
||||||
wantHostname string
|
|
||||||
}{
|
|
||||||
"azure scale set names work": {
|
|
||||||
hostname: "constellation-scale-set-coordinators-name_0",
|
|
||||||
wantHostname: "constellation-scale-set-coordinators-name-0",
|
|
||||||
},
|
|
||||||
"compliant hostname is not modified": {
|
|
||||||
hostname: "abcd-123",
|
|
||||||
wantHostname: "abcd-123",
|
|
||||||
},
|
|
||||||
"uppercase hostnames are lowercased": {
|
|
||||||
hostname: "ABCD",
|
|
||||||
wantHostname: "abcd",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
hostname := k8sCompliantHostname(tc.hostname)
|
|
||||||
|
|
||||||
assert.Equal(tc.wantHostname, hostname)
|
|
||||||
assert.Regexp(compliantHostname, hostname)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,24 +174,20 @@ type clusterStub struct {
|
|||||||
getJoinTokenErr error
|
getJoinTokenErr error
|
||||||
startKubeletErr error
|
startKubeletErr error
|
||||||
|
|
||||||
initInputs []kubernetes.InitClusterInput
|
inAutoscalingNodeGroups []string
|
||||||
joinClusterArgs []joinClusterArgs
|
inCloudServiceAccountURI string
|
||||||
|
inVpnIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterStub) InitCluster(in kubernetes.InitClusterInput) error {
|
func (c *clusterStub) InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI string, vpnIP string, masterSecret []byte) error {
|
||||||
c.initInputs = append(c.initInputs, in)
|
c.inAutoscalingNodeGroups = autoscalingNodeGroups
|
||||||
|
c.inCloudServiceAccountURI = cloudServiceAccountURI
|
||||||
|
c.inVpnIP = vpnIP
|
||||||
|
|
||||||
return c.initErr
|
return c.initErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterStub) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, _ bool, _ role.Role) error {
|
func (c *clusterStub) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, nodeVPNIP string, certKey string, peerRole role.Role) error {
|
||||||
c.joinClusterArgs = append(c.joinClusterArgs, joinClusterArgs{
|
|
||||||
args: args,
|
|
||||||
nodeName: nodeName,
|
|
||||||
nodeIP: nodeIP,
|
|
||||||
providerID: providerID,
|
|
||||||
})
|
|
||||||
|
|
||||||
return c.joinErr
|
return c.joinErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,149 +195,14 @@ func (c *clusterStub) GetKubeconfig() ([]byte, error) {
|
|||||||
return c.kubeconfig, c.getKubeconfigErr
|
return c.kubeconfig, c.getKubeconfigErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterStub) GetKubeadmCertificateKey() (string, error) {
|
func (c *clusterStub) GetKubeadmCertificateKey(context.Context) (string, error) {
|
||||||
return "dummy", nil
|
return "dummy", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterStub) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (c *clusterStub) GetJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return c.getJoinTokenResponse, c.getJoinTokenErr
|
return c.getJoinTokenResponse, c.getJoinTokenErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterStub) StartKubelet() error {
|
func (c *clusterStub) StartKubelet() error {
|
||||||
return c.startKubeletErr
|
return c.startKubeletErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type prepareInstanceRequest struct {
|
|
||||||
instance Instance
|
|
||||||
vpnIP string
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubCloudControllerManager struct {
|
|
||||||
imageRes string
|
|
||||||
pathRes string
|
|
||||||
nameRes string
|
|
||||||
prepareInstanceRes error
|
|
||||||
extraArgsRes []string
|
|
||||||
configMapsRes resources.ConfigMaps
|
|
||||||
configMapsErr error
|
|
||||||
secretsRes resources.Secrets
|
|
||||||
secretsErr error
|
|
||||||
volumesRes []k8s.Volume
|
|
||||||
volumeMountRes []k8s.VolumeMount
|
|
||||||
envRes []k8s.EnvVar
|
|
||||||
supportedRes bool
|
|
||||||
|
|
||||||
prepareInstanceRequests []prepareInstanceRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Image() string {
|
|
||||||
return s.imageRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Path() string {
|
|
||||||
return s.pathRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Name() string {
|
|
||||||
return s.nameRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) PrepareInstance(instance Instance, vpnIP string) error {
|
|
||||||
s.prepareInstanceRequests = append(s.prepareInstanceRequests, prepareInstanceRequest{
|
|
||||||
instance: instance,
|
|
||||||
vpnIP: vpnIP,
|
|
||||||
})
|
|
||||||
return s.prepareInstanceRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) ExtraArgs() []string {
|
|
||||||
return s.extraArgsRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) ConfigMaps(instance Instance) (resources.ConfigMaps, error) {
|
|
||||||
return s.configMapsRes, s.configMapsErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
|
||||||
return s.secretsRes, s.secretsErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Volumes() []k8s.Volume {
|
|
||||||
return s.volumesRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) VolumeMounts() []k8s.VolumeMount {
|
|
||||||
return s.volumeMountRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Env() []k8s.EnvVar {
|
|
||||||
return s.envRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudControllerManager) Supported() bool {
|
|
||||||
return s.supportedRes
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubCloudNodeManager struct {
|
|
||||||
imageRes string
|
|
||||||
pathRes string
|
|
||||||
extraArgsRes []string
|
|
||||||
supportedRes bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudNodeManager) Image() string {
|
|
||||||
return s.imageRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudNodeManager) Path() string {
|
|
||||||
return s.pathRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudNodeManager) ExtraArgs() []string {
|
|
||||||
return s.extraArgsRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubCloudNodeManager) Supported() bool {
|
|
||||||
return s.supportedRes
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubClusterAutoscaler struct {
|
|
||||||
nameRes string
|
|
||||||
supportedRes bool
|
|
||||||
secretsRes resources.Secrets
|
|
||||||
secretsErr error
|
|
||||||
volumesRes []k8s.Volume
|
|
||||||
volumeMountRes []k8s.VolumeMount
|
|
||||||
envRes []k8s.EnvVar
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) Name() string {
|
|
||||||
return s.nameRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) Secrets(instance Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
|
||||||
return s.secretsRes, s.secretsErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) Volumes() []k8s.Volume {
|
|
||||||
return s.volumesRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) VolumeMounts() []k8s.VolumeMount {
|
|
||||||
return s.volumeMountRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) Env() []k8s.EnvVar {
|
|
||||||
return s.envRes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubClusterAutoscaler) Supported() bool {
|
|
||||||
return s.supportedRes
|
|
||||||
}
|
|
||||||
|
|
||||||
type joinClusterArgs struct {
|
|
||||||
args *kubeadm.BootstrapTokenDiscovery
|
|
||||||
nodeName string
|
|
||||||
nodeIP string
|
|
||||||
providerID string
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -25,8 +24,6 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var coordinatorVPNIP = netip.AddrFrom4([4]byte{10, 118, 0, 1})
|
|
||||||
|
|
||||||
type Core struct {
|
type Core struct {
|
||||||
state state.State
|
state state.State
|
||||||
openTPM vtpm.TPMOpenFunc
|
openTPM vtpm.TPMOpenFunc
|
||||||
@ -35,9 +32,6 @@ type Core struct {
|
|||||||
vpn VPN
|
vpn VPN
|
||||||
kube Cluster
|
kube Cluster
|
||||||
metadata ProviderMetadata
|
metadata ProviderMetadata
|
||||||
cloudControllerManager CloudControllerManager
|
|
||||||
cloudNodeManager CloudNodeManager
|
|
||||||
clusterAutoscaler ClusterAutoscaler
|
|
||||||
encryptedDisk EncryptedDisk
|
encryptedDisk EncryptedDisk
|
||||||
kms kms.CloudKMS
|
kms kms.CloudKMS
|
||||||
zaplogger *zap.Logger
|
zaplogger *zap.Logger
|
||||||
@ -50,8 +44,7 @@ type Core struct {
|
|||||||
|
|
||||||
// NewCore creates and initializes a new Core object.
|
// NewCore creates and initializes a new Core object.
|
||||||
func NewCore(vpn VPN, kube Cluster,
|
func NewCore(vpn VPN, kube Cluster,
|
||||||
metadata ProviderMetadata, cloudControllerManager CloudControllerManager, cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler,
|
metadata ProviderMetadata, encryptedDisk EncryptedDisk, zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler, linuxUserManager user.LinuxUserManager,
|
||||||
encryptedDisk EncryptedDisk, zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler, linuxUserManager user.LinuxUserManager,
|
|
||||||
) (*Core, error) {
|
) (*Core, error) {
|
||||||
stor := store.NewStdStore()
|
stor := store.NewStdStore()
|
||||||
c := &Core{
|
c := &Core{
|
||||||
@ -60,9 +53,6 @@ func NewCore(vpn VPN, kube Cluster,
|
|||||||
vpn: vpn,
|
vpn: vpn,
|
||||||
kube: kube,
|
kube: kube,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
cloudNodeManager: cloudNodeManager,
|
|
||||||
cloudControllerManager: cloudControllerManager,
|
|
||||||
clusterAutoscaler: clusterAutoscaler,
|
|
||||||
encryptedDisk: encryptedDisk,
|
encryptedDisk: encryptedDisk,
|
||||||
zaplogger: zapLogger,
|
zaplogger: zapLogger,
|
||||||
kms: nil, // KMS is set up during init phase
|
kms: nil, // KMS is set up during init phase
|
||||||
|
@ -38,7 +38,7 @@ func TestGetNextNodeIP(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NoError(core.InitializeStoreIPs())
|
require.NoError(core.InitializeStoreIPs())
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ func TestSwitchToPersistentStore(t *testing.T) {
|
|||||||
|
|
||||||
storeFactory := &fakeStoreFactory{}
|
storeFactory := &fakeStoreFactory{}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(core.store.Put("test", []byte("test")))
|
require.NoError(core.store.Put("test", []byte("test")))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ func TestGetIDs(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
_, _, err = core.GetIDs(nil)
|
_, _, err = core.GetIDs(nil)
|
||||||
@ -122,7 +122,7 @@ func TestNotifyNodeHeartbeat(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
const ip = "192.0.2.1"
|
const ip = "192.0.2.1"
|
||||||
@ -136,7 +136,7 @@ func TestDeriveKey(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// error when no kms is set up
|
// error when no kms is set up
|
||||||
@ -214,7 +214,7 @@ func TestInitialize(t *testing.T) {
|
|||||||
VPNPrivKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
|
VPNPrivKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
|
||||||
}).ToFile(fileHandler))
|
}).ToFile(fileHandler))
|
||||||
}
|
}
|
||||||
core, err := NewCore(&stubVPN{}, &clusterStub{}, &ProviderMetadataFake{}, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, &fakeStoreFactory{}, fileHandler, user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, &clusterStub{}, &ProviderMetadataFake{}, nil, zaptest.NewLogger(t), openTPM, &fakeStoreFactory{}, fileHandler, user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
core.initialVPNPeersRetriever = fakeInitializeVPNPeersRetriever
|
core.initialVPNPeersRetriever = fakeInitializeVPNPeersRetriever
|
||||||
// prepare store to emulate initialized KMS
|
// prepare store to emulate initialized KMS
|
||||||
@ -272,7 +272,7 @@ func TestPersistNodeState(t *testing.T) {
|
|||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NoError(file.Close())
|
require.NoError(file.Close())
|
||||||
}
|
}
|
||||||
core, err := NewCore(tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler, user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(tc.vpn, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler, user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
err = core.PersistNodeState(role.Coordinator, "192.0.2.1", []byte("owner-id"), []byte("cluster-id"))
|
err = core.PersistNodeState(role.Coordinator, "192.0.2.1", []byte("owner-id"), []byte("cluster-id"))
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
|
@ -45,7 +45,7 @@ func TestGetDiskUUID(t *testing.T) {
|
|||||||
uuid: tc.wantUUID,
|
uuid: tc.wantUUID,
|
||||||
}
|
}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
uuid, err := core.GetDiskUUID()
|
uuid, err := core.GetDiskUUID()
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
@ -88,7 +88,7 @@ func TestUpdateDiskPassphrase(t *testing.T) {
|
|||||||
updatePassphraseErr: tc.updatePassphraseErr,
|
updatePassphraseErr: tc.updatePassphraseErr,
|
||||||
}
|
}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
err = core.UpdateDiskPassphrase("passphrase")
|
err = core.UpdateDiskPassphrase("passphrase")
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
|
@ -124,16 +124,13 @@ func newMockCoreWithDialer(bufDialer *bufconnDialer) (*Core, *pubapi.API, error)
|
|||||||
vpn := &stubVPN{}
|
vpn := &stubVPN{}
|
||||||
kubeFake := &ClusterFake{}
|
kubeFake := &ClusterFake{}
|
||||||
metadataFake := &ProviderMetadataFake{}
|
metadataFake := &ProviderMetadataFake{}
|
||||||
ccmFake := &CloudControllerManagerFake{}
|
|
||||||
cnmFake := &CloudNodeManagerFake{}
|
|
||||||
autoscalerFake := &ClusterAutoscalerFake{}
|
|
||||||
encryptedDiskFake := &EncryptedDiskFake{}
|
encryptedDiskFake := &EncryptedDiskFake{}
|
||||||
|
|
||||||
getPublicAddr := func() (string, error) {
|
getPublicAddr := func() (string, error) {
|
||||||
return "192.0.2.1", nil
|
return "192.0.2.1", nil
|
||||||
}
|
}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(vpn, kubeFake, metadataFake, ccmFake, cnmFake, autoscalerFake, encryptedDiskFake, zapLogger, simulator.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(vpn, kubeFake, metadataFake, encryptedDiskFake, zapLogger, simulator.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func TestGetPeers(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// prepare store
|
// prepare store
|
||||||
@ -116,7 +116,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&tc.vpn, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
err = core.AddPeer(tc.peer)
|
err = core.AddPeer(tc.peer)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/atls"
|
"github.com/edgelesssys/constellation/coordinator/atls"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/peer"
|
"github.com/edgelesssys/constellation/coordinator/peer"
|
||||||
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
|
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
@ -71,7 +72,7 @@ func TestReinitializeAsNode(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
coordinators := []Instance{{IPs: []string{"192.0.2.1"}, Role: role.Coordinator}}
|
coordinators := []cloudtypes.Instance{{PrivateIPs: []string{"192.0.2.1"}, Role: role.Coordinator}}
|
||||||
netDialer := testdialer.NewBufconnDialer()
|
netDialer := testdialer.NewBufconnDialer()
|
||||||
dialer := grpcutil.NewDialer(&MockValidator{}, netDialer)
|
dialer := grpcutil.NewDialer(&MockValidator{}, netDialer)
|
||||||
server := newPubAPIServer()
|
server := newPubAPIServer()
|
||||||
@ -81,7 +82,7 @@ func TestReinitializeAsNode(t *testing.T) {
|
|||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
vpn := &stubVPN{}
|
vpn := &stubVPN{}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
err = core.ReinitializeAsNode(context.Background(), dialer, vpnIP, &stubPubAPI{}, 0)
|
err = core.ReinitializeAsNode(context.Background(), dialer, vpnIP, &stubPubAPI{}, 0)
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ func TestReinitializeAsCoordinator(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
coordinators := []Instance{{IPs: []string{"192.0.2.1"}, Role: role.Coordinator}}
|
coordinators := []cloudtypes.Instance{{PrivateIPs: []string{"192.0.2.1"}, Role: role.Coordinator}}
|
||||||
netDialer := testdialer.NewBufconnDialer()
|
netDialer := testdialer.NewBufconnDialer()
|
||||||
dialer := grpcutil.NewDialer(&MockValidator{}, netDialer)
|
dialer := grpcutil.NewDialer(&MockValidator{}, netDialer)
|
||||||
server := newPubAPIServer()
|
server := newPubAPIServer()
|
||||||
@ -154,7 +155,7 @@ func TestReinitializeAsCoordinator(t *testing.T) {
|
|||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
vpn := &stubVPN{}
|
vpn := &stubVPN{}
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, zaptest.NewLogger(t), nil, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
// prepare store to emulate initialized KMS
|
// prepare store to emulate initialized KMS
|
||||||
require.NoError(core.data().PutKMSData(kms.KMSInformation{StorageUri: kms.NoStoreURI, KmsUri: kms.ClusterKMSURI}))
|
require.NoError(core.data().PutKMSData(kms.KMSInformation{StorageUri: kms.NoStoreURI, KmsUri: kms.ClusterKMSURI}))
|
||||||
@ -224,10 +225,10 @@ func TestGetInitialVPNPeers(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
coordinators := func(ips []string) []Instance {
|
coordinators := func(ips []string) []cloudtypes.Instance {
|
||||||
instances := []Instance{}
|
instances := []cloudtypes.Instance{}
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
instances = append(instances, Instance{IPs: []string{ip}, Role: role.Coordinator})
|
instances = append(instances, cloudtypes.Instance{PrivateIPs: []string{ip}, Role: role.Coordinator})
|
||||||
}
|
}
|
||||||
return instances
|
return instances
|
||||||
}(tc.coordinatorIPs)
|
}(tc.coordinatorIPs)
|
||||||
|
@ -67,7 +67,7 @@ func TestAdvanceState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
core, err := NewCore(&stubVPN{}, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(state.Uninitialized, core.GetState())
|
assert.Equal(state.Uninitialized, core.GetState())
|
||||||
core.state = tc.initialState
|
core.state = tc.initialState
|
||||||
|
246
coordinator/kubernetes/cloud_provider.go
Normal file
246
coordinator/kubernetes/cloud_provider.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
|
k8s "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderMetadata implementers read/write cloud provider metadata.
|
||||||
|
type ProviderMetadata interface {
|
||||||
|
// List retrieves all instances belonging to the current Constellation.
|
||||||
|
List(ctx context.Context) ([]cloudtypes.Instance, error)
|
||||||
|
// Self retrieves the current instance.
|
||||||
|
Self(ctx context.Context) (cloudtypes.Instance, error)
|
||||||
|
// GetSubnetworkCIDR retrieves the subnetwork CIDR for the current instance.
|
||||||
|
GetSubnetworkCIDR(ctx context.Context) (string, error)
|
||||||
|
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
|
||||||
|
SupportsLoadBalancer() bool
|
||||||
|
// GetLoadBalancerIP retrieves the load balancer IP.
|
||||||
|
GetLoadBalancerIP(ctx context.Context) (string, error)
|
||||||
|
// GetInstance retrieves an instance using its providerID.
|
||||||
|
GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error)
|
||||||
|
// SignalRole signals the constellation role via cloud provider metadata (if supported by the CSP and deployment type, otherwise does nothing).
|
||||||
|
SignalRole(ctx context.Context, role role.Role) error
|
||||||
|
// SetVPNIP stores the internally used VPN IP in cloud provider metadata (if supported and required for autoscaling by the CSP, otherwise does nothing).
|
||||||
|
SetVPNIP(ctx context.Context, vpnIP string) error
|
||||||
|
// Supported is used to determine if metadata API is implemented for this cloud provider.
|
||||||
|
Supported() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloudControllerManager implementers provide configuration for the k8s cloud-controller-manager.
|
||||||
|
type CloudControllerManager interface {
|
||||||
|
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
|
||||||
|
Image() string
|
||||||
|
// Path returns the path used by cloud-controller-manager executable within the container image.
|
||||||
|
Path() string
|
||||||
|
// Name returns the cloud-provider name as used by k8s cloud-controller-manager (k8s.gcr.io/cloud-controller-manager).
|
||||||
|
Name() string
|
||||||
|
// ExtraArgs returns a list of arguments to append to the cloud-controller-manager command.
|
||||||
|
ExtraArgs() []string
|
||||||
|
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||||
|
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||||
|
ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error)
|
||||||
|
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||||
|
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||||
|
Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error)
|
||||||
|
// Volumes returns a list of volumes to deploy together with the k8s cloud-controller-manager.
|
||||||
|
// Reference: https://kubernetes.io/docs/concepts/storage/volumes/ .
|
||||||
|
Volumes() []k8s.Volume
|
||||||
|
// VolumeMounts a list of of volume mounts to deploy together with the k8s cloud-controller-manager.
|
||||||
|
VolumeMounts() []k8s.VolumeMount
|
||||||
|
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cloud-controller-manager.
|
||||||
|
Env() []k8s.EnvVar
|
||||||
|
// Supported is used to determine if cloud controller manager is implemented for this cloud provider.
|
||||||
|
Supported() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloudNodeManager implementers provide configuration for the k8s cloud-node-manager.
|
||||||
|
type CloudNodeManager interface {
|
||||||
|
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
|
||||||
|
Image() string
|
||||||
|
// Path returns the path used by cloud-node-manager executable within the container image.
|
||||||
|
Path() string
|
||||||
|
// ExtraArgs returns a list of arguments to append to the cloud-node-manager command.
|
||||||
|
ExtraArgs() []string
|
||||||
|
// Supported is used to determine if cloud node manager is implemented for this cloud provider.
|
||||||
|
Supported() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClusterAutoscaler implementers provide configuration for the k8s cluster-autoscaler.
|
||||||
|
type ClusterAutoscaler interface {
|
||||||
|
// Name returns the cloud-provider name as used by k8s cluster-autoscaler.
|
||||||
|
Name() string
|
||||||
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
|
Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error)
|
||||||
|
// Volumes returns a list of volumes to deploy together with the k8s cluster-autoscaler.
|
||||||
|
Volumes() []k8s.Volume
|
||||||
|
// VolumeMounts returns a list of volume mounts to deploy together with the k8s cluster-autoscaler.
|
||||||
|
VolumeMounts() []k8s.VolumeMount
|
||||||
|
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cluster-autoscaler.
|
||||||
|
Env() []k8s.EnvVar
|
||||||
|
// Supported is used to determine if cluster autoscaler is implemented for this cloud provider.
|
||||||
|
Supported() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubProviderMetadata struct {
|
||||||
|
GetLoadBalancerIPErr error
|
||||||
|
GetLoadBalancerIPResp string
|
||||||
|
|
||||||
|
GetSubnetworkCIDRErr error
|
||||||
|
GetSubnetworkCIDRResp string
|
||||||
|
|
||||||
|
ListErr error
|
||||||
|
ListResp []cloudtypes.Instance
|
||||||
|
|
||||||
|
SignalRoleErr error
|
||||||
|
SetVPNIPErr error
|
||||||
|
|
||||||
|
SelfErr error
|
||||||
|
SelfResp cloudtypes.Instance
|
||||||
|
|
||||||
|
GetInstanceErr error
|
||||||
|
GetInstanceResp cloudtypes.Instance
|
||||||
|
|
||||||
|
SupportedResp bool
|
||||||
|
SupportsLoadBalancerResp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||||
|
return m.GetLoadBalancerIPResp, m.GetLoadBalancerIPErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||||
|
return m.GetSubnetworkCIDRResp, m.GetSubnetworkCIDRErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
|
return m.ListResp, m.ListErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
|
return m.SelfResp, m.SelfErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
|
return m.GetInstanceResp, m.GetInstanceErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) SignalRole(ctx context.Context, role role.Role) error {
|
||||||
|
return m.SignalRoleErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
||||||
|
return m.SetVPNIPErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) Supported() bool {
|
||||||
|
return m.SupportedResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubProviderMetadata) SupportsLoadBalancer() bool {
|
||||||
|
return m.SupportsLoadBalancerResp
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubCloudControllerManager struct {
|
||||||
|
SupportedResp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Image() string {
|
||||||
|
return "stub-image:latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Path() string {
|
||||||
|
return "/stub-controller-manager"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Name() string {
|
||||||
|
return "stub"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) ExtraArgs() []string {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||||
|
return []*k8s.ConfigMap{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
|
return []*k8s.Secret{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Volumes() []k8s.Volume {
|
||||||
|
return []k8s.Volume{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) VolumeMounts() []k8s.VolumeMount {
|
||||||
|
return []k8s.VolumeMount{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Env() []k8s.EnvVar {
|
||||||
|
return []k8s.EnvVar{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudControllerManager) Supported() bool {
|
||||||
|
return m.SupportedResp
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubCloudNodeManager struct {
|
||||||
|
SupportedResp bool
|
||||||
|
|
||||||
|
ImageResp string
|
||||||
|
PathResp string
|
||||||
|
ExtraArgsResp []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudNodeManager) Image() string {
|
||||||
|
return m.ImageResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudNodeManager) Path() string {
|
||||||
|
return m.PathResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudNodeManager) ExtraArgs() []string {
|
||||||
|
return m.ExtraArgsResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubCloudNodeManager) Supported() bool {
|
||||||
|
return m.SupportedResp
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubClusterAutoscaler struct {
|
||||||
|
SupportedResp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubClusterAutoscaler) Name() string {
|
||||||
|
return "stub"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||||
|
func (a *stubClusterAutoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||||
|
return resources.Secrets{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volumes returns a list of volumes to deploy together with the k8s cluster-autoscaler.
|
||||||
|
func (a *stubClusterAutoscaler) Volumes() []k8s.Volume {
|
||||||
|
return []k8s.Volume{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeMounts returns a list of volume mounts to deploy together with the k8s cluster-autoscaler.
|
||||||
|
func (a *stubClusterAutoscaler) VolumeMounts() []k8s.VolumeMount {
|
||||||
|
return []k8s.VolumeMount{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Env returns a list of k8s environment key-value pairs to deploy together with the k8s cluster-autoscaler.
|
||||||
|
func (a *stubClusterAutoscaler) Env() []k8s.EnvVar {
|
||||||
|
return []k8s.EnvVar{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *stubClusterAutoscaler) Supported() bool {
|
||||||
|
return a.SupportedResp
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
|
||||||
k8s "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InitClusterInput collects the arguments to initialize a new cluster.
|
|
||||||
type InitClusterInput struct {
|
|
||||||
APIServerAdvertiseIP string
|
|
||||||
NodeIP string
|
|
||||||
NodeName string
|
|
||||||
ProviderID string
|
|
||||||
SupportClusterAutoscaler bool
|
|
||||||
AutoscalingCloudprovider string
|
|
||||||
AutoscalingNodeGroups []string
|
|
||||||
AutoscalingSecrets resources.Secrets
|
|
||||||
AutoscalingVolumes []k8s.Volume
|
|
||||||
AutoscalingVolumeMounts []k8s.VolumeMount
|
|
||||||
AutoscalingEnv []k8s.EnvVar
|
|
||||||
SupportsCloudControllerManager bool
|
|
||||||
CloudControllerManagerName string
|
|
||||||
CloudControllerManagerImage string
|
|
||||||
CloudControllerManagerPath string
|
|
||||||
CloudControllerManagerExtraArgs []string
|
|
||||||
CloudControllerManagerConfigMaps resources.ConfigMaps
|
|
||||||
CloudControllerManagerSecrets resources.Secrets
|
|
||||||
CloudControllerManagerVolumes []k8s.Volume
|
|
||||||
CloudControllerManagerVolumeMounts []k8s.VolumeMount
|
|
||||||
CloudControllerManagerEnv []k8s.EnvVar
|
|
||||||
SupportsCloudNodeManager bool
|
|
||||||
CloudNodeManagerImage string
|
|
||||||
CloudNodeManagerPath string
|
|
||||||
CloudNodeManagerExtraArgs []string
|
|
||||||
MasterSecret []byte
|
|
||||||
}
|
|
@ -104,7 +104,6 @@ func (c *CoreOSConfiguration) InitConfiguration(externalCloudProvider bool) Kube
|
|||||||
"profiling": "false",
|
"profiling": "false",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ControlPlaneEndpoint: "127.0.0.1:16443",
|
|
||||||
},
|
},
|
||||||
// warning: this config is applied to every node in the cluster!
|
// warning: this config is applied to every node in the cluster!
|
||||||
KubeletConfiguration: kubeletconf.KubeletConfiguration{
|
KubeletConfiguration: kubeletconf.KubeletConfiguration{
|
||||||
@ -123,6 +122,18 @@ func (c *CoreOSConfiguration) InitConfiguration(externalCloudProvider bool) Kube
|
|||||||
APIVersion: kubeletconf.SchemeGroupVersion.String(),
|
APIVersion: kubeletconf.SchemeGroupVersion.String(),
|
||||||
Kind: "KubeletConfiguration",
|
Kind: "KubeletConfiguration",
|
||||||
},
|
},
|
||||||
|
RegisterWithTaints: []corev1.Taint{
|
||||||
|
{
|
||||||
|
Key: "node.cloudprovider.kubernetes.io/uninitialized",
|
||||||
|
Value: "true",
|
||||||
|
Effect: corev1.TaintEffectPreferNoSchedule,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "node.cilium.io/agent-not-ready",
|
||||||
|
Value: "true",
|
||||||
|
Effect: corev1.TaintEffectPreferNoSchedule,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,63 +168,6 @@ func (c *CoreOSConfiguration) JoinConfiguration(externalCloudProvider bool) Kube
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AWSConfiguration struct{}
|
|
||||||
|
|
||||||
func (a *AWSConfiguration) InitConfiguration() KubeadmInitYAML {
|
|
||||||
return KubeadmInitYAML{
|
|
||||||
InitConfiguration: kubeadm.InitConfiguration{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: kubeadm.SchemeGroupVersion.String(),
|
|
||||||
Kind: "InitConfiguration",
|
|
||||||
},
|
|
||||||
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
|
||||||
CRISocket: "/run/containerd/containerd.sock",
|
|
||||||
IgnorePreflightErrors: []string{"SystemVerification"},
|
|
||||||
},
|
|
||||||
LocalAPIEndpoint: kubeadm.APIEndpoint{BindPort: bindPort},
|
|
||||||
},
|
|
||||||
ClusterConfiguration: kubeadm.ClusterConfiguration{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: kubeadm.SchemeGroupVersion.String(),
|
|
||||||
Kind: "ClusterConfiguration",
|
|
||||||
},
|
|
||||||
APIServer: kubeadm.APIServer{
|
|
||||||
CertSANs: []string{"10.118.0.1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
KubeletConfiguration: kubeletconf.KubeletConfiguration{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: kubeletconf.SchemeGroupVersion.String(),
|
|
||||||
Kind: "KubeletConfiguration",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AWSConfiguration) JoinConfiguration() KubeadmJoinYAML {
|
|
||||||
return KubeadmJoinYAML{
|
|
||||||
JoinConfiguration: kubeadm.JoinConfiguration{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: kubeadm.SchemeGroupVersion.String(),
|
|
||||||
Kind: "JoinConfiguration",
|
|
||||||
},
|
|
||||||
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
|
||||||
CRISocket: "/run/containerd/containerd.sock",
|
|
||||||
IgnorePreflightErrors: []string{"SystemVerification"},
|
|
||||||
},
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
KubeletConfiguration: kubeletconf.KubeletConfiguration{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: kubeletconf.SchemeGroupVersion.String(),
|
|
||||||
Kind: "KubeletConfiguration",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type KubeadmJoinYAML struct {
|
type KubeadmJoinYAML struct {
|
||||||
JoinConfiguration kubeadm.JoinConfiguration
|
JoinConfiguration kubeadm.JoinConfiguration
|
||||||
KubeletConfiguration kubeletconf.KubeletConfiguration
|
KubeletConfiguration kubeletconf.KubeletConfiguration
|
||||||
@ -276,10 +230,27 @@ func (k *KubeadmInitYAML) SetNodeName(nodeName string) {
|
|||||||
k.InitConfiguration.NodeRegistration.Name = nodeName
|
k.InitConfiguration.NodeRegistration.Name = nodeName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCertSANs sets the SANs for the certificate.
|
||||||
|
func (k *KubeadmInitYAML) SetCertSANs(certSANs []string) {
|
||||||
|
for _, certSAN := range certSANs {
|
||||||
|
if certSAN == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k.ClusterConfiguration.APIServer.CertSANs = append(k.ClusterConfiguration.APIServer.CertSANs, certSAN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (k *KubeadmInitYAML) SetApiServerAdvertiseAddress(apiServerAdvertiseAddress string) {
|
func (k *KubeadmInitYAML) SetApiServerAdvertiseAddress(apiServerAdvertiseAddress string) {
|
||||||
k.InitConfiguration.LocalAPIEndpoint.AdvertiseAddress = apiServerAdvertiseAddress
|
k.InitConfiguration.LocalAPIEndpoint.AdvertiseAddress = apiServerAdvertiseAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetControlPlaneEndpoint sets the control plane endpoint if controlPlaneEndpoint is not empty.
|
||||||
|
func (k *KubeadmInitYAML) SetControlPlaneEndpoint(controlPlaneEndpoint string) {
|
||||||
|
if controlPlaneEndpoint != "" {
|
||||||
|
k.ClusterConfiguration.ControlPlaneEndpoint = controlPlaneEndpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (k *KubeadmInitYAML) SetServiceCIDR(serviceCIDR string) {
|
func (k *KubeadmInitYAML) SetServiceCIDR(serviceCIDR string) {
|
||||||
k.ClusterConfiguration.Networking.ServiceSubnet = serviceCIDR
|
k.ClusterConfiguration.Networking.ServiceSubnet = serviceCIDR
|
||||||
}
|
}
|
||||||
|
@ -13,27 +13,11 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInitConfiguration(t *testing.T) {
|
func TestInitConfiguration(t *testing.T) {
|
||||||
awsConfig := AWSConfiguration{}
|
|
||||||
coreOSConfig := CoreOSConfiguration{}
|
coreOSConfig := CoreOSConfiguration{}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
config KubeadmInitYAML
|
config KubeadmInitYAML
|
||||||
}{
|
}{
|
||||||
"AWS init config can be created": {
|
|
||||||
config: awsConfig.InitConfiguration(),
|
|
||||||
},
|
|
||||||
"AWS init config with all fields can be created": {
|
|
||||||
config: func() KubeadmInitYAML {
|
|
||||||
c := awsConfig.InitConfiguration()
|
|
||||||
c.SetApiServerAdvertiseAddress("192.0.2.0")
|
|
||||||
c.SetNodeIP("192.0.2.0")
|
|
||||||
c.SetNodeName("node")
|
|
||||||
c.SetPodNetworkCIDR("10.244.0.0/16")
|
|
||||||
c.SetServiceCIDR("10.245.0.0/24")
|
|
||||||
c.SetProviderID("somecloudprovider://instance-id")
|
|
||||||
return c
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
"CoreOS init config can be created": {
|
"CoreOS init config can be created": {
|
||||||
config: coreOSConfig.InitConfiguration(true),
|
config: coreOSConfig.InitConfiguration(true),
|
||||||
},
|
},
|
||||||
@ -67,27 +51,11 @@ func TestInitConfiguration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJoinConfiguration(t *testing.T) {
|
func TestJoinConfiguration(t *testing.T) {
|
||||||
awsConfig := AWSConfiguration{}
|
|
||||||
coreOSConfig := CoreOSConfiguration{}
|
coreOSConfig := CoreOSConfiguration{}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
config KubeadmJoinYAML
|
config KubeadmJoinYAML
|
||||||
}{
|
}{
|
||||||
"AWS join config can be created": {
|
|
||||||
config: awsConfig.JoinConfiguration(),
|
|
||||||
},
|
|
||||||
"AWS join config with all fields can be created": {
|
|
||||||
config: func() KubeadmJoinYAML {
|
|
||||||
c := awsConfig.JoinConfiguration()
|
|
||||||
c.SetApiServerEndpoint("192.0.2.0:6443")
|
|
||||||
c.SetNodeIP("192.0.2.0")
|
|
||||||
c.SetNodeName("node")
|
|
||||||
c.SetToken("token")
|
|
||||||
c.AppendDiscoveryTokenCaCertHash("discovery-token-ca-cert-hash")
|
|
||||||
c.SetProviderID("somecloudprovider://instance-id")
|
|
||||||
return c
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
"CoreOS join config can be created": {
|
"CoreOS join config can be created": {
|
||||||
config: coreOSConfig.JoinConfiguration(true),
|
config: coreOSConfig.JoinConfiguration(true),
|
||||||
},
|
},
|
||||||
|
@ -239,18 +239,13 @@ func TestGetObjects(t *testing.T) {
|
|||||||
resourcesYAML string
|
resourcesYAML string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"GetObjects works on flannel deployment": {
|
|
||||||
wantResources: resources.NewDefaultFlannelDeployment(),
|
|
||||||
resourcesYAML: string(nginxDeplYAML),
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"GetObjects works on cluster-autoscaler deployment": {
|
"GetObjects works on cluster-autoscaler deployment": {
|
||||||
wantResources: resources.NewDefaultFlannelDeployment(),
|
wantResources: resources.NewDefaultAutoscalerDeployment(nil, nil, nil),
|
||||||
resourcesYAML: string(nginxDeplYAML),
|
resourcesYAML: string(nginxDeplYAML),
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"GetObjects works on cloud-controller-manager deployment": {
|
"GetObjects works on cloud-controller-manager deployment": {
|
||||||
wantResources: resources.NewDefaultCloudControllerManagerDeployment("someProvider", "someImage", "somePath", nil, nil, nil, nil),
|
wantResources: resources.NewDefaultCloudControllerManagerDeployment("someProvider", "someImage", "somePath", "someCIDR", nil, nil, nil, nil),
|
||||||
resourcesYAML: string(nginxDeplYAML),
|
resourcesYAML: string(nginxDeplYAML),
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
@ -9,8 +9,6 @@ import (
|
|||||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultCIDR = "10.244.0.0/16"
|
|
||||||
|
|
||||||
type cloudControllerManagerDeployment struct {
|
type cloudControllerManagerDeployment struct {
|
||||||
ServiceAccount k8s.ServiceAccount
|
ServiceAccount k8s.ServiceAccount
|
||||||
ClusterRoleBinding rbac.ClusterRoleBinding
|
ClusterRoleBinding rbac.ClusterRoleBinding
|
||||||
@ -22,14 +20,12 @@ type cloudControllerManagerDeployment struct {
|
|||||||
// https://kubernetes.io/docs/tasks/administer-cluster/running-cloud-controller/#cloud-controller-manager
|
// https://kubernetes.io/docs/tasks/administer-cluster/running-cloud-controller/#cloud-controller-manager
|
||||||
|
|
||||||
// NewDefaultCloudControllerManagerDeployment creates a new *cloudControllerManagerDeployment, customized for the CSP.
|
// NewDefaultCloudControllerManagerDeployment creates a new *cloudControllerManagerDeployment, customized for the CSP.
|
||||||
func NewDefaultCloudControllerManagerDeployment(cloudProvider, image, path string, extraArgs []string, extraVolumes []k8s.Volume, extraVolumeMounts []k8s.VolumeMount, env []k8s.EnvVar) *cloudControllerManagerDeployment {
|
func NewDefaultCloudControllerManagerDeployment(cloudProvider, image, path, podCIDR string, extraArgs []string, extraVolumes []k8s.Volume, extraVolumeMounts []k8s.VolumeMount, env []k8s.EnvVar) *cloudControllerManagerDeployment {
|
||||||
command := []string{
|
command := []string{
|
||||||
path,
|
path,
|
||||||
fmt.Sprintf("--cloud-provider=%s", cloudProvider),
|
fmt.Sprintf("--cloud-provider=%s", cloudProvider),
|
||||||
"--leader-elect=true",
|
"--leader-elect=true",
|
||||||
"--allocate-node-cidrs=false",
|
fmt.Sprintf("--cluster-cidr=%s", podCIDR),
|
||||||
"--configure-cloud-routes=false",
|
|
||||||
fmt.Sprintf("--cluster-cidr=%s", defaultCIDR),
|
|
||||||
"-v=2",
|
"-v=2",
|
||||||
}
|
}
|
||||||
command = append(command, extraArgs...)
|
command = append(command, extraArgs...)
|
||||||
@ -151,6 +147,10 @@ func NewDefaultCloudControllerManagerDeployment(cloudProvider, image, path strin
|
|||||||
Key: "node-role.kubernetes.io/master",
|
Key: "node-role.kubernetes.io/master",
|
||||||
Effect: k8s.TaintEffectNoSchedule,
|
Effect: k8s.TaintEffectNoSchedule,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: "node.kubernetes.io/not-ready",
|
||||||
|
Effect: k8s.TaintEffectNoSchedule,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
NodeSelector: map[string]string{
|
NodeSelector: map[string]string{
|
||||||
"node-role.kubernetes.io/master": "",
|
"node-role.kubernetes.io/master": "",
|
||||||
|
@ -12,7 +12,7 @@ func TestCloudControllerMarshalUnmarshal(t *testing.T) {
|
|||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cloudControllerManagerDepl := NewDefaultCloudControllerManagerDeployment("dummy-cloudprovider", "some-image:latest", "/dummy_path", []string{}, []k8s.Volume{}, []k8s.VolumeMount{}, nil)
|
cloudControllerManagerDepl := NewDefaultCloudControllerManagerDeployment("dummy-cloudprovider", "some-image:latest", "/dummy_path", "192.0.2.0/24", []string{}, []k8s.Volume{}, []k8s.VolumeMount{}, nil)
|
||||||
data, err := cloudControllerManagerDepl.Marshal()
|
data, err := cloudControllerManagerDepl.Marshal()
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
|
@ -1,339 +0,0 @@
|
|||||||
package resources
|
|
||||||
|
|
||||||
import (
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
apps "k8s.io/api/apps/v1"
|
|
||||||
k8s "k8s.io/api/core/v1"
|
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
|
||||||
rbac "k8s.io/api/rbac/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
cniConfJSON = `{"name":"cbr0","cniVersion":"0.3.1","plugins":[{"type":"flannel","delegate":{"hairpinMode":true,"isDefaultGateway":true}},{"type":"portmap","capabilities":{"portMappings":true}}]}`
|
|
||||||
netConfJSON = `{"Network":"10.244.0.0/16","Backend":{"Type":"vxlan"}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference: https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
|
|
||||||
// Changes compared to the reference: added the wireguard interface "wg0" to the args of the "kube-flannel" container of the DaemonSet.
|
|
||||||
|
|
||||||
type FlannelDeployment struct {
|
|
||||||
PodSecurityPolicy policy.PodSecurityPolicy
|
|
||||||
ClusterRole rbac.ClusterRole
|
|
||||||
ClusterRoleBinding rbac.ClusterRoleBinding
|
|
||||||
ServiceAccount k8s.ServiceAccount
|
|
||||||
ConfigMap k8s.ConfigMap
|
|
||||||
DaemonSet apps.DaemonSet
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDefaultFlannelDeployment() *FlannelDeployment {
|
|
||||||
return &FlannelDeployment{
|
|
||||||
PodSecurityPolicy: policy.PodSecurityPolicy{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
APIVersion: "policy/v1beta1",
|
|
||||||
Kind: "PodSecurityPolicy",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "psp.flannel.unprivileged",
|
|
||||||
Annotations: map[string]string{
|
|
||||||
"seccomp.security.alpha.kubernetes.io/allowedProfileNames": "docker/default",
|
|
||||||
"seccomp.security.alpha.kubernetes.io/defaultProfileName": "docker/default",
|
|
||||||
"apparmor.security.beta.kubernetes.io/allowedProfileNames": "runtime/default",
|
|
||||||
"apparmor.security.beta.kubernetes.io/defaultProfileName": "runtime/default",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: policy.PodSecurityPolicySpec{
|
|
||||||
Privileged: false,
|
|
||||||
Volumes: []policy.FSType{
|
|
||||||
policy.FSType("configMap"),
|
|
||||||
policy.FSType("secret"),
|
|
||||||
policy.FSType("emptyDir"),
|
|
||||||
policy.FSType("hostPath"),
|
|
||||||
},
|
|
||||||
AllowedHostPaths: []policy.AllowedHostPath{
|
|
||||||
{PathPrefix: "/etc/cni/net.d"},
|
|
||||||
{PathPrefix: "/etc/kube-flannel"},
|
|
||||||
{PathPrefix: "/run/flannel"},
|
|
||||||
},
|
|
||||||
ReadOnlyRootFilesystem: false,
|
|
||||||
RunAsUser: policy.RunAsUserStrategyOptions{
|
|
||||||
Rule: policy.RunAsUserStrategyRunAsAny,
|
|
||||||
},
|
|
||||||
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
|
|
||||||
Rule: policy.SupplementalGroupsStrategyRunAsAny,
|
|
||||||
},
|
|
||||||
FSGroup: policy.FSGroupStrategyOptions{
|
|
||||||
Rule: policy.FSGroupStrategyRunAsAny,
|
|
||||||
},
|
|
||||||
AllowPrivilegeEscalation: proto.Bool(false),
|
|
||||||
DefaultAllowPrivilegeEscalation: proto.Bool(false),
|
|
||||||
AllowedCapabilities: []k8s.Capability{
|
|
||||||
k8s.Capability("NET_ADMIN"),
|
|
||||||
k8s.Capability("NET_RAW"),
|
|
||||||
},
|
|
||||||
HostPID: false,
|
|
||||||
HostIPC: false,
|
|
||||||
HostNetwork: true,
|
|
||||||
HostPorts: []policy.HostPortRange{
|
|
||||||
{Min: 0, Max: 65535},
|
|
||||||
},
|
|
||||||
SELinux: policy.SELinuxStrategyOptions{
|
|
||||||
Rule: policy.SELinuxStrategyRunAsAny,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ClusterRole: rbac.ClusterRole{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
||||||
Kind: "ClusterRole",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "flannel",
|
|
||||||
},
|
|
||||||
Rules: []rbac.PolicyRule{
|
|
||||||
{
|
|
||||||
APIGroups: []string{"extensions"},
|
|
||||||
Resources: []string{"podsecuritypolicies"},
|
|
||||||
Verbs: []string{"use"},
|
|
||||||
ResourceNames: []string{"psp.flannel.unprivileged"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"pods"},
|
|
||||||
Verbs: []string{"get"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"nodes"},
|
|
||||||
Verbs: []string{"list", "watch"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"nodes/status"},
|
|
||||||
Verbs: []string{"patch"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ClusterRoleBinding: rbac.ClusterRoleBinding{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
Kind: "ClusterRoleBinding",
|
|
||||||
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "flannel",
|
|
||||||
},
|
|
||||||
RoleRef: rbac.RoleRef{
|
|
||||||
APIGroup: "rbac.authorization.k8s.io",
|
|
||||||
Kind: "ClusterRole",
|
|
||||||
Name: "flannel",
|
|
||||||
},
|
|
||||||
Subjects: []rbac.Subject{
|
|
||||||
{
|
|
||||||
Kind: "ServiceAccount",
|
|
||||||
Name: "flannel",
|
|
||||||
Namespace: "kube-system",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ServiceAccount: k8s.ServiceAccount{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
APIVersion: "v1",
|
|
||||||
Kind: "ServiceAccount",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "flannel",
|
|
||||||
Namespace: "kube-system",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ConfigMap: k8s.ConfigMap{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
Kind: "ConfigMap",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "kube-flannel-cfg",
|
|
||||||
Namespace: "kube-system",
|
|
||||||
Labels: map[string]string{
|
|
||||||
"tier": "node",
|
|
||||||
"app": "flannel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Data: map[string]string{
|
|
||||||
"cni-conf.json": cniConfJSON,
|
|
||||||
"net-conf.json": netConfJSON,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DaemonSet: apps.DaemonSet{
|
|
||||||
TypeMeta: v1.TypeMeta{
|
|
||||||
APIVersion: "apps/v1",
|
|
||||||
Kind: "DaemonSet",
|
|
||||||
},
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "kube-flannel-ds",
|
|
||||||
Namespace: "kube-system",
|
|
||||||
Labels: map[string]string{
|
|
||||||
"tier": "node",
|
|
||||||
"app": "flannel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: apps.DaemonSetSpec{
|
|
||||||
Selector: &v1.LabelSelector{
|
|
||||||
MatchLabels: map[string]string{"app": "flannel"},
|
|
||||||
},
|
|
||||||
Template: k8s.PodTemplateSpec{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Labels: map[string]string{
|
|
||||||
"tier": "node",
|
|
||||||
"app": "flannel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: k8s.PodSpec{
|
|
||||||
Affinity: &k8s.Affinity{
|
|
||||||
NodeAffinity: &k8s.NodeAffinity{
|
|
||||||
RequiredDuringSchedulingIgnoredDuringExecution: &k8s.NodeSelector{
|
|
||||||
NodeSelectorTerms: []k8s.NodeSelectorTerm{
|
|
||||||
{MatchExpressions: []k8s.NodeSelectorRequirement{
|
|
||||||
{
|
|
||||||
Key: "kubernetes.io/os",
|
|
||||||
Operator: k8s.NodeSelectorOpIn,
|
|
||||||
Values: []string{"linux"},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HostNetwork: true,
|
|
||||||
PriorityClassName: "system-node-critical",
|
|
||||||
Tolerations: []k8s.Toleration{
|
|
||||||
{
|
|
||||||
Operator: k8s.TolerationOpExists,
|
|
||||||
Effect: k8s.TaintEffectNoSchedule,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ServiceAccountName: "flannel",
|
|
||||||
InitContainers: []k8s.Container{
|
|
||||||
{
|
|
||||||
Name: "install-cni-plugin",
|
|
||||||
Image: "rancher/mirrored-flannelcni-flannel-cni-plugin:v1.0.0",
|
|
||||||
Command: []string{"cp"},
|
|
||||||
Args: []string{"-f", "/flannel", "/opt/cni/bin/flannel"},
|
|
||||||
VolumeMounts: []k8s.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "cni-plugin",
|
|
||||||
MountPath: "/opt/cni/bin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "install-cni",
|
|
||||||
Image: "quay.io/coreos/flannel:v0.15.1",
|
|
||||||
Command: []string{"cp"},
|
|
||||||
Args: []string{"-f", "/etc/kube-flannel/cni-conf.json", "/etc/cni/net.d/10-flannel.conflist"},
|
|
||||||
VolumeMounts: []k8s.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "cni",
|
|
||||||
MountPath: "/etc/cni/net.d",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "flannel-cfg",
|
|
||||||
MountPath: "/etc/kube-flannel/",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Containers: []k8s.Container{
|
|
||||||
{
|
|
||||||
Name: "kube-flannel",
|
|
||||||
Image: "quay.io/coreos/flannel:v0.15.1",
|
|
||||||
Command: []string{"/opt/bin/flanneld"},
|
|
||||||
Args: []string{"--ip-masq", "--kube-subnet-mgr", "--iface", "wg0"},
|
|
||||||
Resources: k8s.ResourceRequirements{
|
|
||||||
Requests: k8s.ResourceList{
|
|
||||||
"cpu": resource.MustParse("100m"),
|
|
||||||
"memory": resource.MustParse("50Mi"),
|
|
||||||
},
|
|
||||||
Limits: k8s.ResourceList{
|
|
||||||
"cpu": resource.MustParse("100m"),
|
|
||||||
"memory": resource.MustParse("50Mi"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityContext: &k8s.SecurityContext{
|
|
||||||
Privileged: proto.Bool(false),
|
|
||||||
Capabilities: &k8s.Capabilities{
|
|
||||||
Add: []k8s.Capability{k8s.Capability("NET_ADMIN"), k8s.Capability("NET_RAW")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Env: []k8s.EnvVar{
|
|
||||||
{
|
|
||||||
Name: "POD_NAME",
|
|
||||||
ValueFrom: &k8s.EnvVarSource{
|
|
||||||
FieldRef: &k8s.ObjectFieldSelector{FieldPath: "metadata.name"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "POD_NAMESPACE",
|
|
||||||
ValueFrom: &k8s.EnvVarSource{
|
|
||||||
FieldRef: &k8s.ObjectFieldSelector{FieldPath: "metadata.namespace"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VolumeMounts: []k8s.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "run",
|
|
||||||
MountPath: "/run/flannel",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "flannel-cfg",
|
|
||||||
MountPath: "/etc/kube-flannel/",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Volumes: []k8s.Volume{
|
|
||||||
{
|
|
||||||
Name: "run",
|
|
||||||
VolumeSource: k8s.VolumeSource{
|
|
||||||
HostPath: &k8s.HostPathVolumeSource{
|
|
||||||
Path: "/run/flannel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "cni-plugin",
|
|
||||||
VolumeSource: k8s.VolumeSource{
|
|
||||||
HostPath: &k8s.HostPathVolumeSource{
|
|
||||||
Path: "/opt/cni/bin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "cni",
|
|
||||||
VolumeSource: k8s.VolumeSource{
|
|
||||||
HostPath: &k8s.HostPathVolumeSource{
|
|
||||||
Path: "/etc/cni/net.d",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "flannel-cfg",
|
|
||||||
VolumeSource: k8s.VolumeSource{
|
|
||||||
ConfigMap: &k8s.ConfigMapVolumeSource{
|
|
||||||
LocalObjectReference: k8s.LocalObjectReference{
|
|
||||||
Name: "kube-flannel-cfg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlannelDeployment) Marshal() ([]byte, error) {
|
|
||||||
return MarshalK8SResources(f)
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package resources
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFlannelDeployment(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
flannelDeployment := NewDefaultFlannelDeployment()
|
|
||||||
data, err := flannelDeployment.Marshal()
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
var recreated FlannelDeployment
|
|
||||||
require.NoError(UnmarshalK8SResources(data, &recreated))
|
|
||||||
assert.Equal(flannelDeployment, &recreated)
|
|
||||||
}
|
|
@ -20,6 +20,11 @@ const (
|
|||||||
kubeletStartTimeout = 10 * time.Minute
|
kubeletStartTimeout = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kubernetesKeyRegexp = regexp.MustCompile("[a-f0-9]{64}")
|
||||||
|
providerIDRegex = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`)
|
||||||
|
)
|
||||||
|
|
||||||
// Client provides the functionality of `kubectl apply`.
|
// Client provides the functionality of `kubectl apply`.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Apply(resources resources.Marshaler, forceConflicts bool) error
|
Apply(resources resources.Marshaler, forceConflicts bool) error
|
||||||
@ -27,21 +32,6 @@ type Client interface {
|
|||||||
// TODO: add tolerations
|
// TODO: add tolerations
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterUtil interface {
|
|
||||||
InstallComponents(ctx context.Context, version string) error
|
|
||||||
InitCluster(initConfig []byte) error
|
|
||||||
JoinCluster(joinConfig []byte) error
|
|
||||||
SetupPodNetwork(kubectl Client, podNetworkConfiguration resources.Marshaler) error
|
|
||||||
SetupAutoscaling(kubectl Client, clusterAutoscalerConfiguration resources.Marshaler, secrets resources.Marshaler) error
|
|
||||||
SetupCloudControllerManager(kubectl Client, cloudControllerManagerConfiguration resources.Marshaler, configMaps resources.Marshaler, secrets resources.Marshaler) error
|
|
||||||
SetupCloudNodeManager(kubectl Client, cloudNodeManagerConfiguration resources.Marshaler) error
|
|
||||||
SetupKMS(kubectl Client, kmsConfiguration resources.Marshaler) error
|
|
||||||
StartKubelet() error
|
|
||||||
RestartKubelet() error
|
|
||||||
GetControlPlaneJoinCertificateKey() (string, error)
|
|
||||||
CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// KubernetesUtil provides low level management of the kubernetes cluster.
|
// KubernetesUtil provides low level management of the kubernetes cluster.
|
||||||
type KubernetesUtil struct {
|
type KubernetesUtil struct {
|
||||||
inst installer
|
inst installer
|
||||||
@ -68,7 +58,7 @@ func (k *KubernetesUtil) InstallComponents(ctx context.Context, version string)
|
|||||||
return enableSystemdUnit(ctx, kubeletServiceEtcPath)
|
return enableSystemdUnit(ctx, kubeletServiceEtcPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KubernetesUtil) InitCluster(initConfig []byte) error {
|
func (k *KubernetesUtil) InitCluster(ctx context.Context, initConfig []byte) error {
|
||||||
// TODO: audit policy should be user input
|
// TODO: audit policy should be user input
|
||||||
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -88,7 +78,7 @@ func (k *KubernetesUtil) InitCluster(initConfig []byte) error {
|
|||||||
return fmt.Errorf("writing kubeadm init yaml config %v failed: %w", initConfigFile.Name(), err)
|
return fmt.Errorf("writing kubeadm init yaml config %v failed: %w", initConfigFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(kubeadmPath, "init", "--config", initConfigFile.Name())
|
cmd := exec.CommandContext(ctx, kubeadmPath, "init", "--config", initConfigFile.Name())
|
||||||
_, err = cmd.Output()
|
_, err = cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var exitErr *exec.ExitError
|
var exitErr *exec.ExitError
|
||||||
@ -100,18 +90,84 @@ func (k *KubernetesUtil) InitCluster(initConfig []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupPodNetwork sets up the flannel pod network.
|
type SetupPodNetworkInput struct {
|
||||||
func (k *KubernetesUtil) SetupPodNetwork(kubectl Client, podNetworkConfiguration resources.Marshaler) error {
|
CloudProvider string
|
||||||
if err := kubectl.Apply(podNetworkConfiguration, true); err != nil {
|
NodeName string
|
||||||
|
FirstNodePodCIDR string
|
||||||
|
SubnetworkPodCIDR string
|
||||||
|
ProviderID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupPodNetwork sets up the cilium pod network.
|
||||||
|
func (k *KubernetesUtil) SetupPodNetwork(ctx context.Context, in SetupPodNetworkInput) error {
|
||||||
|
switch in.CloudProvider {
|
||||||
|
case "gcp":
|
||||||
|
return k.setupGCPPodNetwork(ctx, in.NodeName, in.FirstNodePodCIDR, in.SubnetworkPodCIDR)
|
||||||
|
case "azure":
|
||||||
|
return k.setupAzurePodNetwork(ctx, in.ProviderID, in.SubnetworkPodCIDR)
|
||||||
|
case "qemu":
|
||||||
|
return k.setupQemuPodNetwork(ctx)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported cloud provider %q", in.CloudProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubernetesUtil) setupAzurePodNetwork(ctx context.Context, providerID, subnetworkPodCIDR string) error {
|
||||||
|
matches := providerIDRegex.FindStringSubmatch(providerID)
|
||||||
|
if len(matches) != 5 {
|
||||||
|
return fmt.Errorf("error splitting providerID %q", providerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ciliumInstall := exec.CommandContext(ctx, "cilium", "install", "--azure-resource-group", matches[2], "--encryption", "wireguard", "--ipam", "azure",
|
||||||
|
"--helm-set",
|
||||||
|
"tunnel=disabled,enableIPv4Masquerade=true,azure.enabled=true,debug.enabled=true,ipv4NativeRoutingCIDR="+subnetworkPodCIDR+
|
||||||
|
",endpointRoutes.enabled=true,encryption.enabled=true,encryption.type=wireguard,l7Proxy=false,egressMasqueradeInterfaces=eth0")
|
||||||
|
ciliumInstall.Env = append(os.Environ(), "KUBECONFIG="+kubeConfig)
|
||||||
|
out, err := ciliumInstall.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New(string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubernetesUtil) setupGCPPodNetwork(ctx context.Context, nodeName, nodePodCIDR, subnetworkPodCIDR string) error {
|
||||||
|
out, err := exec.CommandContext(ctx, kubectlPath, "--kubeconfig", kubeConfig, "patch", "node", nodeName, "-p", "{\"spec\":{\"podCIDR\": \""+nodePodCIDR+"\"}}").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New(string(out))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow coredns to run on uninitialized nodes (required by cloud-controller-manager)
|
// allow coredns to run on uninitialized nodes (required by cloud-controller-manager)
|
||||||
err := exec.Command(kubectlPath, "--kubeconfig", kubeConfig, "-n", "kube-system", "patch", "deployment", "coredns", "--type", "json", "-p", "[{\"op\":\"add\",\"path\":\"/spec/template/spec/tolerations/-\",\"value\":{\"key\":\"node.cloudprovider.kubernetes.io/uninitialized\",\"value\":\"true\",\"effect\":\"NoSchedule\"}}]").Run()
|
err = exec.CommandContext(ctx, kubectlPath, "--kubeconfig", kubeConfig, "-n", "kube-system", "patch", "deployment", "coredns", "--type", "json", "-p", "[{\"op\":\"add\",\"path\":\"/spec/template/spec/tolerations/-\",\"value\":{\"key\":\"node.cloudprovider.kubernetes.io/uninitialized\",\"value\":\"true\",\"effect\":\"NoSchedule\"}},{\"op\":\"add\",\"path\":\"/spec/template/spec/nodeSelector\",\"value\":{\"node-role.kubernetes.io/control-plane\":\"\"}}]").Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return exec.Command(kubectlPath, "--kubeconfig", kubeConfig, "-n", "kube-system", "patch", "deployment", "coredns", "--type", "json", "-p", "[{\"op\":\"add\",\"path\":\"/spec/template/spec/tolerations/-\",\"value\":{\"key\":\"node.kubernetes.io/network-unavailable\",\"value\":\"\",\"effect\":\"NoSchedule\"}}]").Run()
|
|
||||||
|
err = exec.CommandContext(ctx, kubectlPath, "--kubeconfig", kubeConfig, "-n", "kube-system", "patch", "deployment", "coredns", "--type", "json", "-p", "[{\"op\":\"add\",\"path\":\"/spec/template/spec/tolerations/-\",\"value\":{\"key\":\"node.kubernetes.io/network-unavailable\",\"value\":\"\",\"effect\":\"NoSchedule\"}}]").Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciliumInstall := exec.CommandContext(ctx, "cilium", "install", "--encryption", "wireguard", "--ipam", "kubernetes", "--ipv4-native-routing-cidr", subnetworkPodCIDR, "--helm-set", "endpointRoutes.enabled=true,tunnel=disabled")
|
||||||
|
ciliumInstall.Env = append(os.Environ(), "KUBECONFIG="+kubeConfig)
|
||||||
|
out, err = ciliumInstall.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New(string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubernetesUtil) setupQemuPodNetwork(ctx context.Context) error {
|
||||||
|
ciliumInstall := exec.CommandContext(ctx, "cilium", "install", "--encryption", "wireguard", "--helm-set", "ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16,endpointRoutes.enabled=true")
|
||||||
|
ciliumInstall.Env = append(os.Environ(), "KUBECONFIG="+kubeConfig)
|
||||||
|
out, err := ciliumInstall.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New(string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupAutoscaling deploys the k8s cluster autoscaler.
|
// SetupAutoscaling deploys the k8s cluster autoscaler.
|
||||||
@ -142,7 +198,7 @@ func (k *KubernetesUtil) SetupCloudNodeManager(kubectl Client, cloudNodeManagerC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JoinCluster joins existing Kubernetes cluster using kubeadm join.
|
// JoinCluster joins existing Kubernetes cluster using kubeadm join.
|
||||||
func (k *KubernetesUtil) JoinCluster(joinConfig []byte) error {
|
func (k *KubernetesUtil) JoinCluster(ctx context.Context, joinConfig []byte) error {
|
||||||
// TODO: audit policy should be user input
|
// TODO: audit policy should be user input
|
||||||
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,7 +219,7 @@ func (k *KubernetesUtil) JoinCluster(joinConfig []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// run `kubeadm join` to join a worker node to an existing Kubernetes cluster
|
// run `kubeadm join` to join a worker node to an existing Kubernetes cluster
|
||||||
cmd := exec.Command(kubeadmPath, "join", "--config", joinConfigFile.Name())
|
cmd := exec.CommandContext(ctx, kubeadmPath, "join", "--config", joinConfigFile.Name())
|
||||||
if _, err := cmd.Output(); err != nil {
|
if _, err := cmd.Output(); err != nil {
|
||||||
var exitErr *exec.ExitError
|
var exitErr *exec.ExitError
|
||||||
if errors.As(err, &exitErr) {
|
if errors.As(err, &exitErr) {
|
||||||
@ -201,10 +257,10 @@ func (k *KubernetesUtil) RestartKubelet() error {
|
|||||||
|
|
||||||
// GetControlPlaneJoinCertificateKey return the key which can be used in combination with the joinArgs
|
// GetControlPlaneJoinCertificateKey return the key which can be used in combination with the joinArgs
|
||||||
// to join the Cluster as control-plane.
|
// to join the Cluster as control-plane.
|
||||||
func (k *KubernetesUtil) GetControlPlaneJoinCertificateKey() (string, error) {
|
func (k *KubernetesUtil) GetControlPlaneJoinCertificateKey(ctx context.Context) (string, error) {
|
||||||
// Key will be valid for 1h (no option to reduce the duration).
|
// Key will be valid for 1h (no option to reduce the duration).
|
||||||
// https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init-phase/#cmd-phase-upload-certs
|
// https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init-phase/#cmd-phase-upload-certs
|
||||||
output, err := exec.Command(kubeadmPath, "init", "phase", "upload-certs", "--upload-certs").Output()
|
output, err := exec.CommandContext(ctx, kubeadmPath, "init", "phase", "upload-certs", "--upload-certs").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var exitErr *exec.ExitError
|
var exitErr *exec.ExitError
|
||||||
if errors.As(err, &exitErr) {
|
if errors.As(err, &exitErr) {
|
||||||
@ -218,7 +274,7 @@ func (k *KubernetesUtil) GetControlPlaneJoinCertificateKey() (string, error) {
|
|||||||
[upload-certs] Using certificate key:
|
[upload-certs] Using certificate key:
|
||||||
9555b74008f24687eb964bd90a164ecb5760a89481d9c55a77c129b7db438168
|
9555b74008f24687eb964bd90a164ecb5760a89481d9c55a77c129b7db438168
|
||||||
*/
|
*/
|
||||||
key := regexp.MustCompile("[a-f0-9]{64}").FindString(string(output))
|
key := kubernetesKeyRegexp.FindString(string(output))
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return "", fmt.Errorf("failed to parse kubeadm output: %s", string(output))
|
return "", fmt.Errorf("failed to parse kubeadm output: %s", string(output))
|
||||||
}
|
}
|
||||||
@ -226,8 +282,8 @@ func (k *KubernetesUtil) GetControlPlaneJoinCertificateKey() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateJoinToken creates a new bootstrap (join) token.
|
// CreateJoinToken creates a new bootstrap (join) token.
|
||||||
func (k *KubernetesUtil) CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (k *KubernetesUtil) CreateJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
output, err := exec.Command(kubeadmPath, "token", "create", "--ttl", ttl.String(), "--print-join-command").Output()
|
output, err := exec.CommandContext(ctx, kubeadmPath, "token", "create", "--ttl", ttl.String(), "--print-join-command").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("kubeadm token create failed: %w", err)
|
return nil, fmt.Errorf("kubeadm token create failed: %w", err)
|
||||||
}
|
}
|
||||||
|
25
coordinator/kubernetes/k8sutil.go
Normal file
25
coordinator/kubernetes/k8sutil.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clusterUtil interface {
|
||||||
|
InstallComponents(ctx context.Context, version string) error
|
||||||
|
InitCluster(ctx context.Context, initConfig []byte) error
|
||||||
|
JoinCluster(ctx context.Context, joinConfig []byte) error
|
||||||
|
SetupPodNetwork(context.Context, k8sapi.SetupPodNetworkInput) error
|
||||||
|
SetupAutoscaling(kubectl k8sapi.Client, clusterAutoscalerConfiguration resources.Marshaler, secrets resources.Marshaler) error
|
||||||
|
SetupCloudControllerManager(kubectl k8sapi.Client, cloudControllerManagerConfiguration resources.Marshaler, configMaps resources.Marshaler, secrets resources.Marshaler) error
|
||||||
|
SetupCloudNodeManager(kubectl k8sapi.Client, cloudNodeManagerConfiguration resources.Marshaler) error
|
||||||
|
SetupKMS(kubectl k8sapi.Client, kmsConfiguration resources.Marshaler) error
|
||||||
|
StartKubelet() error
|
||||||
|
RestartKubelet() error
|
||||||
|
GetControlPlaneJoinCertificateKey(ctx context.Context) (string, error)
|
||||||
|
CreateJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
@ -13,12 +14,6 @@ import (
|
|||||||
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// k8s pod network cidr. This value was chosen to match the default flannel pod network.
|
|
||||||
const (
|
|
||||||
podNetworkCidr = "10.244.0.0/16"
|
|
||||||
serviceCidr = "10.245.0.0/24"
|
|
||||||
)
|
|
||||||
|
|
||||||
// configReader provides kubeconfig as []byte.
|
// configReader provides kubeconfig as []byte.
|
||||||
type configReader interface {
|
type configReader interface {
|
||||||
ReadKubeconfig() ([]byte, error)
|
ReadKubeconfig() ([]byte, error)
|
||||||
@ -30,43 +25,96 @@ type configurationProvider interface {
|
|||||||
JoinConfiguration(externalCloudProvider bool) k8sapi.KubeadmJoinYAML
|
JoinConfiguration(externalCloudProvider bool) k8sapi.KubeadmJoinYAML
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubeWrapper implements ClusterWrapper interface.
|
// KubeWrapper implements Cluster interface.
|
||||||
type KubeWrapper struct {
|
type KubeWrapper struct {
|
||||||
clusterUtil k8sapi.ClusterUtil
|
cloudProvider string
|
||||||
configProvider configurationProvider
|
clusterUtil clusterUtil
|
||||||
client k8sapi.Client
|
configProvider configurationProvider
|
||||||
kubeconfigReader configReader
|
client k8sapi.Client
|
||||||
|
kubeconfigReader configReader
|
||||||
|
cloudControllerManager CloudControllerManager
|
||||||
|
cloudNodeManager CloudNodeManager
|
||||||
|
clusterAutoscaler ClusterAutoscaler
|
||||||
|
providerMetadata ProviderMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new KubeWrapper with real values.
|
// New creates a new KubeWrapper with real values.
|
||||||
func New(clusterUtil k8sapi.ClusterUtil, configProvider configurationProvider, client k8sapi.Client) *KubeWrapper {
|
func New(cloudProvider string, clusterUtil clusterUtil, configProvider configurationProvider, client k8sapi.Client, cloudControllerManager CloudControllerManager,
|
||||||
|
cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler, providerMetadata ProviderMetadata,
|
||||||
|
) *KubeWrapper {
|
||||||
return &KubeWrapper{
|
return &KubeWrapper{
|
||||||
clusterUtil: clusterUtil,
|
cloudProvider: cloudProvider,
|
||||||
configProvider: configProvider,
|
clusterUtil: clusterUtil,
|
||||||
client: client,
|
configProvider: configProvider,
|
||||||
kubeconfigReader: &KubeconfigReader{fs: afero.Afero{Fs: afero.NewOsFs()}},
|
client: client,
|
||||||
|
kubeconfigReader: &KubeconfigReader{fs: afero.Afero{Fs: afero.NewOsFs()}},
|
||||||
|
cloudControllerManager: cloudControllerManager,
|
||||||
|
cloudNodeManager: cloudNodeManager,
|
||||||
|
clusterAutoscaler: clusterAutoscaler,
|
||||||
|
providerMetadata: providerMetadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
|
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
|
||||||
func (k *KubeWrapper) InitCluster(in InitClusterInput) error {
|
func (k *KubeWrapper) InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, vpnIP string, masterSecret []byte) error {
|
||||||
// TODO: k8s version should be user input
|
// TODO: k8s version should be user input
|
||||||
if err := k.clusterUtil.InstallComponents(context.TODO(), "1.23.6"); err != nil {
|
if err := k.clusterUtil.InstallComponents(context.TODO(), "1.23.6"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
initConfig := k.configProvider.InitConfiguration(in.SupportsCloudControllerManager)
|
nodeName := vpnIP
|
||||||
initConfig.SetApiServerAdvertiseAddress(in.APIServerAdvertiseIP)
|
var providerID string
|
||||||
initConfig.SetNodeIP(in.NodeIP)
|
var instance cloudtypes.Instance
|
||||||
initConfig.SetNodeName(in.NodeName)
|
var publicIP string
|
||||||
initConfig.SetPodNetworkCIDR(podNetworkCidr)
|
var nodePodCIDR string
|
||||||
initConfig.SetServiceCIDR(serviceCidr)
|
var subnetworkPodCIDR string
|
||||||
initConfig.SetProviderID(in.ProviderID)
|
// this is the IP in "kubeadm init --control-plane-endpoint=<IP/DNS>:<port>" hence the unfortunate name
|
||||||
|
var controlPlaneEndpointIP string
|
||||||
|
var nodeIP string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
||||||
|
if k.providerMetadata.Supported() {
|
||||||
|
instance, err = k.providerMetadata.Self(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("retrieving own instance metadata failed: %w", err)
|
||||||
|
}
|
||||||
|
nodeName = k8sCompliantHostname(instance.Name)
|
||||||
|
providerID = instance.ProviderID
|
||||||
|
if len(instance.PrivateIPs) > 0 {
|
||||||
|
nodeIP = instance.PrivateIPs[0]
|
||||||
|
}
|
||||||
|
if len(instance.PublicIPs) > 0 {
|
||||||
|
publicIP = instance.PublicIPs[0]
|
||||||
|
}
|
||||||
|
if len(instance.AliasIPRanges) > 0 {
|
||||||
|
nodePodCIDR = instance.AliasIPRanges[0]
|
||||||
|
}
|
||||||
|
subnetworkPodCIDR, err = k.providerMetadata.GetSubnetworkCIDR(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("retrieving subnetwork CIDR failed: %w", err)
|
||||||
|
}
|
||||||
|
controlPlaneEndpointIP = publicIP
|
||||||
|
if k.providerMetadata.SupportsLoadBalancer() {
|
||||||
|
controlPlaneEndpointIP, err = k.providerMetadata.GetLoadBalancerIP(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("retrieving load balancer IP failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: configure kubeadm init config
|
||||||
|
initConfig := k.configProvider.InitConfiguration(k.cloudControllerManager.Supported())
|
||||||
|
initConfig.SetNodeIP(nodeIP)
|
||||||
|
initConfig.SetCertSANs([]string{publicIP, nodeIP})
|
||||||
|
initConfig.SetNodeName(nodeName)
|
||||||
|
initConfig.SetProviderID(providerID)
|
||||||
|
initConfig.SetControlPlaneEndpoint(controlPlaneEndpointIP)
|
||||||
initConfigYAML, err := initConfig.Marshal()
|
initConfigYAML, err := initConfig.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encoding kubeadm init configuration as YAML failed: %w", err)
|
return fmt.Errorf("encoding kubeadm init configuration as YAML failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := k.clusterUtil.InitCluster(initConfigYAML); err != nil {
|
if err := k.clusterUtil.InitCluster(ctx, initConfigYAML); err != nil {
|
||||||
return fmt.Errorf("kubeadm init failed: %w", err)
|
return fmt.Errorf("kubeadm init failed: %w", err)
|
||||||
}
|
}
|
||||||
kubeConfig, err := k.GetKubeconfig()
|
kubeConfig, err := k.GetKubeconfig()
|
||||||
@ -74,54 +122,72 @@ func (k *KubeWrapper) InitCluster(in InitClusterInput) error {
|
|||||||
return fmt.Errorf("reading kubeconfig after cluster initialization failed: %w", err)
|
return fmt.Errorf("reading kubeconfig after cluster initialization failed: %w", err)
|
||||||
}
|
}
|
||||||
k.client.SetKubeconfig(kubeConfig)
|
k.client.SetKubeconfig(kubeConfig)
|
||||||
flannel := resources.NewDefaultFlannelDeployment()
|
|
||||||
if err = k.clusterUtil.SetupPodNetwork(k.client, flannel); err != nil {
|
// Step 3: configure & start kubernetes controllers
|
||||||
|
|
||||||
|
setupPodNetworkInput := k8sapi.SetupPodNetworkInput{
|
||||||
|
CloudProvider: k.cloudProvider,
|
||||||
|
NodeName: nodeName,
|
||||||
|
FirstNodePodCIDR: nodePodCIDR,
|
||||||
|
SubnetworkPodCIDR: subnetworkPodCIDR,
|
||||||
|
ProviderID: providerID,
|
||||||
|
}
|
||||||
|
if err = k.clusterUtil.SetupPodNetwork(ctx, setupPodNetworkInput); err != nil {
|
||||||
return fmt.Errorf("setup of pod network failed: %w", err)
|
return fmt.Errorf("setup of pod network failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
kms := resources.NewKMSDeployment(in.MasterSecret)
|
kms := resources.NewKMSDeployment(masterSecret)
|
||||||
if err = k.clusterUtil.SetupKMS(k.client, kms); err != nil {
|
if err = k.clusterUtil.SetupKMS(k.client, kms); err != nil {
|
||||||
return fmt.Errorf("setup of kms failed: %w", err)
|
return fmt.Errorf("setup of kms failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.SupportsCloudControllerManager {
|
if err := k.setupCCM(context.TODO(), vpnIP, subnetworkPodCIDR, cloudServiceAccountURI, instance); err != nil {
|
||||||
cloudControllerManagerConfiguration := resources.NewDefaultCloudControllerManagerDeployment(
|
return fmt.Errorf("setting up cloud controller manager failed: %w", err)
|
||||||
in.CloudControllerManagerName, in.CloudControllerManagerImage, in.CloudControllerManagerPath, in.CloudControllerManagerExtraArgs,
|
}
|
||||||
in.CloudControllerManagerVolumes, in.CloudControllerManagerVolumeMounts, in.CloudControllerManagerEnv,
|
if err := k.setupCloudNodeManager(); err != nil {
|
||||||
)
|
return fmt.Errorf("setting up cloud node manager failed: %w", err)
|
||||||
if err := k.clusterUtil.SetupCloudControllerManager(k.client, cloudControllerManagerConfiguration, in.CloudControllerManagerConfigMaps, in.CloudControllerManagerSecrets); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup cloud-controller-manager: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.SupportsCloudNodeManager {
|
if err := k.setupClusterAutoscaler(instance, cloudServiceAccountURI, autoscalingNodeGroups); err != nil {
|
||||||
cloudNodeManagerConfiguration := resources.NewDefaultCloudNodeManagerDeployment(
|
return fmt.Errorf("setting up cluster autoscaler failed: %w", err)
|
||||||
in.CloudNodeManagerImage, in.CloudNodeManagerPath, in.CloudNodeManagerExtraArgs,
|
|
||||||
)
|
|
||||||
if err := k.clusterUtil.SetupCloudNodeManager(k.client, cloudNodeManagerConfiguration); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup cloud-node-manager: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.SupportClusterAutoscaler {
|
|
||||||
clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(in.AutoscalingVolumes, in.AutoscalingVolumeMounts, in.AutoscalingEnv)
|
|
||||||
clusterAutoscalerConfiguration.SetAutoscalerCommand(in.AutoscalingCloudprovider, in.AutoscalingNodeGroups)
|
|
||||||
if err := k.clusterUtil.SetupAutoscaling(k.client, clusterAutoscalerConfiguration, in.AutoscalingSecrets); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup cluster-autoscaler: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinCluster joins existing Kubernetes cluster.
|
// JoinCluster joins existing Kubernetes cluster.
|
||||||
func (k *KubeWrapper) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeInternalIP, nodeVPNIP, providerID, certKey string, ccmSupported bool, peerRole role.Role) error {
|
func (k *KubeWrapper) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, nodeVPNIP, certKey string, peerRole role.Role) error {
|
||||||
// TODO: k8s version should be user input
|
// TODO: k8s version should be user input
|
||||||
if err := k.clusterUtil.InstallComponents(context.TODO(), "1.23.6"); err != nil {
|
if err := k.clusterUtil.InstallComponents(context.TODO(), "1.23.6"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
joinConfig := k.configProvider.JoinConfiguration(ccmSupported)
|
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
||||||
|
var providerID string
|
||||||
|
nodeName := nodeVPNIP
|
||||||
|
nodeInternalIP := nodeVPNIP
|
||||||
|
if k.providerMetadata.Supported() {
|
||||||
|
instance, err := k.providerMetadata.Self(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("retrieving own instance metadata failed: %w", err)
|
||||||
|
}
|
||||||
|
providerID = instance.ProviderID
|
||||||
|
nodeName = instance.Name
|
||||||
|
if len(instance.PrivateIPs) > 0 {
|
||||||
|
nodeInternalIP = instance.PrivateIPs[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodeName = k8sCompliantHostname(nodeName)
|
||||||
|
|
||||||
|
if k.cloudControllerManager.Supported() && k.providerMetadata.Supported() {
|
||||||
|
if err := k.prepareInstanceForCCM(context.TODO(), nodeVPNIP); err != nil {
|
||||||
|
return fmt.Errorf("preparing node for CCM failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: configure kubeadm join config
|
||||||
|
|
||||||
|
joinConfig := k.configProvider.JoinConfiguration(k.cloudControllerManager.Supported())
|
||||||
joinConfig.SetApiServerEndpoint(args.APIServerEndpoint)
|
joinConfig.SetApiServerEndpoint(args.APIServerEndpoint)
|
||||||
joinConfig.SetToken(args.Token)
|
joinConfig.SetToken(args.Token)
|
||||||
joinConfig.AppendDiscoveryTokenCaCertHash(args.CACertHashes[0])
|
joinConfig.AppendDiscoveryTokenCaCertHash(args.CACertHashes[0])
|
||||||
@ -129,13 +195,13 @@ func (k *KubeWrapper) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeNam
|
|||||||
joinConfig.SetNodeName(nodeName)
|
joinConfig.SetNodeName(nodeName)
|
||||||
joinConfig.SetProviderID(providerID)
|
joinConfig.SetProviderID(providerID)
|
||||||
if peerRole == role.Coordinator {
|
if peerRole == role.Coordinator {
|
||||||
joinConfig.SetControlPlane(nodeVPNIP, certKey)
|
joinConfig.SetControlPlane(nodeInternalIP, certKey)
|
||||||
}
|
}
|
||||||
joinConfigYAML, err := joinConfig.Marshal()
|
joinConfigYAML, err := joinConfig.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encoding kubeadm join configuration as YAML failed: %w", err)
|
return fmt.Errorf("encoding kubeadm join configuration as YAML failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := k.clusterUtil.JoinCluster(joinConfigYAML); err != nil {
|
if err := k.clusterUtil.JoinCluster(ctx, joinConfigYAML); err != nil {
|
||||||
return fmt.Errorf("joining cluster failed: %v %w ", string(joinConfigYAML), err)
|
return fmt.Errorf("joining cluster failed: %v %w ", string(joinConfigYAML), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,13 +222,89 @@ func (k *KubeWrapper) GetKubeconfig() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetKubeadmCertificateKey return the key needed to join the Cluster as Control-Plane (has to be executed on a control-plane; errors otherwise).
|
// GetKubeadmCertificateKey return the key needed to join the Cluster as Control-Plane (has to be executed on a control-plane; errors otherwise).
|
||||||
func (k *KubeWrapper) GetKubeadmCertificateKey() (string, error) {
|
func (k *KubeWrapper) GetKubeadmCertificateKey(ctx context.Context) (string, error) {
|
||||||
return k.clusterUtil.GetControlPlaneJoinCertificateKey()
|
return k.clusterUtil.GetControlPlaneJoinCertificateKey(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetJoinToken returns a bootstrap (join) token.
|
// GetJoinToken returns a bootstrap (join) token.
|
||||||
func (k *KubeWrapper) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (k *KubeWrapper) GetJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return k.clusterUtil.CreateJoinToken(ttl)
|
return k.clusterUtil.CreateJoinToken(ctx, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubeWrapper) setupCCM(ctx context.Context, vpnIP, subnetworkPodCIDR, cloudServiceAccountURI string, instance cloudtypes.Instance) error {
|
||||||
|
if !k.cloudControllerManager.Supported() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := k.prepareInstanceForCCM(context.TODO(), vpnIP); err != nil {
|
||||||
|
return fmt.Errorf("preparing node for CCM failed: %w", err)
|
||||||
|
}
|
||||||
|
ccmConfigMaps, err := k.cloudControllerManager.ConfigMaps(instance)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("defining ConfigMaps for CCM failed: %w", err)
|
||||||
|
}
|
||||||
|
ccmSecrets, err := k.cloudControllerManager.Secrets(ctx, instance, cloudServiceAccountURI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("defining Secrets for CCM failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudControllerManagerConfiguration := resources.NewDefaultCloudControllerManagerDeployment(
|
||||||
|
k.cloudControllerManager.Name(), k.cloudControllerManager.Image(), k.cloudControllerManager.Path(), subnetworkPodCIDR,
|
||||||
|
k.cloudControllerManager.ExtraArgs(), k.cloudControllerManager.Volumes(), k.cloudControllerManager.VolumeMounts(), k.cloudControllerManager.Env(),
|
||||||
|
)
|
||||||
|
if err := k.clusterUtil.SetupCloudControllerManager(k.client, cloudControllerManagerConfiguration, ccmConfigMaps, ccmSecrets); err != nil {
|
||||||
|
return fmt.Errorf("failed to setup cloud-controller-manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubeWrapper) setupCloudNodeManager() error {
|
||||||
|
if !k.cloudNodeManager.Supported() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cloudNodeManagerConfiguration := resources.NewDefaultCloudNodeManagerDeployment(
|
||||||
|
k.cloudNodeManager.Image(), k.cloudNodeManager.Path(), k.cloudNodeManager.ExtraArgs(),
|
||||||
|
)
|
||||||
|
if err := k.clusterUtil.SetupCloudNodeManager(k.client, cloudNodeManagerConfiguration); err != nil {
|
||||||
|
return fmt.Errorf("failed to setup cloud-node-manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubeWrapper) setupClusterAutoscaler(instance cloudtypes.Instance, cloudServiceAccountURI string, autoscalingNodeGroups []string) error {
|
||||||
|
if !k.clusterAutoscaler.Supported() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
caSecrets, err := k.clusterAutoscaler.Secrets(instance, cloudServiceAccountURI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("defining Secrets for cluster-autoscaler failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(k.clusterAutoscaler.Volumes(), k.clusterAutoscaler.VolumeMounts(), k.clusterAutoscaler.Env())
|
||||||
|
clusterAutoscalerConfiguration.SetAutoscalerCommand(k.clusterAutoscaler.Name(), autoscalingNodeGroups)
|
||||||
|
if err := k.clusterUtil.SetupAutoscaling(k.client, clusterAutoscalerConfiguration, caSecrets); err != nil {
|
||||||
|
return fmt.Errorf("failed to setup cluster-autoscaler: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareInstanceForCCM sets the vpn IP in cloud provider metadata.
|
||||||
|
func (k *KubeWrapper) prepareInstanceForCCM(ctx context.Context, vpnIP string) error {
|
||||||
|
if err := k.providerMetadata.SetVPNIP(ctx, vpnIP); err != nil {
|
||||||
|
return fmt.Errorf("setting VPN IP for cloud-controller-manager failed: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// k8sCompliantHostname transforms a hostname to an RFC 1123 compliant, lowercase subdomain as required by Kubernetes node names.
|
||||||
|
// The following regex is used by k8s for validation: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/ .
|
||||||
|
// Only a simple heuristic is used for now (to lowercase, replace underscores).
|
||||||
|
func k8sCompliantHostname(in string) string {
|
||||||
|
hostname := strings.ToLower(in)
|
||||||
|
hostname = strings.ReplaceAll(hostname, "_", "-")
|
||||||
|
return hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartKubelet starts the kubelet service.
|
// StartKubelet starts the kubelet service.
|
||||||
|
@ -3,9 +3,11 @@ package kubernetes
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
|
||||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
@ -13,13 +15,484 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
goleak.VerifyTestMain(m)
|
goleak.VerifyTestMain(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInitCluster(t *testing.T) {
|
||||||
|
someErr := errors.New("failed")
|
||||||
|
coordinatorVPNIP := "192.0.2.0"
|
||||||
|
serviceAccountUri := "some-service-account-uri"
|
||||||
|
masterSecret := []byte("some-master-secret")
|
||||||
|
autoscalingNodeGroups := []string{"0,10,autoscaling_group_0"}
|
||||||
|
|
||||||
|
nodeName := "node-name"
|
||||||
|
providerID := "provider-id"
|
||||||
|
privateIP := "192.0.2.1"
|
||||||
|
publicIP := "192.0.2.2"
|
||||||
|
loadbalancerIP := "192.0.2.3"
|
||||||
|
aliasIPRange := "192.0.2.0/24"
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
clusterUtil stubClusterUtil
|
||||||
|
kubeCTL stubKubeCTL
|
||||||
|
providerMetadata ProviderMetadata
|
||||||
|
CloudControllerManager CloudControllerManager
|
||||||
|
CloudNodeManager CloudNodeManager
|
||||||
|
ClusterAutoscaler ClusterAutoscaler
|
||||||
|
kubeconfigReader configReader
|
||||||
|
wantConfig k8sapi.KubeadmInitYAML
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"kubeadm init works without metadata": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{SupportedResp: false},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{SupportedResp: false},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantConfig: k8sapi.KubeadmInitYAML{
|
||||||
|
InitConfiguration: kubeadm.InitConfiguration{
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
KubeletExtraArgs: map[string]string{
|
||||||
|
"node-ip": "",
|
||||||
|
"provider-id": "",
|
||||||
|
},
|
||||||
|
Name: coordinatorVPNIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClusterConfiguration: kubeadm.ClusterConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kubeadm init works with metadata and loadbalancer": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfResp: cloudtypes.Instance{
|
||||||
|
Name: nodeName,
|
||||||
|
ProviderID: providerID,
|
||||||
|
PrivateIPs: []string{privateIP},
|
||||||
|
PublicIPs: []string{publicIP},
|
||||||
|
AliasIPRanges: []string{aliasIPRange},
|
||||||
|
},
|
||||||
|
GetLoadBalancerIPResp: loadbalancerIP,
|
||||||
|
SupportsLoadBalancerResp: true,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{SupportedResp: false},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantConfig: k8sapi.KubeadmInitYAML{
|
||||||
|
InitConfiguration: kubeadm.InitConfiguration{
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
KubeletExtraArgs: map[string]string{
|
||||||
|
"node-ip": privateIP,
|
||||||
|
"provider-id": providerID,
|
||||||
|
},
|
||||||
|
Name: nodeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClusterConfiguration: kubeadm.ClusterConfiguration{
|
||||||
|
ControlPlaneEndpoint: loadbalancerIP,
|
||||||
|
APIServer: kubeadm.APIServer{
|
||||||
|
CertSANs: []string{publicIP, privateIP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when retrieving metadata self": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SelfErr: someErr,
|
||||||
|
SupportedResp: true,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when retrieving metadata subnetwork cidr": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
GetSubnetworkCIDRErr: someErr,
|
||||||
|
SupportedResp: true,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when retrieving metadata loadbalancer ip": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
GetLoadBalancerIPErr: someErr,
|
||||||
|
SupportsLoadBalancerResp: true,
|
||||||
|
SupportedResp: true,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when applying the init config": {
|
||||||
|
clusterUtil: stubClusterUtil{initClusterErr: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when setting up the pod network": {
|
||||||
|
clusterUtil: stubClusterUtil{setupPodNetworkErr: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when setting the cloud contoller manager": {
|
||||||
|
clusterUtil: stubClusterUtil{setupCloudControllerManagerError: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{SupportedResp: true},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when setting the cloud node manager": {
|
||||||
|
clusterUtil: stubClusterUtil{setupCloudNodeManagerError: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{SupportedResp: true},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when setting the cluster autoscaler": {
|
||||||
|
clusterUtil: stubClusterUtil{setupAutoscalingError: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{SupportedResp: true},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when reading kubeconfig": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
ReadErr: someErr,
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm init fails when setting up the kms": {
|
||||||
|
clusterUtil: stubClusterUtil{setupKMSError: someErr},
|
||||||
|
kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("someKubeconfig"),
|
||||||
|
},
|
||||||
|
providerMetadata: &stubProviderMetadata{SupportedResp: false},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
CloudNodeManager: &stubCloudNodeManager{SupportedResp: false},
|
||||||
|
ClusterAutoscaler: &stubClusterAutoscaler{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
kube := KubeWrapper{
|
||||||
|
clusterUtil: &tc.clusterUtil,
|
||||||
|
providerMetadata: tc.providerMetadata,
|
||||||
|
cloudControllerManager: tc.CloudControllerManager,
|
||||||
|
cloudNodeManager: tc.CloudNodeManager,
|
||||||
|
clusterAutoscaler: tc.ClusterAutoscaler,
|
||||||
|
configProvider: &stubConfigProvider{InitConfig: k8sapi.KubeadmInitYAML{}},
|
||||||
|
client: &tc.kubeCTL,
|
||||||
|
kubeconfigReader: tc.kubeconfigReader,
|
||||||
|
}
|
||||||
|
err := kube.InitCluster(context.Background(), autoscalingNodeGroups, serviceAccountUri, coordinatorVPNIP, masterSecret)
|
||||||
|
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
var kubeadmConfig k8sapi.KubeadmInitYAML
|
||||||
|
require.NoError(resources.UnmarshalK8SResources(tc.clusterUtil.initConfigs[0], &kubeadmConfig))
|
||||||
|
require.Equal(tc.wantConfig.ClusterConfiguration, kubeadmConfig.ClusterConfiguration)
|
||||||
|
require.Equal(tc.wantConfig.InitConfiguration, kubeadmConfig.InitConfiguration)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJoinCluster(t *testing.T) {
|
||||||
|
someErr := errors.New("failed")
|
||||||
|
joinCommand := &kubeadm.BootstrapTokenDiscovery{
|
||||||
|
APIServerEndpoint: "192.0.2.0:6443",
|
||||||
|
Token: "kube-fake-token",
|
||||||
|
CACertHashes: []string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"},
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeVPNIP := "192.0.2.0"
|
||||||
|
certKey := "cert-key"
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
clusterUtil stubClusterUtil
|
||||||
|
providerMetadata ProviderMetadata
|
||||||
|
CloudControllerManager CloudControllerManager
|
||||||
|
wantConfig kubeadm.JoinConfiguration
|
||||||
|
role role.Role
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"kubeadm join worker works without metadata": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
role: role.Node,
|
||||||
|
wantConfig: kubeadm.JoinConfiguration{
|
||||||
|
Discovery: kubeadm.Discovery{
|
||||||
|
BootstrapToken: joinCommand,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
Name: nodeVPNIP,
|
||||||
|
KubeletExtraArgs: map[string]string{"node-ip": "192.0.2.0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kubeadm join worker works with metadata": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfResp: cloudtypes.Instance{
|
||||||
|
ProviderID: "provider-id",
|
||||||
|
Name: "metadata-name",
|
||||||
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
role: role.Node,
|
||||||
|
wantConfig: kubeadm.JoinConfiguration{
|
||||||
|
Discovery: kubeadm.Discovery{
|
||||||
|
BootstrapToken: joinCommand,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
Name: "metadata-name",
|
||||||
|
KubeletExtraArgs: map[string]string{"node-ip": "192.0.2.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kubeadm join worker works with metadata and cloud controller manager": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfResp: cloudtypes.Instance{
|
||||||
|
ProviderID: "provider-id",
|
||||||
|
Name: "metadata-name",
|
||||||
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{
|
||||||
|
SupportedResp: true,
|
||||||
|
},
|
||||||
|
role: role.Node,
|
||||||
|
wantConfig: kubeadm.JoinConfiguration{
|
||||||
|
Discovery: kubeadm.Discovery{
|
||||||
|
BootstrapToken: joinCommand,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
Name: "metadata-name",
|
||||||
|
KubeletExtraArgs: map[string]string{"node-ip": "192.0.2.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kubeadm join control-plane node works with metadata": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfResp: cloudtypes.Instance{
|
||||||
|
ProviderID: "provider-id",
|
||||||
|
Name: "metadata-name",
|
||||||
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
role: role.Coordinator,
|
||||||
|
wantConfig: kubeadm.JoinConfiguration{
|
||||||
|
Discovery: kubeadm.Discovery{
|
||||||
|
BootstrapToken: joinCommand,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||||
|
Name: "metadata-name",
|
||||||
|
KubeletExtraArgs: map[string]string{"node-ip": "192.0.2.1"},
|
||||||
|
},
|
||||||
|
ControlPlane: &kubeadm.JoinControlPlane{
|
||||||
|
LocalAPIEndpoint: kubeadm.APIEndpoint{
|
||||||
|
AdvertiseAddress: "192.0.2.1",
|
||||||
|
BindPort: 6443,
|
||||||
|
},
|
||||||
|
CertificateKey: certKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kubeadm join worker fails when retrieving self metadata": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfErr: someErr,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
role: role.Node,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm join worker fails when applying the join config": {
|
||||||
|
clusterUtil: stubClusterUtil{joinClusterErr: someErr},
|
||||||
|
providerMetadata: &stubProviderMetadata{},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{},
|
||||||
|
role: role.Node,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"kubeadm join worker works fails when setting the metadata for the cloud controller manager": {
|
||||||
|
clusterUtil: stubClusterUtil{},
|
||||||
|
providerMetadata: &stubProviderMetadata{
|
||||||
|
SupportedResp: true,
|
||||||
|
SelfResp: cloudtypes.Instance{
|
||||||
|
ProviderID: "provider-id",
|
||||||
|
Name: "metadata-name",
|
||||||
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
|
},
|
||||||
|
SetVPNIPErr: someErr,
|
||||||
|
},
|
||||||
|
CloudControllerManager: &stubCloudControllerManager{
|
||||||
|
SupportedResp: true,
|
||||||
|
},
|
||||||
|
role: role.Node,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
kube := KubeWrapper{
|
||||||
|
clusterUtil: &tc.clusterUtil,
|
||||||
|
providerMetadata: tc.providerMetadata,
|
||||||
|
cloudControllerManager: tc.CloudControllerManager,
|
||||||
|
configProvider: &stubConfigProvider{},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := kube.JoinCluster(context.Background(), joinCommand, nodeVPNIP, certKey, tc.role)
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
var joinYaml k8sapi.KubeadmJoinYAML
|
||||||
|
joinYaml, err = joinYaml.Unmarshal(tc.clusterUtil.joinConfigs[0])
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
assert.Equal(tc.wantConfig, joinYaml.JoinConfiguration)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetKubeconfig(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
Kubewrapper KubeWrapper
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"check single replacement": {
|
||||||
|
Kubewrapper: KubeWrapper{kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("127.0.0.1:16443"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
"check multiple replacement": {
|
||||||
|
Kubewrapper: KubeWrapper{kubeconfigReader: &stubKubeconfigReader{
|
||||||
|
Kubeconfig: []byte("127.0.0.1:16443...127.0.0.1:16443"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
data, err := tc.Kubewrapper.GetKubeconfig()
|
||||||
|
require.NoError(err)
|
||||||
|
assert.NotContains(string(data), "127.0.0.1:16443")
|
||||||
|
assert.Contains(string(data), "10.118.0.1:6443")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestK8sCompliantHostname(t *testing.T) {
|
||||||
|
compliantHostname := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
||||||
|
testCases := map[string]struct {
|
||||||
|
hostname string
|
||||||
|
wantHostname string
|
||||||
|
}{
|
||||||
|
"azure scale set names work": {
|
||||||
|
hostname: "constellation-scale-set-coordinators-name_0",
|
||||||
|
wantHostname: "constellation-scale-set-coordinators-name-0",
|
||||||
|
},
|
||||||
|
"compliant hostname is not modified": {
|
||||||
|
hostname: "abcd-123",
|
||||||
|
wantHostname: "abcd-123",
|
||||||
|
},
|
||||||
|
"uppercase hostnames are lowercased": {
|
||||||
|
hostname: "ABCD",
|
||||||
|
wantHostname: "abcd",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
hostname := k8sCompliantHostname(tc.hostname)
|
||||||
|
|
||||||
|
assert.Equal(tc.wantHostname, hostname)
|
||||||
|
assert.Regexp(compliantHostname, hostname)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type stubClusterUtil struct {
|
type stubClusterUtil struct {
|
||||||
installComponentsErr error
|
installComponentsErr error
|
||||||
initClusterErr error
|
initClusterErr error
|
||||||
@ -42,12 +515,12 @@ func (s *stubClusterUtil) InstallComponents(ctx context.Context, version string)
|
|||||||
return s.installComponentsErr
|
return s.installComponentsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubClusterUtil) InitCluster(initConfig []byte) error {
|
func (s *stubClusterUtil) InitCluster(ctx context.Context, initConfig []byte) error {
|
||||||
s.initConfigs = append(s.initConfigs, initConfig)
|
s.initConfigs = append(s.initConfigs, initConfig)
|
||||||
return s.initClusterErr
|
return s.initClusterErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubClusterUtil) SetupPodNetwork(kubectl k8sapi.Client, podNetworkConfiguration resources.Marshaler) error {
|
func (s *stubClusterUtil) SetupPodNetwork(context.Context, k8sapi.SetupPodNetworkInput) error {
|
||||||
return s.setupPodNetworkErr
|
return s.setupPodNetworkErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +540,7 @@ func (s *stubClusterUtil) SetupCloudNodeManager(kubectl k8sapi.Client, cloudNode
|
|||||||
return s.setupCloudNodeManagerError
|
return s.setupCloudNodeManagerError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubClusterUtil) JoinCluster(joinConfig []byte) error {
|
func (s *stubClusterUtil) JoinCluster(ctx context.Context, joinConfig []byte) error {
|
||||||
s.joinConfigs = append(s.joinConfigs, joinConfig)
|
s.joinConfigs = append(s.joinConfigs, joinConfig)
|
||||||
return s.joinClusterErr
|
return s.joinClusterErr
|
||||||
}
|
}
|
||||||
@ -80,11 +553,11 @@ func (s *stubClusterUtil) RestartKubelet() error {
|
|||||||
return s.restartKubeletErr
|
return s.restartKubeletErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubClusterUtil) GetControlPlaneJoinCertificateKey() (string, error) {
|
func (s *stubClusterUtil) GetControlPlaneJoinCertificateKey(context.Context) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubClusterUtil) CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (s *stubClusterUtil) CreateJoinToken(ctx context.Context, ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return s.createJoinTokenResponse, s.createJoinTokenErr
|
return s.createJoinTokenResponse, s.createJoinTokenErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,163 +605,3 @@ type stubKubeconfigReader struct {
|
|||||||
func (s *stubKubeconfigReader) ReadKubeconfig() ([]byte, error) {
|
func (s *stubKubeconfigReader) ReadKubeconfig() ([]byte, error) {
|
||||||
return s.Kubeconfig, s.ReadErr
|
return s.Kubeconfig, s.ReadErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitCluster(t *testing.T) {
|
|
||||||
someErr := errors.New("failed")
|
|
||||||
coordinatorVPNIP := "192.0.2.0"
|
|
||||||
coordinatorProviderID := "somecloudprovider://instance-id"
|
|
||||||
instanceName := "instance-id"
|
|
||||||
supportsClusterAutoscaler := false
|
|
||||||
cloudprovider := "some-cloudprovider"
|
|
||||||
cloudControllerManagerImage := "some-image:latest"
|
|
||||||
cloudControllerManagerPath := "/some_path"
|
|
||||||
autoscalingNodeGroups := []string{"0,10,autoscaling_group_0"}
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
clusterUtil stubClusterUtil
|
|
||||||
kubeCTL stubKubeCTL
|
|
||||||
kubeconfigReader stubKubeconfigReader
|
|
||||||
initConfig k8sapi.KubeadmInitYAML
|
|
||||||
joinConfig k8sapi.KubeadmJoinYAML
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"kubeadm init works": {
|
|
||||||
clusterUtil: stubClusterUtil{},
|
|
||||||
kubeconfigReader: stubKubeconfigReader{
|
|
||||||
Kubeconfig: []byte("someKubeconfig"),
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"kubeadm init errors": {
|
|
||||||
clusterUtil: stubClusterUtil{initClusterErr: someErr},
|
|
||||||
kubeconfigReader: stubKubeconfigReader{
|
|
||||||
Kubeconfig: []byte("someKubeconfig"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"pod network setup errors": {
|
|
||||||
clusterUtil: stubClusterUtil{setupPodNetworkErr: someErr},
|
|
||||||
kubeconfigReader: stubKubeconfigReader{
|
|
||||||
Kubeconfig: []byte("someKubeconfig"),
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
kube := KubeWrapper{
|
|
||||||
clusterUtil: &tc.clusterUtil,
|
|
||||||
configProvider: &stubConfigProvider{InitConfig: k8sapi.KubeadmInitYAML{}},
|
|
||||||
client: &tc.kubeCTL,
|
|
||||||
kubeconfigReader: &tc.kubeconfigReader,
|
|
||||||
}
|
|
||||||
err := kube.InitCluster(
|
|
||||||
InitClusterInput{
|
|
||||||
APIServerAdvertiseIP: coordinatorVPNIP,
|
|
||||||
NodeName: instanceName,
|
|
||||||
ProviderID: coordinatorProviderID,
|
|
||||||
SupportClusterAutoscaler: supportsClusterAutoscaler,
|
|
||||||
AutoscalingCloudprovider: cloudprovider,
|
|
||||||
AutoscalingNodeGroups: autoscalingNodeGroups,
|
|
||||||
CloudControllerManagerName: cloudprovider,
|
|
||||||
CloudControllerManagerImage: cloudControllerManagerImage,
|
|
||||||
CloudControllerManagerPath: cloudControllerManagerPath,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
var kubeadmConfig k8sapi.KubeadmInitYAML
|
|
||||||
require.NoError(resources.UnmarshalK8SResources(tc.clusterUtil.initConfigs[0], &kubeadmConfig))
|
|
||||||
assert.Equal(kubeadmConfig.InitConfiguration.LocalAPIEndpoint.AdvertiseAddress, "192.0.2.0")
|
|
||||||
assert.Equal(kubeadmConfig.ClusterConfiguration.Networking.PodSubnet, "10.244.0.0/16")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJoinCluster(t *testing.T) {
|
|
||||||
someErr := errors.New("failed")
|
|
||||||
joinCommand := &kubeadm.BootstrapTokenDiscovery{
|
|
||||||
APIServerEndpoint: "192.0.2.0:6443",
|
|
||||||
Token: "kube-fake-token",
|
|
||||||
CACertHashes: []string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"},
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeVPNIP := "192.0.2.0"
|
|
||||||
coordinatorProviderID := "somecloudprovider://instance-id"
|
|
||||||
instanceName := "instance-id"
|
|
||||||
client := fakeK8SClient{}
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
clusterUtil stubClusterUtil
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"kubeadm join works": {
|
|
||||||
clusterUtil: stubClusterUtil{},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
"kubeadm join errors": {
|
|
||||||
clusterUtil: stubClusterUtil{joinClusterErr: someErr},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
kube := New(&tc.clusterUtil, &stubConfigProvider{}, &client)
|
|
||||||
err := kube.JoinCluster(joinCommand, instanceName, nodeVPNIP, nodeVPNIP, coordinatorProviderID, "", true, role.Node)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
var joinConfig kubeadm.JoinConfiguration
|
|
||||||
require.NoError(yaml.Unmarshal(tc.clusterUtil.joinConfigs[0], &joinConfig))
|
|
||||||
|
|
||||||
assert.Equal("192.0.2.0:6443", joinConfig.Discovery.BootstrapToken.APIServerEndpoint)
|
|
||||||
assert.Equal("kube-fake-token", joinConfig.Discovery.BootstrapToken.Token)
|
|
||||||
assert.Equal([]string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"}, joinConfig.Discovery.BootstrapToken.CACertHashes)
|
|
||||||
assert.Equal(map[string]string{"node-ip": "192.0.2.0"}, joinConfig.NodeRegistration.KubeletExtraArgs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetKubeconfig(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
Kubewrapper KubeWrapper
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"check single replacement": {
|
|
||||||
Kubewrapper: KubeWrapper{kubeconfigReader: &stubKubeconfigReader{
|
|
||||||
Kubeconfig: []byte("127.0.0.1:16443"),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
"check multiple replacement": {
|
|
||||||
Kubewrapper: KubeWrapper{kubeconfigReader: &stubKubeconfigReader{
|
|
||||||
Kubeconfig: []byte("127.0.0.1:16443...127.0.0.1:16443"),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
data, err := tc.Kubewrapper.GetKubeconfig()
|
|
||||||
require.NoError(err)
|
|
||||||
assert.NotContains(string(data), "127.0.0.1:16443")
|
|
||||||
assert.Contains(string(data), "10.118.0.1:6443")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -99,7 +99,7 @@ func (a *API) ActivateAsCoordinator(in *pubproto.ActivateAsCoordinatorRequest, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
logToCLI("Initializing Kubernetes ...")
|
logToCLI("Initializing Kubernetes ...")
|
||||||
kubeconfig, err := a.core.InitCluster(in.AutoscalingNodeGroups, in.CloudServiceAccountUri, in.MasterSecret)
|
kubeconfig, err := a.core.InitCluster(context.TODO(), in.AutoscalingNodeGroups, in.CloudServiceAccountUri, in.MasterSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(codes.Internal, "initializing Kubernetes cluster failed: %v", err)
|
return status.Errorf(codes.Internal, "initializing Kubernetes cluster failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,6 @@ type Core interface {
|
|||||||
|
|
||||||
CreateSSHUsers([]ssh.UserKey) error
|
CreateSSHUsers([]ssh.UserKey) error
|
||||||
|
|
||||||
InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error)
|
InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error)
|
||||||
JoinCluster(joinToken *kubeadm.BootstrapTokenDiscovery, certificateKey string, role role.Role) error
|
JoinCluster(ctx context.Context, joinToken *kubeadm.BootstrapTokenDiscovery, certificateKey string, role role.Role) error
|
||||||
}
|
}
|
||||||
|
@ -122,12 +122,12 @@ func (c *fakeCore) UpdatePeers(peers []peer.Peer) error {
|
|||||||
return c.UpdatePeersErr
|
return c.UpdatePeersErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeCore) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error) {
|
func (c *fakeCore) InitCluster(ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI string, masterSecret []byte) ([]byte, error) {
|
||||||
c.autoscalingNodeGroups = autoscalingNodeGroups
|
c.autoscalingNodeGroups = autoscalingNodeGroups
|
||||||
return c.kubeconfig, nil
|
return c.kubeconfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeCore) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, _ string, _ role.Role) error {
|
func (c *fakeCore) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, _ string, _ role.Role) error {
|
||||||
c.joinArgs = append(c.joinArgs, *args)
|
c.joinArgs = append(c.joinArgs, *args)
|
||||||
return c.joinClusterErr
|
return c.joinClusterErr
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func (a *API) ActivateAsAdditionalCoordinator(ctx context.Context, in *pubproto.
|
|||||||
return nil, status.Errorf(codes.Internal, "add peers to vpn: %v", err)
|
return nil, status.Errorf(codes.Internal, "add peers to vpn: %v", err)
|
||||||
}
|
}
|
||||||
a.logger.Info("about to join the k8s cluster")
|
a.logger.Info("about to join the k8s cluster")
|
||||||
err = a.core.JoinCluster(joinArgs, certKey, role.Coordinator)
|
err = a.core.JoinCluster(context.TODO(), joinArgs, certKey, role.Coordinator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "%v", err)
|
return nil, status.Errorf(codes.Internal, "%v", err)
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ func (a *API) JoinCluster(ctx context.Context, in *pubproto.JoinClusterRequest)
|
|||||||
return nil, status.Errorf(codes.Internal, "request K8s join string: %v", err)
|
return nil, status.Errorf(codes.Internal, "request K8s join string: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.core.JoinCluster(&kubeadm.BootstrapTokenDiscovery{
|
err = a.core.JoinCluster(context.TODO(), &kubeadm.BootstrapTokenDiscovery{
|
||||||
APIServerEndpoint: resp.ApiServerEndpoint,
|
APIServerEndpoint: resp.ApiServerEndpoint,
|
||||||
Token: resp.Token,
|
Token: resp.Token,
|
||||||
CACertHashes: []string{resp.DiscoveryTokenCaCertHash},
|
CACertHashes: []string{resp.DiscoveryTokenCaCertHash},
|
||||||
|
@ -47,7 +47,7 @@ func (a *API) GetUpdate(ctx context.Context, in *vpnproto.GetUpdateRequest) (*vp
|
|||||||
|
|
||||||
// GetK8SJoinArgs is the RPC call to get the K8s join args.
|
// GetK8SJoinArgs is the RPC call to get the K8s join args.
|
||||||
func (a *API) GetK8SJoinArgs(ctx context.Context, in *vpnproto.GetK8SJoinArgsRequest) (*vpnproto.GetK8SJoinArgsResponse, error) {
|
func (a *API) GetK8SJoinArgs(ctx context.Context, in *vpnproto.GetK8SJoinArgsRequest) (*vpnproto.GetK8SJoinArgsResponse, error) {
|
||||||
args, err := a.core.GetK8sJoinArgs()
|
args, err := a.core.GetK8sJoinArgs(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "%v", err)
|
return nil, status.Errorf(codes.Internal, "%v", err)
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ func (a *API) GetK8SJoinArgs(ctx context.Context, in *vpnproto.GetK8SJoinArgsReq
|
|||||||
|
|
||||||
// GetK8SCertificateKey is the RPC call to get the K8s certificateKey necessary for control-plane join.
|
// GetK8SCertificateKey is the RPC call to get the K8s certificateKey necessary for control-plane join.
|
||||||
func (a *API) GetK8SCertificateKey(ctx context.Context, in *vpnproto.GetK8SCertificateKeyRequest) (*vpnproto.GetK8SCertificateKeyResponse, error) {
|
func (a *API) GetK8SCertificateKey(ctx context.Context, in *vpnproto.GetK8SCertificateKeyRequest) (*vpnproto.GetK8SCertificateKeyResponse, error) {
|
||||||
certKey, err := a.core.GetK8SCertificateKey()
|
certKey, err := a.core.GetK8SCertificateKey(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "%v", err)
|
return nil, status.Errorf(codes.Internal, "%v", err)
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ func (a *API) GetDataKey(ctx context.Context, in *vpnproto.GetDataKeyRequest) (*
|
|||||||
type Core interface {
|
type Core interface {
|
||||||
GetPeers(resourceVersion int) (int, []peer.Peer, error)
|
GetPeers(resourceVersion int) (int, []peer.Peer, error)
|
||||||
NotifyNodeHeartbeat(net.Addr)
|
NotifyNodeHeartbeat(net.Addr)
|
||||||
GetK8sJoinArgs() (*kubeadm.BootstrapTokenDiscovery, error)
|
GetK8sJoinArgs(ctx context.Context) (*kubeadm.BootstrapTokenDiscovery, error)
|
||||||
GetK8SCertificateKey() (string, error)
|
GetK8SCertificateKey(ctx context.Context) (string, error)
|
||||||
GetDataKey(ctx context.Context, dataKeyID string, length int) ([]byte, error)
|
GetDataKey(ctx context.Context, dataKeyID string, length int) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
@ -202,11 +202,11 @@ func (c *stubCore) NotifyNodeHeartbeat(addr net.Addr) {
|
|||||||
c.heartbeats = append(c.heartbeats, addr)
|
c.heartbeats = append(c.heartbeats, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubCore) GetK8sJoinArgs() (*kubeadm.BootstrapTokenDiscovery, error) {
|
func (c *stubCore) GetK8sJoinArgs(context.Context) (*kubeadm.BootstrapTokenDiscovery, error) {
|
||||||
return &c.joinArgs, nil
|
return &c.joinArgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubCore) GetK8SCertificateKey() (string, error) {
|
func (c *stubCore) GetK8SCertificateKey(context.Context) (string, error) {
|
||||||
return c.kubeadmCertificateKey, c.getCertKeyErr
|
return c.kubeadmCertificateKey, c.getCertKeyErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,19 @@ func getIPsFromConfig(stat statec.ConstellationState, config configc.Config) ([]
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return append(coordinators.PublicIPs(), nodes.PublicIPs()...), nil
|
|
||||||
|
var ips []string
|
||||||
|
// only deploy to non empty public IPs
|
||||||
|
for _, ip := range append(coordinators.PublicIPs(), nodes.PublicIPs()...) {
|
||||||
|
if ip != "" {
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return nil, fmt.Errorf("no public IPs found in statefile")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,16 +5,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure"
|
azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
|
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
|
||||||
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type providerMetadata interface {
|
type providerMetadata interface {
|
||||||
// List retrieves all instances belonging to the current constellation.
|
// List retrieves all instances belonging to the current constellation.
|
||||||
List(ctx context.Context) ([]core.Instance, error)
|
List(ctx context.Context) ([]cloudtypes.Instance, error)
|
||||||
// Self retrieves the current instance.
|
// Self retrieves the current instance.
|
||||||
Self(ctx context.Context) (core.Instance, error)
|
Self(ctx context.Context) (cloudtypes.Instance, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetcher checks the metadata service to search for instances that were set up for debugging and cloud provider specific SSH keys.
|
// Fetcher checks the metadata service to search for instances that were set up for debugging and cloud provider specific SSH keys.
|
||||||
@ -66,7 +66,7 @@ func (f *Fetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
|
|||||||
}
|
}
|
||||||
var ips []string
|
var ips []string
|
||||||
for _, instance := range instances {
|
for _, instance := range instances {
|
||||||
ips = append(ips, instance.IPs...)
|
ips = append(ips, instance.PrivateIPs...)
|
||||||
}
|
}
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -21,15 +21,15 @@ func TestDiscoverDebugIPs(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"disovery works": {
|
"disovery works": {
|
||||||
meta: stubMetadata{
|
meta: stubMetadata{
|
||||||
listRes: []core.Instance{
|
listRes: []cloudtypes.Instance{
|
||||||
{
|
{
|
||||||
IPs: []string{"192.0.2.0"},
|
PrivateIPs: []string{"192.0.2.0"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
IPs: []string{"192.0.2.1"},
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
IPs: []string{"192.0.2.2"},
|
PrivateIPs: []string{"192.0.2.2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -75,7 +75,7 @@ func TestFetchSSHKeys(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"fetch works": {
|
"fetch works": {
|
||||||
meta: stubMetadata{
|
meta: stubMetadata{
|
||||||
selfRes: core.Instance{
|
selfRes: cloudtypes.Instance{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
ProviderID: "provider-id",
|
ProviderID: "provider-id",
|
||||||
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
|
||||||
@ -117,24 +117,24 @@ func TestFetchSSHKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stubMetadata struct {
|
type stubMetadata struct {
|
||||||
listRes []core.Instance
|
listRes []cloudtypes.Instance
|
||||||
listErr error
|
listErr error
|
||||||
selfRes core.Instance
|
selfRes cloudtypes.Instance
|
||||||
selfErr error
|
selfErr error
|
||||||
getInstanceRes core.Instance
|
getInstanceRes cloudtypes.Instance
|
||||||
getInstanceErr error
|
getInstanceErr error
|
||||||
supportedRes bool
|
supportedRes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (m *stubMetadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
return m.listRes, m.listErr
|
return m.listRes, m.listErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) Self(ctx context.Context) (core.Instance, error) {
|
func (m *stubMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
return m.selfRes, m.selfErr
|
return m.selfRes, m.selfErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
func (m *stubMetadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||||
return m.getInstanceRes, m.getInstanceErr
|
return m.getInstanceRes, m.getInstanceErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
go.mod
9
go.mod
@ -32,7 +32,10 @@ replace (
|
|||||||
k8s.io/sample-controller => k8s.io/sample-controller v0.24.0
|
k8s.io/sample-controller => k8s.io/sample-controller v0.24.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/nmiculinic/wg-quick-go v0.1.3 => github.com/katexochen/wg-quick-go v0.1.3-beta.1
|
replace (
|
||||||
|
github.com/martinjungblut/go-cryptsetup => github.com/daniel-weisse/go-cryptsetup v0.0.0-20220511084044-b537356aa24b
|
||||||
|
github.com/nmiculinic/wg-quick-go v0.1.3 => github.com/katexochen/wg-quick-go v0.1.3-beta.1
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.5.0
|
cloud.google.com/go/compute v1.5.0
|
||||||
@ -105,7 +108,6 @@ require (
|
|||||||
k8s.io/kubernetes v1.24.0
|
k8s.io/kubernetes v1.24.0
|
||||||
k8s.io/mount-utils v0.24.0
|
k8s.io/mount-utils v0.24.0
|
||||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||||
sigs.k8s.io/yaml v1.3.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -223,6 +225,5 @@ require (
|
|||||||
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
||||||
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/martinjungblut/go-cryptsetup => github.com/daniel-weisse/go-cryptsetup v0.0.0-20220511084044-b537356aa24b
|
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/spf13/cobra/doc"
|
"github.com/spf13/cobra/doc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var seeAlsoRegexp = regexp.MustCompile(`(?s)### SEE ALSO\n.+?\n\n`)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
rootCmd := cmd.NewRootCmd()
|
rootCmd := cmd.NewRootCmd()
|
||||||
@ -28,7 +30,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove "see also" sections. They list parent and child commands, which is not interesting for us.
|
// Remove "see also" sections. They list parent and child commands, which is not interesting for us.
|
||||||
cleanedBody := regexp.MustCompile(`(?s)### SEE ALSO\n.+?\n\n`).ReplaceAll(body.Bytes(), nil)
|
cleanedBody := seeAlsoRegexp.ReplaceAll(body.Bytes(), nil)
|
||||||
|
|
||||||
fmt.Printf("Commands:\n\n%s\n%s", cmdList, cleanedBody)
|
fmt.Printf("Commands:\n\n%s\n%s", cmdList, cleanedBody)
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,13 @@ func Default() *Config {
|
|||||||
FromPort: constants.NodePortFrom,
|
FromPort: constants.NodePortFrom,
|
||||||
ToPort: constants.NodePortTo,
|
ToPort: constants.NodePortTo,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "kubernetes",
|
||||||
|
Description: "Kubernetes",
|
||||||
|
Protocol: "tcp",
|
||||||
|
IPRange: "0.0.0.0/0",
|
||||||
|
FromPort: constants.KubernetesPort,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Provider: ProviderConfig{
|
Provider: ProviderConfig{
|
||||||
// TODO remove our subscriptions from the default config
|
// TODO remove our subscriptions from the default config
|
||||||
@ -204,7 +211,7 @@ func Default() *Config {
|
|||||||
SubscriptionID: "0d202bbb-4fa7-4af8-8125-58c269a05435",
|
SubscriptionID: "0d202bbb-4fa7-4af8-8125-58c269a05435",
|
||||||
TenantID: "adb650a8-5da3-4b15-b4b0-3daf65ff7626",
|
TenantID: "adb650a8-5da3-4b15-b4b0-3daf65ff7626",
|
||||||
Location: "North Europe",
|
Location: "North Europe",
|
||||||
Image: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1654096948",
|
Image: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation-coreos/versions/0.0.1654162332",
|
||||||
Measurements: azurePCRs,
|
Measurements: azurePCRs,
|
||||||
UserAssignedIdentity: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-dev-identity",
|
UserAssignedIdentity: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-dev-identity",
|
||||||
},
|
},
|
||||||
@ -212,7 +219,7 @@ func Default() *Config {
|
|||||||
Project: "constellation-331613",
|
Project: "constellation-331613",
|
||||||
Region: "europe-west3",
|
Region: "europe-west3",
|
||||||
Zone: "europe-west3-b",
|
Zone: "europe-west3-b",
|
||||||
Image: "projects/constellation-images/global/images/constellation-coreos-1654096948",
|
Image: "projects/constellation-images/global/images/constellation-coreos-1654162332",
|
||||||
ServiceAccountRoles: []string{
|
ServiceAccountRoles: []string{
|
||||||
"roles/compute.instanceAdmin.v1",
|
"roles/compute.instanceAdmin.v1",
|
||||||
"roles/compute.networkAdmin",
|
"roles/compute.networkAdmin",
|
||||||
|
@ -29,8 +29,9 @@ const (
|
|||||||
NVMEOverTCPPort = 8009
|
NVMEOverTCPPort = 8009
|
||||||
// Default NodePort Range
|
// Default NodePort Range
|
||||||
// https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
|
// https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
|
||||||
NodePortFrom = 30000
|
NodePortFrom = 30000
|
||||||
NodePortTo = 32767
|
NodePortTo = 32767
|
||||||
|
KubernetesPort = 6443
|
||||||
|
|
||||||
//
|
//
|
||||||
// Filenames.
|
// Filenames.
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/coordinator/atls"
|
"github.com/edgelesssys/constellation/coordinator/atls"
|
||||||
|
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/coordinator/core"
|
"github.com/edgelesssys/constellation/coordinator/core"
|
||||||
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
|
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
|
||||||
"github.com/edgelesssys/constellation/coordinator/role"
|
"github.com/edgelesssys/constellation/coordinator/role"
|
||||||
@ -20,33 +21,33 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestRequestKeyLoop(t *testing.T) {
|
func TestRequestKeyLoop(t *testing.T) {
|
||||||
defaultInstance := core.Instance{
|
defaultInstance := cloudtypes.Instance{
|
||||||
Name: "test-instance",
|
Name: "test-instance",
|
||||||
ProviderID: "/test/provider",
|
ProviderID: "/test/provider",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
IPs: []string{"192.0.2.1"},
|
PrivateIPs: []string{"192.0.2.1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
server *stubAPIServer
|
server *stubAPIServer
|
||||||
wantCalls int
|
wantCalls int
|
||||||
listResponse []core.Instance
|
listResponse []cloudtypes.Instance
|
||||||
dontStartServer bool
|
dontStartServer bool
|
||||||
}{
|
}{
|
||||||
"success": {
|
"success": {
|
||||||
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
||||||
listResponse: []core.Instance{defaultInstance},
|
listResponse: []cloudtypes.Instance{defaultInstance},
|
||||||
},
|
},
|
||||||
"no error if server throws an error": {
|
"no error if server throws an error": {
|
||||||
server: &stubAPIServer{
|
server: &stubAPIServer{
|
||||||
requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{},
|
requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{},
|
||||||
requestStateDiskKeyErr: errors.New("error"),
|
requestStateDiskKeyErr: errors.New("error"),
|
||||||
},
|
},
|
||||||
listResponse: []core.Instance{defaultInstance},
|
listResponse: []cloudtypes.Instance{defaultInstance},
|
||||||
},
|
},
|
||||||
"no error if the server can not be reached": {
|
"no error if the server can not be reached": {
|
||||||
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
||||||
listResponse: []core.Instance{defaultInstance},
|
listResponse: []cloudtypes.Instance{defaultInstance},
|
||||||
dontStartServer: true,
|
dontStartServer: true,
|
||||||
},
|
},
|
||||||
"no error if no endpoint is available": {
|
"no error if no endpoint is available": {
|
||||||
@ -54,13 +55,13 @@ func TestRequestKeyLoop(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"works for multiple endpoints": {
|
"works for multiple endpoints": {
|
||||||
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
server: &stubAPIServer{requestStateDiskKeyResp: &pubproto.RequestStateDiskKeyResponse{}},
|
||||||
listResponse: []core.Instance{
|
listResponse: []cloudtypes.Instance{
|
||||||
defaultInstance,
|
defaultInstance,
|
||||||
{
|
{
|
||||||
Name: "test-instance-2",
|
Name: "test-instance-2",
|
||||||
ProviderID: "/test/provider",
|
ProviderID: "/test/provider",
|
||||||
Role: role.Coordinator,
|
Role: role.Coordinator,
|
||||||
IPs: []string{"192.0.2.2"},
|
PrivateIPs: []string{"192.0.2.2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -192,19 +193,15 @@ func (s *stubAPIServer) RequestStateDiskKey(ctx context.Context, in *pubproto.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stubMetadata struct {
|
type stubMetadata struct {
|
||||||
listResponse []core.Instance
|
listResponse []cloudtypes.Instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s stubMetadata) List(ctx context.Context) ([]core.Instance, error) {
|
func (s stubMetadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||||
return s.listResponse, nil
|
return s.listResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s stubMetadata) Self(ctx context.Context) (core.Instance, error) {
|
func (s stubMetadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||||
return core.Instance{}, nil
|
return cloudtypes.Instance{}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (s stubMetadata) GetInstance(ctx context.Context, providerID string) (core.Instance, error) {
|
|
||||||
return core.Instance{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s stubMetadata) SignalRole(ctx context.Context, role role.Role) error {
|
func (s stubMetadata) SignalRole(ctx context.Context, role role.Role) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user