cli: add support for multiple coordinators

Signed-off-by: Benedict Schlueter <bs@edgeless.systems>
This commit is contained in:
Benedict Schlueter 2022-04-25 17:21:58 +02:00 committed by Benedict Schlüter
parent ff8830e718
commit 49def1e97f
5 changed files with 58 additions and 21 deletions

View File

@ -122,6 +122,7 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
pubKey: flags.userPubKey, pubKey: flags.userPubKey,
masterSecret: flags.masterSecret, masterSecret: flags.masterSecret,
nodePrivIPs: nodes.PrivateIPs(), nodePrivIPs: nodes.PrivateIPs(),
coordinatorPrivIPs: coordinators.PrivateIPs()[1:],
autoscalingNodeGroups: autoscalingNodeGroups, autoscalingNodeGroups: autoscalingNodeGroups,
cloudServiceAccountURI: serviceAccount, cloudServiceAccountURI: serviceAccount,
} }
@ -161,7 +162,7 @@ func activate(ctx context.Context, cmd *cobra.Command, client protoClient, input
return activationResult{}, err return activationResult{}, err
} }
respCl, err := client.Activate(ctx, input.pubKey, input.masterSecret, input.nodePrivIPs, input.autoscalingNodeGroups, input.cloudServiceAccountURI) respCl, err := client.Activate(ctx, input.pubKey, input.masterSecret, input.nodePrivIPs, input.coordinatorPrivIPs, input.autoscalingNodeGroups, input.cloudServiceAccountURI)
if err != nil { if err != nil {
return activationResult{}, err return activationResult{}, err
} }
@ -208,6 +209,7 @@ type activationInput struct {
pubKey []byte pubKey []byte
masterSecret []byte masterSecret []byte
nodePrivIPs []string nodePrivIPs []string
coordinatorPrivIPs []string
autoscalingNodeGroups []string autoscalingNodeGroups []string
cloudServiceAccountURI string cloudServiceAccountURI string
} }
@ -386,12 +388,20 @@ func getScalingGroupsFromConfig(stat state.ConstellationState, config *config.Co
} }
func getAWSInstances(stat state.ConstellationState) (coordinators, nodes ScalingGroup, err error) { func getAWSInstances(stat state.ConstellationState) (coordinators, nodes ScalingGroup, err error) {
coordinatorID, coordinator, err := stat.EC2Instances.GetOne() coordinatorID, _, err := stat.EC2Instances.GetOne()
if err != nil { if err != nil {
return return
} }
coordinatorMap := stat.EC2Instances
var coordinatorInstances Instances
for _, node := range coordinatorMap {
coordinatorInstances = append(coordinatorInstances, Instance(node))
}
// GroupID of coordinators is empty, since they currently do not scale. // GroupID of coordinators is empty, since they currently do not scale.
coordinators = ScalingGroup{Instances: Instances{Instance(coordinator)}, GroupID: ""} coordinators = ScalingGroup{
Instances: coordinatorInstances,
GroupID: "",
}
nodeMap := stat.EC2Instances.GetOthers(coordinatorID) nodeMap := stat.EC2Instances.GetOthers(coordinatorID)
if len(nodeMap) == 0 { if len(nodeMap) == 0 {
@ -411,12 +421,19 @@ func getAWSInstances(stat state.ConstellationState) (coordinators, nodes Scaling
} }
func getGCPInstances(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) { func getGCPInstances(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) {
_, coordinator, err := stat.GCPCoordinators.GetOne() coordinatorMap := stat.GCPCoordinators
if err != nil { if len(coordinatorMap) == 0 {
return return ScalingGroup{}, ScalingGroup{}, errors.New("no coordinators available, can't create Constellation without any instance")
}
var coordinatorInstances Instances
for _, node := range coordinatorMap {
coordinatorInstances = append(coordinatorInstances, Instance(node))
} }
// GroupID of coordinators is empty, since they currently do not scale. // GroupID of coordinators is empty, since they currently do not scale.
coordinators = ScalingGroup{Instances: Instances{Instance(coordinator)}, GroupID: ""} coordinators = ScalingGroup{
Instances: coordinatorInstances,
GroupID: "",
}
nodeMap := stat.GCPNodes nodeMap := stat.GCPNodes
if len(nodeMap) == 0 { if len(nodeMap) == 0 {
@ -438,13 +455,19 @@ func getGCPInstances(stat state.ConstellationState, config *config.Config) (coor
} }
func getAzureInstances(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) { func getAzureInstances(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) {
_, coordinator, err := stat.AzureCoordinators.GetOne() coordinatorMap := stat.AzureCoordinators
if err != nil { if len(coordinatorMap) == 0 {
return return ScalingGroup{}, ScalingGroup{}, errors.New("no coordinators available, can't create Constellation without any instance")
}
var coordinatorInstances Instances
for _, node := range coordinatorMap {
coordinatorInstances = append(coordinatorInstances, Instance(node))
} }
// GroupID of coordinators is empty, since they currently do not scale. // GroupID of coordinators is empty, since they currently do not scale.
coordinators = ScalingGroup{Instances: Instances{Instance(coordinator)}, GroupID: ""} coordinators = ScalingGroup{
Instances: coordinatorInstances,
GroupID: "",
}
nodeMap := stat.AzureNodes nodeMap := stat.AzureNodes
if len(nodeMap) == 0 { if len(nodeMap) == 0 {
return ScalingGroup{}, ScalingGroup{}, errors.New("no nodes available, can't create Constellation with one instance") return ScalingGroup{}, ScalingGroup{}, errors.New("no nodes available, can't create Constellation with one instance")

View File

@ -10,5 +10,5 @@ import (
type protoClient interface { type protoClient interface {
Connect(ip, port string, validators []atls.Validator) error Connect(ip, port string, validators []atls.Validator) error
Close() error Close() error
Activate(ctx context.Context, userPublicKey, masterSecret []byte, endpoints, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error)
} }

View File

@ -19,7 +19,8 @@ type stubProtoClient struct {
activateUserPublicKey []byte activateUserPublicKey []byte
activateMasterSecret []byte activateMasterSecret []byte
activateEndpoints []string activateNodeIPs []string
activateCoordinatorIPs []string
activateAutoscalingNodeGroups []string activateAutoscalingNodeGroups []string
cloudServiceAccountURI string cloudServiceAccountURI string
} }
@ -34,16 +35,21 @@ func (c *stubProtoClient) Close() error {
return c.closeErr return c.closeErr
} }
func (c *stubProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, endpoints []string, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) { func (c *stubProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs []string, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) {
c.activateUserPublicKey = userPublicKey c.activateUserPublicKey = userPublicKey
c.activateMasterSecret = masterSecret c.activateMasterSecret = masterSecret
c.activateEndpoints = endpoints c.activateNodeIPs = nodeIPs
c.activateCoordinatorIPs = coordinatorIPs
c.activateAutoscalingNodeGroups = autoscalingNodeGroups c.activateAutoscalingNodeGroups = autoscalingNodeGroups
c.cloudServiceAccountURI = cloudServiceAccountURI c.cloudServiceAccountURI = cloudServiceAccountURI
return c.respClient, c.activateErr return c.respClient, c.activateErr
} }
func (c *stubProtoClient) ActivateAdditionalCoordinators(ctx context.Context, ips []string) error {
return c.activateErr
}
type stubActivationRespClient struct { type stubActivationRespClient struct {
nextLogErr *error nextLogErr *error
getKubeconfigErr error getKubeconfigErr error
@ -106,13 +112,20 @@ func (c *fakeProtoClient) Close() error {
return nil return nil
} }
func (c *fakeProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, endpoints []string, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) { func (c *fakeProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs []string, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) {
if !c.conn { if !c.conn {
return nil, errors.New("client is not connected") return nil, errors.New("client is not connected")
} }
return c.respClient, nil return c.respClient, nil
} }
func (c *fakeProtoClient) ActivateAdditionalCoordinators(ctx context.Context, ips []string) error {
if !c.conn {
return errors.New("client is not connected")
}
return nil
}
type fakeActivationRespClient struct { type fakeActivationRespClient struct {
responses []fakeActivationRespMessage responses []fakeActivationRespMessage
kubeconfig string kubeconfig string

View File

@ -64,14 +64,14 @@ func (c *Client) Close() error {
// Activate activates the Constellation coordinator via a grpc call. // Activate activates the Constellation coordinator via a grpc call.
// The handed IP addresses must be the private IP addresses of running AWS or GCP instances, // The handed IP addresses must be the private IP addresses of running AWS or GCP instances,
// and the userPublicKey is the VPN key of the users WireGuard interface. // and the userPublicKey is the VPN key of the users WireGuard interface.
func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byte, ips, autoscalingNodeGroups []string, cloudServiceAccountURI string) (ActivationResponseClient, error) { func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string) (ActivationResponseClient, error) {
if c.avpn == nil { if c.avpn == nil {
return nil, errors.New("client is not connected") return nil, errors.New("client is not connected")
} }
if len(userPublicKey) == 0 { if len(userPublicKey) == 0 {
return nil, errors.New("parameter userPublicKey is empty") return nil, errors.New("parameter userPublicKey is empty")
} }
if len(ips) == 0 { if len(nodeIPs) == 0 {
return nil, errors.New("parameter ips is empty") return nil, errors.New("parameter ips is empty")
} }
@ -82,7 +82,8 @@ func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byt
avpnRequest := &pubproto.ActivateAsCoordinatorRequest{ avpnRequest := &pubproto.ActivateAsCoordinatorRequest{
AdminVpnPubKey: pubKey[:], AdminVpnPubKey: pubKey[:],
NodePublicIps: ips, NodePublicIps: nodeIPs,
CoordinatorPublicIps: coordinatorIPs,
AutoscalingNodeGroups: autoscalingNodeGroups, AutoscalingNodeGroups: autoscalingNodeGroups,
MasterSecret: masterSecret, MasterSecret: masterSecret,
KmsUri: kms.ClusterKMSURI, KmsUri: kms.ClusterKMSURI,

View File

@ -120,7 +120,7 @@ func TestActivate(t *testing.T) {
if tc.avpn != nil { if tc.avpn != nil {
client.avpn = tc.avpn client.avpn = tc.avpn
} }
_, err := client.Activate(context.Background(), []byte(tc.userPublicKey), []byte("Constellation"), tc.ips, nil, "serviceaccount://test") _, err := client.Activate(context.Background(), []byte(tc.userPublicKey), []byte("Constellation"), tc.ips, nil, nil, "serviceaccount://test")
if tc.errExpected { if tc.errExpected {
assert.Error(err) assert.Error(err)
} else { } else {