Remove cli/ec2

This commit is contained in:
katexochen 2022-06-07 16:45:32 +02:00
parent 064151a956
commit 6a9419e89c
19 changed files with 6 additions and 1804 deletions

View File

@ -397,8 +397,6 @@ func readOrGeneratedMasterSecret(w io.Writer, fileHandler file.Handler, filename
func getScalingGroupsFromConfig(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) {
switch {
case len(stat.EC2Instances) != 0:
return getAWSInstances(stat)
case len(stat.GCPCoordinators) != 0:
return getGCPInstances(stat, config)
case len(stat.AzureCoordinators) != 0:
@ -410,39 +408,6 @@ func getScalingGroupsFromConfig(stat state.ConstellationState, config *config.Co
}
}
func getAWSInstances(stat state.ConstellationState) (coordinators, nodes ScalingGroup, err error) {
coordinatorID, _, err := stat.EC2Instances.GetOne()
if err != nil {
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.
coordinators = ScalingGroup{
Instances: coordinatorInstances,
GroupID: "",
}
nodeMap := stat.EC2Instances.GetOthers(coordinatorID)
if len(nodeMap) == 0 {
return ScalingGroup{}, ScalingGroup{}, errors.New("no worker nodes available, can't create Constellation cluster with one instance")
}
var nodeInstances Instances
for _, node := range nodeMap {
nodeInstances = append(nodeInstances, Instance(node))
}
// TODO: make min / max configurable and abstract autoscaling for different cloud providers
// TODO: GroupID of workers is empty, since they currently do not scale.
nodes = ScalingGroup{Instances: nodeInstances, GroupID: ""}
return
}
func getGCPInstances(stat state.ConstellationState, config *config.Config) (coordinators, nodes ScalingGroup, err error) {
coordinatorMap := stat.GCPCoordinators
if len(coordinatorMap) == 0 {

View File

@ -11,7 +11,6 @@ import (
"time"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/edgelesssys/constellation/cli/ec2"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state"
@ -167,15 +166,12 @@ func TestInitialize(t *testing.T) {
wantErr: true,
},
"no instances to pick one": {
existingState: state.ConstellationState{
EC2Instances: ec2.Instances{},
EC2SecurityGroup: "sg-test",
},
client: &stubProtoClient{},
waiter: &stubStatusWaiter{},
privKey: testKey,
vpnHandler: &stubVPNHandler{},
wantErr: true,
existingState: state.ConstellationState{GCPNodes: cloudtypes.Instances{}},
client: &stubProtoClient{},
waiter: &stubStatusWaiter{},
privKey: testKey,
vpnHandler: &stubVPNHandler{},
wantErr: true,
},
"public key to short": {
existingState: testGcpState,

View File

@ -1,42 +0,0 @@
package client
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/ec2"
)
// api collects used functions of AWS' ec2.Client as interfaces to enable testing.
type api interface {
ec2.DescribeInstancesAPIClient
// Instances
RunInstances(ctx context.Context,
params *ec2.RunInstancesInput,
optFns ...func(*ec2.Options)) (*ec2.RunInstancesOutput, error)
TerminateInstances(ctx context.Context,
params *ec2.TerminateInstancesInput,
optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error)
CreateTags(ctx context.Context,
params *ec2.CreateTagsInput,
optFns ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error)
// SecurityGroup
CreateSecurityGroup(ctx context.Context,
params *ec2.CreateSecurityGroupInput,
optFns ...func(*ec2.Options)) (*ec2.CreateSecurityGroupOutput, error)
DeleteSecurityGroup(ctx context.Context,
params *ec2.DeleteSecurityGroupInput,
optFns ...func(*ec2.Options)) (*ec2.DeleteSecurityGroupOutput, error)
AuthorizeSecurityGroupIngress(ctx context.Context,
params *ec2.AuthorizeSecurityGroupIngressInput,
optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupIngressOutput, error)
AuthorizeSecurityGroupEgress(ctx context.Context,
params *ec2.AuthorizeSecurityGroupEgressInput,
optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupEgressOutput, error)
}

View File

@ -1,137 +0,0 @@
package client
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/smithy-go"
)
// stubAPI is a stub ec2 api for testing.
type stubAPI struct {
instances []types.Instance
securityGroup types.SecurityGroup
describeInstancesErr error
runInstancesErr error
runInstancesDryRunErr *error
terminateInstancesErr error
terminateInstancesDryRunErr *error
createTagsErr error
createSecurityGroupErr error
createSecurityGroupDryRunErr *error
deleteSecurityGroupErr error
deleteSecurityGroupDryRunErr *error
authorizeSecurityGroupIngressErr error
authorizeSecurityGroupIngressDryRunErr *error
authorizeSecurityGroupEgressErr error
authorizeSecurityGroupEgressDryRunErr *error
}
func (a stubAPI) DescribeInstances(ctx context.Context,
params *ec2.DescribeInstancesInput,
optFns ...func(*ec2.Options),
) (*ec2.DescribeInstancesOutput, error) {
return &ec2.DescribeInstancesOutput{
Reservations: []types.Reservation{
{Instances: a.instances},
},
}, a.describeInstancesErr
}
func (a stubAPI) RunInstances(ctx context.Context,
params *ec2.RunInstancesInput,
optFns ...func(*ec2.Options),
) (*ec2.RunInstancesOutput, error) {
if err := getDryRunErr(params.DryRun, a.runInstancesDryRunErr); err != nil {
return nil, err
}
return &ec2.RunInstancesOutput{Instances: a.instances}, a.runInstancesErr
}
func (a stubAPI) CreateTags(ctx context.Context,
params *ec2.CreateTagsInput,
optFns ...func(*ec2.Options),
) (*ec2.CreateTagsOutput, error) {
return nil, a.createTagsErr
}
func (a stubAPI) TerminateInstances(ctx context.Context,
params *ec2.TerminateInstancesInput,
optFns ...func(*ec2.Options),
) (*ec2.TerminateInstancesOutput, error) {
if err := getDryRunErr(params.DryRun, a.terminateInstancesDryRunErr); err != nil {
return nil, err
}
return nil, a.terminateInstancesErr
}
func (a stubAPI) CreateSecurityGroup(ctx context.Context,
params *ec2.CreateSecurityGroupInput,
optFns ...func(*ec2.Options),
) (*ec2.CreateSecurityGroupOutput, error) {
if err := getDryRunErr(params.DryRun, a.createSecurityGroupDryRunErr); err != nil {
return nil, err
}
return &ec2.CreateSecurityGroupOutput{
GroupId: a.securityGroup.GroupId,
}, a.createSecurityGroupErr
}
func (a stubAPI) DeleteSecurityGroup(ctx context.Context,
params *ec2.DeleteSecurityGroupInput,
optFns ...func(*ec2.Options),
) (*ec2.DeleteSecurityGroupOutput, error) {
if err := getDryRunErr(params.DryRun, a.deleteSecurityGroupDryRunErr); err != nil {
return nil, err
}
return nil, a.deleteSecurityGroupErr
}
func (a stubAPI) AuthorizeSecurityGroupIngress(ctx context.Context,
params *ec2.AuthorizeSecurityGroupIngressInput,
optFns ...func(*ec2.Options),
) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
if err := getDryRunErr(params.DryRun, a.authorizeSecurityGroupIngressDryRunErr); err != nil {
return nil, err
}
return nil, a.authorizeSecurityGroupIngressErr
}
func (a stubAPI) AuthorizeSecurityGroupEgress(ctx context.Context,
params *ec2.AuthorizeSecurityGroupEgressInput,
optFns ...func(*ec2.Options),
) (*ec2.AuthorizeSecurityGroupEgressOutput, error) {
if err := getDryRunErr(params.DryRun, a.authorizeSecurityGroupEgressDryRunErr); err != nil {
return nil, err
}
return nil, a.authorizeSecurityGroupEgressErr
}
func getDryRunErr(dryRun *bool, stubErr *error) error {
if dryRun == nil || !*dryRun {
return nil
}
if stubErr != nil {
return *stubErr
}
return &smithy.GenericAPIError{Code: "DryRunOperation"}
}
var stateRunning = types.InstanceState{
Code: aws.Int32(int32(16)),
Name: types.InstanceStateNameRunning,
}
var stateTerminated = types.InstanceState{
Code: aws.Int32(48),
Name: types.InstanceStateNameTerminated,
}

View File

@ -1,71 +0,0 @@
package client
import (
"context"
"errors"
"time"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
awsec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/edgelesssys/constellation/cli/ec2"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/state"
)
// Client for the AWS EC2 API.
type Client struct {
api api
instances ec2.Instances
securityGroup string
timeout time.Duration
}
func newClient(api api) (*Client, error) {
return &Client{
api: api,
instances: make(map[string]ec2.Instance),
timeout: 2 * time.Minute,
}, nil
}
// NewFromDefault creates a Client from the default config.
func NewFromDefault(ctx context.Context) (*Client, error) {
cfg, err := awsconfig.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
return newClient(awsec2.NewFromConfig(cfg))
}
// GetState returns the current configuration of the Constellation,
// which can be stored and used through later CLI commands.
func (c *Client) GetState() (state.ConstellationState, error) {
if len(c.instances) == 0 {
return state.ConstellationState{}, errors.New("client has no instances")
}
if c.securityGroup == "" {
return state.ConstellationState{}, errors.New("client has no security group")
}
return state.ConstellationState{
CloudProvider: cloudprovider.AWS.String(),
EC2Instances: c.instances,
EC2SecurityGroup: c.securityGroup,
}, nil
}
// SetState sets a Client to an existing configuration.
func (c *Client) SetState(stat state.ConstellationState) error {
if stat.CloudProvider != cloudprovider.AWS.String() {
return errors.New("state is not aws state")
}
if len(stat.EC2Instances) == 0 {
return errors.New("state has no instances")
}
if stat.EC2SecurityGroup == "" {
return errors.New("state has no security group")
}
c.instances = stat.EC2Instances
c.securityGroup = stat.EC2SecurityGroup
return nil
}

View File

@ -1,120 +0,0 @@
package client
import (
"testing"
"github.com/edgelesssys/constellation/cli/ec2"
"github.com/edgelesssys/constellation/internal/state"
"github.com/stretchr/testify/assert"
)
func TestGetState(t *testing.T) {
testCases := map[string]struct {
client Client
wantState state.ConstellationState
wantErr bool
}{
"successful get": {
client: Client{
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
securityGroup: "sg",
},
wantState: state.ConstellationState{
CloudProvider: "AWS",
EC2Instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
EC2SecurityGroup: "sg",
},
},
"client without security group": {
client: Client{
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
},
wantErr: true,
},
"client without instances": {
client: Client{
securityGroup: "sg",
},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
stat, err := tc.client.GetState()
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.wantState, stat)
}
})
}
}
func TestSetState(t *testing.T) {
testCases := map[string]struct {
state state.ConstellationState
wantInstances ec2.Instances
wantSecurityGroup string
wantErr bool
}{
"successful set": {
state: state.ConstellationState{
CloudProvider: "AWS",
EC2SecurityGroup: "sg-test",
EC2Instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
},
wantInstances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantSecurityGroup: "sg-test",
},
"state without cloudprovider": {
state: state.ConstellationState{
EC2SecurityGroup: "sg-test",
EC2Instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
},
wantErr: true,
},
"state with incorrect cloudprovider": {
state: state.ConstellationState{
CloudProvider: "incorrect",
EC2SecurityGroup: "sg-test",
EC2Instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
},
wantErr: true,
},
"state without instances": {
state: state.ConstellationState{
CloudProvider: "AWS",
EC2SecurityGroup: "sg-test",
},
wantErr: true,
},
"state without security group": {
state: state.ConstellationState{
CloudProvider: "AWS",
EC2Instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{}
err := client.SetState(tc.state)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.wantInstances, client.instances)
assert.Equal(tc.wantSecurityGroup, client.securityGroup)
}
})
}
}

View File

@ -1,199 +0,0 @@
package client
import (
"context"
"errors"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
awsec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/edgelesssys/constellation/cli/ec2"
)
// CreateInstances creates the instances defined in input.
//
// An existing security group is needed to create instances.
func (c *Client) CreateInstances(ctx context.Context, input CreateInput) error {
if c.securityGroup == "" {
return errors.New("no security group set")
}
input.securityGroupIds = []string{c.securityGroup}
if err := c.createDryRun(ctx, input); err != nil {
return err
}
resp, err := c.api.RunInstances(ctx, input.AWS())
if err != nil {
return fmt.Errorf("failed to create instances: %w", err)
}
for _, instance := range resp.Instances {
id := instance.InstanceId
if id == nil {
return errors.New("instanceId is nil pointer")
}
c.instances[*id] = ec2.Instance{}
}
if err := c.waitStateRunning(ctx); err != nil {
return err
}
if err := c.tagInstances(ctx, input.Tags); err != nil {
return err
}
if err := c.getInstanceIPs(ctx); err != nil {
return err
}
return nil
}
// TerminateInstances terminates all instances of a Client.
func (c *Client) TerminateInstances(ctx context.Context) error {
if len(c.instances) == 0 {
return nil
}
input := &awsec2.TerminateInstancesInput{
InstanceIds: c.instances.IDs(),
}
if err := c.terminateDryRun(ctx, *input); err != nil {
return err
}
if _, err := c.api.TerminateInstances(ctx, input); err != nil {
return err
}
if err := c.waitStateTerminated(ctx); err != nil {
return err
}
c.instances = ec2.Instances{}
return nil
}
// waitStateRunning waits until all the client's instances reached the running state.
//
// A set of instances is also considered to be running if at least one of the
// instances' state is 'running' and the other instances have a nil state.
func (c *Client) waitStateRunning(ctx context.Context) error {
if len(c.instances) == 0 {
return errors.New("client has no instances")
}
describeInput := &awsec2.DescribeInstancesInput{
InstanceIds: c.instances.IDs(),
}
waiter := awsec2.NewInstanceRunningWaiter(c.api)
return waiter.Wait(ctx, describeInput, c.timeout)
}
// waitStateTerminated waits until all the client's instances reached the terminated state.
//
// A set of instances is also considered to be terminated if at least one of the
// instances' state is 'terminated' and the other instances have a nil state.
func (c *Client) waitStateTerminated(ctx context.Context) error {
if len(c.instances) == 0 {
return errors.New("client has no instances")
}
describeInput := &awsec2.DescribeInstancesInput{
InstanceIds: c.instances.IDs(),
}
waiter := awsec2.NewInstanceTerminatedWaiter(c.api)
return waiter.Wait(ctx, describeInput, c.timeout)
}
// tagInstances tags all instances of a client with a given set of tags.
func (c *Client) tagInstances(ctx context.Context, tags ec2.Tags) error {
if len(c.instances) == 0 {
return errors.New("client has no instances")
}
tagInput := &awsec2.CreateTagsInput{
Resources: c.instances.IDs(),
Tags: tags.AWS(),
}
if _, err := c.api.CreateTags(ctx, tagInput); err != nil {
return fmt.Errorf("failed to tag instances: %w", err)
}
return nil
}
// createDryRun checks if user has the privilege to create the instances
// which were defined in input.
func (c *Client) createDryRun(ctx context.Context, input CreateInput) error {
runInput := input.AWS()
runInput.DryRun = aws.Bool(true)
_, err := c.api.RunInstances(ctx, runInput)
return checkDryRunError(err)
}
// terminateDryRun checks if user has the privilege to terminate the instances
// which were defined in input.
func (c *Client) terminateDryRun(ctx context.Context, input awsec2.TerminateInstancesInput) error {
input.DryRun = aws.Bool(true)
_, err := c.api.TerminateInstances(ctx, &input)
return checkDryRunError(err)
}
// getInstanceIPs queries the private and public IP addresses
// and adds the information to each instance.
//
// The instances must be in 'running' state.
func (c *Client) getInstanceIPs(ctx context.Context) error {
describeInput := &awsec2.DescribeInstancesInput{
InstanceIds: c.instances.IDs(),
}
paginator := awsec2.NewDescribeInstancesPaginator(c.api, describeInput)
for paginator.HasMorePages() {
output, err := paginator.NextPage(ctx)
if err != nil {
return err
}
for _, reservation := range output.Reservations {
for _, instanceDescription := range reservation.Instances {
if instanceDescription.InstanceId == nil {
return errors.New("instanceId is nil pointer")
}
if instanceDescription.PublicIpAddress == nil {
return errors.New("publicIpAddress is nil pointer")
}
if instanceDescription.PrivateIpAddress == nil {
return errors.New("privateIpAddress is nil pointer")
}
instance, ok := c.instances[*instanceDescription.InstanceId]
if !ok {
return errors.New("got an instance description to an unknown instanceId")
}
instance.PublicIP = *instanceDescription.PublicIpAddress
instance.PrivateIP = *instanceDescription.PrivateIpAddress
c.instances[*instanceDescription.InstanceId] = instance
}
}
}
return nil
}
// CreateInput defines the propertis of the instances to create.
type CreateInput struct {
ImageId string
InstanceType string
Count int
Tags ec2.Tags
securityGroupIds []string
}
// AWS creates a AWS ec2.RunInstancesInput from an CreateInput.
func (ci *CreateInput) AWS() *awsec2.RunInstancesInput {
return &awsec2.RunInstancesInput{
ImageId: aws.String(ci.ImageId),
InstanceType: ec2.InstanceTypes[ci.InstanceType],
MaxCount: aws.Int32(int32(ci.Count)),
MinCount: aws.Int32(int32(ci.Count)),
EnclaveOptions: &types.EnclaveOptionsRequest{Enabled: aws.Bool(true)},
SecurityGroupIds: ci.securityGroupIds,
}
}

View File

@ -1,493 +0,0 @@
package client
import (
"context"
"errors"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
awsec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/edgelesssys/constellation/cli/ec2"
"github.com/stretchr/testify/assert"
)
func TestCreateInstances(t *testing.T) {
testInstances := []types.Instance{
{
InstanceId: aws.String("id-1"),
PublicIpAddress: aws.String("192.0.2.1"),
PrivateIpAddress: aws.String("192.0.2.2"),
State: &stateRunning,
},
{
InstanceId: aws.String("id-2"),
PublicIpAddress: aws.String("192.0.2.3"),
PrivateIpAddress: aws.String("192.0.2.4"),
State: &stateRunning,
},
{
InstanceId: aws.String("id-3"),
PublicIpAddress: aws.String("192.0.2.5"),
PrivateIpAddress: aws.String("192.0.2.6"),
State: &stateRunning,
},
}
someErr := errors.New("failed")
var noErr error
testCases := map[string]struct {
api stubAPI
instances ec2.Instances
securityGroup string
wantErr bool
wantInstances ec2.Instances
}{
"create": {
api: stubAPI{instances: testInstances},
securityGroup: "sg-test",
wantInstances: ec2.Instances{
"id-1": {PublicIP: "192.0.2.1", PrivateIP: "192.0.2.2"},
"id-2": {PublicIP: "192.0.2.3", PrivateIP: "192.0.2.4"},
"id-3": {PublicIP: "192.0.2.5", PrivateIP: "192.0.2.6"},
},
},
"client already has instances": {
api: stubAPI{instances: testInstances},
instances: ec2.Instances{"id-4": {}, "id-5": {}},
securityGroup: "sg-test",
wantInstances: ec2.Instances{
"id-1": {PublicIP: "192.0.2.1", PrivateIP: "192.0.2.2"},
"id-2": {PublicIP: "192.0.2.3", PrivateIP: "192.0.2.4"},
"id-3": {PublicIP: "192.0.2.5", PrivateIP: "192.0.2.6"},
"id-4": {},
"id-5": {},
},
},
"client already has same instance id": {
api: stubAPI{instances: testInstances},
instances: ec2.Instances{"id-1": {}, "id-4": {}, "id-5": {}},
securityGroup: "sg-test",
wantErr: false,
wantInstances: ec2.Instances{
"id-1": {PublicIP: "192.0.2.1", PrivateIP: "192.0.2.2"},
"id-2": {PublicIP: "192.0.2.3", PrivateIP: "192.0.2.4"},
"id-3": {PublicIP: "192.0.2.5", PrivateIP: "192.0.2.6"},
"id-4": {},
"id-5": {},
},
},
"client has no security group": {
api: stubAPI{},
wantErr: true,
},
"run API error": {
api: stubAPI{runInstancesErr: someErr},
securityGroup: "sg-test",
wantErr: true,
},
"runDryRun API error": {
api: stubAPI{runInstancesDryRunErr: &someErr},
securityGroup: "sg-test",
wantErr: true,
},
"runDryRun missing expected API error": {
api: stubAPI{runInstancesDryRunErr: &noErr},
securityGroup: "sg-test",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{
api: tc.api,
instances: tc.instances,
timeout: time.Millisecond,
securityGroup: tc.securityGroup,
}
if client.instances == nil {
client.instances = make(map[string]ec2.Instance)
}
input := CreateInput{
ImageId: "test-image",
InstanceType: "",
Count: 13,
}
err := client.CreateInstances(context.Background(), input)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.ElementsMatch(tc.wantInstances.IDs(), client.instances.IDs())
assert.ElementsMatch(tc.wantInstances.PublicIPs(), client.instances.PublicIPs())
assert.ElementsMatch(tc.wantInstances.PrivateIPs(), client.instances.PrivateIPs())
}
})
}
}
func TestTerminateInstances(t *testing.T) {
testAWSInstances := []types.Instance{
{InstanceId: aws.String("id-1"), State: &stateTerminated},
{InstanceId: aws.String("id-2"), State: &stateTerminated},
{InstanceId: aws.String("id-3"), State: &stateTerminated},
}
someErr := errors.New("failed")
var noErr error
testCases := map[string]struct {
api stubAPI
instances ec2.Instances
wantErr bool
}{
"client with instances": {
api: stubAPI{instances: testAWSInstances},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"client no instances set": {
api: stubAPI{},
},
"terminate API error": {
api: stubAPI{terminateInstancesErr: someErr},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"terminateDryRun API error": {
api: stubAPI{terminateInstancesDryRunErr: &someErr},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"terminateDryRun miss expected API error": {
api: stubAPI{terminateInstancesDryRunErr: &noErr},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{
api: tc.api,
instances: tc.instances,
timeout: time.Millisecond,
}
if client.instances == nil {
client.instances = make(map[string]ec2.Instance)
}
err := client.TerminateInstances(context.Background())
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Empty(client.instances)
}
})
}
}
func TestWaitStateRunning(t *testing.T) {
testCases := map[string]struct {
api api
instances ec2.Instances
wantErr bool
}{
"instances are running": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateRunning,
},
{
InstanceId: aws.String("id-2"),
State: &stateRunning,
},
{
InstanceId: aws.String("id-3"),
State: &stateRunning,
},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"one instance running, rest nil": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateRunning,
},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"one instance terminated, rest nil": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateTerminated,
},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"instances with different state": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateTerminated,
},
{
InstanceId: aws.String("id-2"),
State: &stateRunning,
},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"all instances have nil state": {
api: stubAPI{instances: []types.Instance{
{InstanceId: aws.String("id-1")},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"client has no instances": {
api: &stubAPI{},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{
api: tc.api,
instances: tc.instances,
timeout: time.Millisecond,
}
if client.instances == nil {
client.instances = make(map[string]ec2.Instance)
}
err := client.waitStateRunning(context.Background())
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
func TestWaitStateTerminated(t *testing.T) {
testCases := map[string]struct {
api api
instances ec2.Instances
wantErr bool
}{
"instances are terminated": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateTerminated,
},
{
InstanceId: aws.String("id-2"),
State: &stateTerminated,
},
{
InstanceId: aws.String("id-3"),
State: &stateTerminated,
},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"one instance terminated, rest nil": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateTerminated,
},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"one instance running, rest nil": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateRunning,
},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"instances with different state": {
api: stubAPI{instances: []types.Instance{
{
InstanceId: aws.String("id-1"),
State: &stateTerminated,
},
{
InstanceId: aws.String("id-2"),
State: &stateRunning,
},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"all instances have nil state": {
api: stubAPI{instances: []types.Instance{
{InstanceId: aws.String("id-1")},
{InstanceId: aws.String("id-2")},
{InstanceId: aws.String("id-3")},
}},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
"client has no instances": {
api: &stubAPI{},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{
api: tc.api,
instances: tc.instances,
timeout: time.Millisecond,
}
if client.instances == nil {
client.instances = make(map[string]ec2.Instance)
}
err := client.waitStateTerminated(context.Background())
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
func TestTagInstances(t *testing.T) {
testTags := ec2.Tags{
{Key: "Name", Value: "Test"},
{Key: "Foo", Value: "Bar"},
}
testCases := map[string]struct {
api stubAPI
instances ec2.Instances
wantErr bool
}{
"tag": {
api: stubAPI{},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: false,
},
"client without instances": {
api: stubAPI{createTagsErr: errors.New("failed")},
wantErr: true,
},
"tag API error": {
api: stubAPI{createTagsErr: errors.New("failed")},
instances: ec2.Instances{"id-1": {}, "id-2": {}, "id-3": {}},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := &Client{
api: tc.api,
instances: tc.instances,
timeout: time.Millisecond,
}
if client.instances == nil {
client.instances = make(map[string]ec2.Instance)
}
err := client.tagInstances(context.Background(), testTags)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
func TestEc2RunInstanceInput(t *testing.T) {
assert := assert.New(t)
testCases := []struct {
in CreateInput
wantOutput awsec2.RunInstancesInput
}{
{
in: CreateInput{
ImageId: "test-image",
InstanceType: "4xlarge",
Count: 13,
securityGroupIds: []string{"test-sec-group"},
},
wantOutput: awsec2.RunInstancesInput{
ImageId: aws.String("test-image"),
InstanceType: types.InstanceTypeC5a4xlarge,
MinCount: aws.Int32(int32(13)),
MaxCount: aws.Int32(int32(13)),
EnclaveOptions: &types.EnclaveOptionsRequest{Enabled: aws.Bool(true)},
SecurityGroupIds: []string{"test-sec-group"},
},
},
{
in: CreateInput{
ImageId: "test-image-2",
InstanceType: "12xlarge",
Count: 2,
securityGroupIds: []string{"test-sec-group-2"},
},
wantOutput: awsec2.RunInstancesInput{
ImageId: aws.String("test-image-2"),
InstanceType: types.InstanceTypeC5a12xlarge,
MinCount: aws.Int32(int32(2)),
MaxCount: aws.Int32(int32(2)),
EnclaveOptions: &types.EnclaveOptionsRequest{Enabled: aws.Bool(true)},
SecurityGroupIds: []string{"test-sec-group-2"},
},
},
}
for _, tc := range testCases {
out := tc.in.AWS()
assert.Equal(tc.wantOutput, *out)
}
}

View File

@ -1,136 +0,0 @@
package client
import (
"context"
"errors"
"github.com/aws/aws-sdk-go-v2/aws"
awsec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/google/uuid"
)
// CreateSecurityGroup creates a AWS security group with the handed properties.
func (c *Client) CreateSecurityGroup(ctx context.Context, input SecurityGroupInput) error {
if c.securityGroup != "" {
return errors.New("client already has a security group")
}
id := uuid.New()
createInput := &awsec2.CreateSecurityGroupInput{
Description: aws.String("Security group of Constellation cluster. This group was generated through the Constellation CLI."),
GroupName: aws.String("Constellation-" + id.String()),
DryRun: aws.Bool(true),
}
// DryRun
_, err := c.api.CreateSecurityGroup(ctx, createInput)
if err := checkDryRunError(err); err != nil {
return err
}
createInput.DryRun = aws.Bool(false)
// Create
out, err := c.api.CreateSecurityGroup(ctx, createInput)
if err != nil {
return err
}
if out.GroupId == nil {
return errors.New("security group creation didn't return an id")
}
c.securityGroup = *out.GroupId
// Authorize.
return c.authorizeSecurityGroup(ctx, input)
}
// DeleteSecurityGroup deletes the security group of the client.
// The deletion is idempotent, no error is returned if the client has
// an empty securityGroupID.
func (c *Client) DeleteSecurityGroup(ctx context.Context) error {
if c.securityGroup == "" {
return nil
}
input := &awsec2.DeleteSecurityGroupInput{
GroupId: aws.String(c.securityGroup),
DryRun: aws.Bool(true),
}
// DryRun
_, err := c.api.DeleteSecurityGroup(ctx, input)
if err := checkDryRunError(err); err != nil {
return err
}
input.DryRun = aws.Bool(false)
// Delete
if _, err := c.api.DeleteSecurityGroup(ctx, input); err != nil {
return err
}
c.securityGroup = ""
return nil
}
func (c *Client) authorizeSecurityGroup(ctx context.Context, input SecurityGroupInput) error {
if c.securityGroup == "" {
return errors.New("client hasn't got a security group id")
}
if err := c.authorizeSecurityGroupIngress(ctx, input.Inbound); err != nil {
return err
}
return c.authorizeSecurityGroupEgress(ctx, input.Outbound)
}
func (c *Client) authorizeSecurityGroupIngress(ctx context.Context, perms cloudtypes.Firewall) error {
if len(perms) == 0 {
return nil
}
authInput := &awsec2.AuthorizeSecurityGroupIngressInput{
GroupId: aws.String(c.securityGroup),
IpPermissions: perms.AWS(),
DryRun: aws.Bool(true),
}
// DryRun
_, err := c.api.AuthorizeSecurityGroupIngress(ctx, authInput)
if err := checkDryRunError(err); err != nil {
return err
}
authInput.DryRun = aws.Bool(false)
// Authorize
_, err = c.api.AuthorizeSecurityGroupIngress(ctx, authInput)
return err
}
func (c *Client) authorizeSecurityGroupEgress(ctx context.Context, perms cloudtypes.Firewall) error {
if len(perms) == 0 {
return nil
}
authInput := &awsec2.AuthorizeSecurityGroupEgressInput{
GroupId: aws.String(c.securityGroup),
IpPermissions: perms.AWS(),
DryRun: aws.Bool(true),
}
// DryRun
_, err := c.api.AuthorizeSecurityGroupEgress(ctx, authInput)
if err := checkDryRunError(err); err != nil {
return err
}
authInput.DryRun = aws.Bool(false)
// Authorize
_, err = c.api.AuthorizeSecurityGroupEgress(ctx, authInput)
return err
}
type SecurityGroupInput struct {
Inbound cloudtypes.Firewall
Outbound cloudtypes.Firewall
}

View File

@ -1,269 +0,0 @@
package client
import (
"context"
"errors"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateSecurityGroup(t *testing.T) {
testInput := SecurityGroupInput{
Inbound: cloudtypes.Firewall{
{
Description: "perm1",
Protocol: "TCP",
IPRange: "192.0.2.0/24",
FromPort: 22,
},
{
Description: "perm2",
Protocol: "UDP",
IPRange: "192.0.2.0/24",
FromPort: 4433,
},
},
Outbound: cloudtypes.Firewall{
{
Description: "perm3",
Protocol: "TCP",
IPRange: "192.0.2.0/24",
FromPort: 4040,
},
},
}
someErr := errors.New("failed")
var noErr error
testCases := map[string]struct {
api stubAPI
securityGroup string
input SecurityGroupInput
wantErr bool
wantSecurityGroup string
}{
"create security group": {
api: stubAPI{securityGroup: types.SecurityGroup{GroupId: aws.String("sg-test")}},
input: testInput,
wantSecurityGroup: "sg-test",
},
"create security group without permissions": {
api: stubAPI{securityGroup: types.SecurityGroup{GroupId: aws.String("sg-test")}},
input: SecurityGroupInput{},
wantSecurityGroup: "sg-test",
},
"client already has security group": {
api: stubAPI{},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"create returns nil security group ID": {
api: stubAPI{securityGroup: types.SecurityGroup{GroupId: nil}},
input: testInput,
wantErr: true,
},
"create API error": {
api: stubAPI{createSecurityGroupErr: someErr},
input: testInput,
wantErr: true,
},
"create DryRun API error": {
api: stubAPI{createSecurityGroupDryRunErr: &someErr},
input: testInput,
wantErr: true,
},
"create DryRun missing expected error": {
api: stubAPI{createSecurityGroupDryRunErr: &noErr},
input: testInput,
wantErr: true,
},
"authorize error": {
api: stubAPI{
securityGroup: types.SecurityGroup{GroupId: aws.String("sg-test")},
authorizeSecurityGroupIngressErr: someErr,
},
input: testInput,
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
client, err := newClient(tc.api)
require.NoError(err)
client.securityGroup = tc.securityGroup
err = client.CreateSecurityGroup(context.Background(), tc.input)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.wantSecurityGroup, client.securityGroup)
}
})
}
}
func TestDeleteSecurityGroup(t *testing.T) {
someErr := errors.New("failed")
var noErr error
testCases := map[string]struct {
api stubAPI
securityGroup string
wantErr bool
}{
"delete security group": {
api: stubAPI{},
securityGroup: "sg-test",
},
"client without security group": {
api: stubAPI{},
},
"delete API error": {
api: stubAPI{deleteSecurityGroupErr: someErr},
securityGroup: "sg-test",
wantErr: true,
},
"delete DryRun API error": {
api: stubAPI{deleteSecurityGroupDryRunErr: &someErr},
securityGroup: "sg-test",
wantErr: true,
},
"delete DryRun missing expected error": {
api: stubAPI{deleteSecurityGroupDryRunErr: &noErr},
securityGroup: "sg-test",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
client, err := newClient(tc.api)
require.NoError(err)
client.securityGroup = tc.securityGroup
err = client.DeleteSecurityGroup(context.Background())
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Empty(client.securityGroup)
}
})
}
}
func TestAuthorizeSecurityGroup(t *testing.T) {
testInput := SecurityGroupInput{
Inbound: cloudtypes.Firewall{
{
Description: "perm1",
Protocol: "TCP",
IPRange: " 192.0.2.0/24",
FromPort: 22,
},
{
Description: "perm2",
Protocol: "UDP",
IPRange: "192.0.2.0/24",
FromPort: 4433,
},
},
Outbound: cloudtypes.Firewall{
{
Description: "perm3",
Protocol: "TCP",
IPRange: "192.0.2.0/24",
FromPort: 4040,
},
},
}
someErr := errors.New("failed")
var noErr error
testCases := map[string]struct {
api stubAPI
securityGroup string
input SecurityGroupInput
wantErr bool
}{
"authorize": {
api: stubAPI{},
securityGroup: "sg-test",
input: testInput,
wantErr: false,
},
"client without security group": {
api: stubAPI{},
input: testInput,
wantErr: true,
},
"authorizeIngress API error": {
api: stubAPI{authorizeSecurityGroupIngressErr: someErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"authorizeIngress DryRun API error": {
api: stubAPI{authorizeSecurityGroupIngressDryRunErr: &someErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"authorizeIngress DryRun missing expected error": {
api: stubAPI{authorizeSecurityGroupIngressDryRunErr: &noErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"authorizeEgress API error": {
api: stubAPI{authorizeSecurityGroupEgressErr: someErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"authorizeEgress DryRun API error": {
api: stubAPI{authorizeSecurityGroupEgressDryRunErr: &someErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
"authorizeEgress DryRun missing expected error": {
api: stubAPI{authorizeSecurityGroupEgressDryRunErr: &noErr},
securityGroup: "sg-test",
input: testInput,
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
client, err := newClient(tc.api)
require.NoError(err)
client.securityGroup = tc.securityGroup
err = client.authorizeSecurityGroup(context.Background(), tc.input)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}

View File

@ -1,21 +0,0 @@
package client
import (
"errors"
"github.com/aws/smithy-go"
)
// checkDryRunError error checks if an error is a DryRun error.
// If the error is nil, an error is returned, since a DryRun error
// is the expected result of a DryRun operation.
func checkDryRunError(err error) error {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "DryRunOperation" {
return nil
}
if err != nil {
return err
}
return errors.New("expected APIError: DryRunOperation, but got no error at all")
}

View File

@ -1,22 +0,0 @@
package client
import (
"errors"
"testing"
"github.com/aws/smithy-go"
"github.com/stretchr/testify/assert"
)
func TestCheckDryRunError(t *testing.T) {
assert := assert.New(t)
someErr := errors.New("failed")
assert.ErrorIs(checkDryRunError(someErr), someErr)
dryRunErr := smithy.GenericAPIError{Code: "DryRunOperation"}
assert.NoError(checkDryRunError(&dryRunErr))
var nilErr error
assert.Error(checkDryRunError(nilErr))
}

View File

@ -1,58 +0,0 @@
package ec2
import "errors"
// Instance is an ec2 instance.
type Instance struct {
PublicIP string
PrivateIP string
}
// Instances is a map of ec2 Instances. The ID of an instance is used as key.
type Instances map[string]Instance
// IDs returns the IDs of all instances of the Constellation.
func (i Instances) IDs() []string {
var ids []string
for id := range i {
ids = append(ids, id)
}
return ids
}
// PublicIPs returns the public IPs of all the instances of the Constellation.
func (i Instances) PublicIPs() []string {
var ips []string
for _, instance := range i {
ips = append(ips, instance.PublicIP)
}
return ips
}
// PrivateIPs returns the private IPs of all the instances of the Constellation.
func (i Instances) PrivateIPs() []string {
var ips []string
for _, instance := range i {
ips = append(ips, instance.PrivateIP)
}
return ips
}
// GetOne return anyone instance out of the instances and its ID.
func (i Instances) GetOne() (string, Instance, error) {
for id, instance := range i {
return id, instance, nil
}
return "", Instance{}, errors.New("map is empty")
}
// GetOthers returns all instances but the one with the handed ID.
func (i Instances) GetOthers(id string) Instances {
others := make(Instances)
for key, instance := range i {
if key != id {
others[key] = instance
}
}
return others
}

View File

@ -1,71 +0,0 @@
package ec2
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIDs(t *testing.T) {
assert := assert.New(t)
testState := testInstances()
wantIDs := []string{"id-9", "id-10", "id-11", "id-12"}
assert.ElementsMatch(wantIDs, testState.IDs())
}
func TestPublicIPs(t *testing.T) {
assert := assert.New(t)
testState := testInstances()
wantIPs := []string{"192.0.2.1", "192.0.2.3", "192.0.2.5", "192.0.2.7"}
assert.ElementsMatch(wantIPs, testState.PublicIPs())
}
func TestPrivateIPs(t *testing.T) {
assert := assert.New(t)
testState := testInstances()
wantIPs := []string{"192.0.2.2", "192.0.2.4", "192.0.2.6", "192.0.2.8"}
assert.ElementsMatch(wantIPs, testState.PrivateIPs())
}
func TestGetOne(t *testing.T) {
assert := assert.New(t)
testState := testInstances()
id, instance, err := testState.GetOne()
assert.NoError(err)
assert.Contains(testState, id)
assert.Equal(testState[id], instance)
}
func TestGetOthers(t *testing.T) {
assert := assert.New(t)
testCases := testInstances().IDs()
for _, id := range testCases {
others := testInstances().GetOthers(id)
assert.NotContains(others, id)
wantInstances := testInstances()
delete(wantInstances, id)
assert.ElementsMatch(others.IDs(), wantInstances.IDs())
}
}
func testInstances() Instances {
return Instances{
"id-9": {
PublicIP: "192.0.2.1",
PrivateIP: "192.0.2.2",
},
"id-10": {
PublicIP: "192.0.2.3",
PrivateIP: "192.0.2.4",
},
"id-11": {
PublicIP: "192.0.2.5",
PrivateIP: "192.0.2.6",
},
"id-12": {
PublicIP: "192.0.2.7",
PrivateIP: "192.0.2.8",
},
}
}

View File

@ -1,18 +0,0 @@
package ec2
import "github.com/aws/aws-sdk-go-v2/service/ec2/types"
// InstanceTypes defines possible values for the SIZE positional argument.
var InstanceTypes = map[string]types.InstanceType{
"4xlarge": types.InstanceTypeC5a4xlarge,
"8xlarge": types.InstanceTypeC5a8xlarge,
"12xlarge": types.InstanceTypeC5a12xlarge,
"16xlarge": types.InstanceTypeC5a16xlarge,
"24xlarge": types.InstanceTypeC5a24xlarge,
// shorthands
"4xl": types.InstanceTypeC5a4xlarge,
"8xl": types.InstanceTypeC5a8xlarge,
"12xl": types.InstanceTypeC5a12xlarge,
"16xl": types.InstanceTypeC5a16xlarge,
"24xl": types.InstanceTypeC5a24xlarge,
}

View File

@ -1,27 +0,0 @@
package ec2
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
)
// Tag is a ec2 tag. It consits of a key and a value.
type Tag struct {
Key string
Value string
}
// Tags is a set of Tags.
type Tags []Tag
// AWS returns a AWS representation of tags.
func (t Tags) AWS() []types.Tag {
var awsTags []types.Tag
for _, tag := range t {
awsTags = append(awsTags, types.Tag{
Key: aws.String(tag.Key),
Value: aws.String(tag.Value),
})
}
return awsTags
}

View File

@ -1,37 +0,0 @@
package ec2
import (
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/stretchr/testify/assert"
)
func TestTagsAws(t *testing.T) {
assert := assert.New(t)
testTags := Tags{
{
Key: "Name",
Value: "Test",
},
{
Key: "Foo",
Value: "Bar",
},
}
wantTags := []types.Tag{
{
Key: aws.String("Name"),
Value: aws.String("Test"),
},
{
Key: aws.String("Foo"),
Value: aws.String("Bar"),
},
}
awsTags := testTags.AWS()
assert.Equal(wantTags, awsTags)
}

View File

@ -13,8 +13,6 @@ import (
func GetScalingGroupsFromConfig(stat state.ConstellationState, config *configc.Config) (coordinators, nodes cmdc.ScalingGroup, err error) {
switch {
case len(stat.EC2Instances) != 0:
return getAWSInstances(stat, config)
case len(stat.GCPCoordinators) != 0:
return getGCPInstances(stat, config)
case len(stat.AzureCoordinators) != 0:
@ -26,38 +24,6 @@ func GetScalingGroupsFromConfig(stat state.ConstellationState, config *configc.C
}
}
func getAWSInstances(stat state.ConstellationState, _ *configc.Config) (coordinators, nodes cmdc.ScalingGroup, err error) {
coordinatorID, _, err := stat.EC2Instances.GetOne()
if err != nil {
return
}
coordinatorMap := stat.EC2Instances
var coordinatorInstances cmdc.Instances
for _, node := range coordinatorMap {
coordinatorInstances = append(coordinatorInstances, cmdc.Instance(node))
}
// GroupID of coordinators is empty, since they currently do not scale.
coordinators = cmdc.ScalingGroup{
Instances: coordinatorInstances,
GroupID: "",
}
nodeMap := stat.EC2Instances.GetOthers(coordinatorID)
if len(nodeMap) == 0 {
return cmdc.ScalingGroup{}, cmdc.ScalingGroup{}, errors.New("no nodes available, can't create Constellation with one instance")
}
var nodeInstances cmdc.Instances
for _, node := range nodeMap {
nodeInstances = append(nodeInstances, cmdc.Instance(node))
}
// TODO: make min / max configurable and abstract autoscaling for different cloud providers
// TODO: GroupID of nodes is empty, since they currently do not scale.
nodes = cmdc.ScalingGroup{Instances: nodeInstances}
return
}
func getGCPInstances(stat state.ConstellationState, config *configc.Config) (coordinators, nodes cmdc.ScalingGroup, err error) {
coordinatorMap := stat.GCPCoordinators
if len(coordinatorMap) == 0 {

View File

@ -2,7 +2,6 @@ package state
import (
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/edgelesssys/constellation/cli/ec2"
)
// ConstellationState is the state of a Constellation.
@ -11,9 +10,6 @@ type ConstellationState struct {
UID string `json:"uid,omitempty"`
CloudProvider string `json:"cloudprovider,omitempty"`
EC2Instances ec2.Instances `json:"ec2instances,omitempty"`
EC2SecurityGroup string `json:"ec2securitygroup,omitempty"`
GCPNodes cloudtypes.Instances `json:"gcpnodes,omitempty"`
GCPCoordinators cloudtypes.Instances `json:"gcpcoordinators,omitempty"`
GCPNodeInstanceGroup string `json:"gcpnodeinstancegroup,omitempty"`