From 17bc2b527c01a003ae60a6cf10fd0066400be68e Mon Sep 17 00:00:00 2001 From: Daniel Spangenberg Date: Wed, 9 Jan 2019 16:28:28 +0100 Subject: [PATCH] Add EBS e2e test --- pkg/actuators/machine/awsclient.go | 25 +++++++++++++++++++++++ pkg/actuators/machine/utils.go | 16 +++++++++++++++ pkg/client/client.go | 5 +++++ pkg/client/fake/fake.go | 5 +++++ pkg/client/mock/client_generated.go | 13 ++++++++++++ test/machines/machines_test.go | 31 +++++++++++++++++++++++++++++ test/utils/manifests.go | 30 ++++++++++++++++++++++++++-- 7 files changed, 123 insertions(+), 2 deletions(-) diff --git a/pkg/actuators/machine/awsclient.go b/pkg/actuators/machine/awsclient.go index bffef54d8d..2061dcf788 100644 --- a/pkg/actuators/machine/awsclient.go +++ b/pkg/actuators/machine/awsclient.go @@ -128,3 +128,28 @@ func (client *AwsClientWrapper) GetAvailabilityZone(machine *machinev1beta1.Mach } return *instance.Placement.AvailabilityZone, nil } + +// GetVolumes gets volumes attached to instance +func (client *AwsClientWrapper) GetVolumes(machine *machinev1beta1.Machine) (map[string]map[string]interface{}, error) { + instance, err := getRunningInstance(machine, client.client) + if err != nil { + return nil, err + } + if instance.BlockDeviceMappings == nil { + return nil, err + } + volumes := make(map[string]map[string]interface{}, len(instance.BlockDeviceMappings)) + for _, blockDeviceMapping := range instance.BlockDeviceMappings { + volume, err := getVolume(client.client, *blockDeviceMapping.Ebs.VolumeId) + if err != nil { + return volumes, err + } + volumes[*blockDeviceMapping.DeviceName] = map[string]interface{}{ + "id": *volume.VolumeId, + "iops": *volume.Iops, + "size": *volume.Size, + "type": *volume.VolumeType, + } + } + return volumes, nil +} diff --git a/pkg/actuators/machine/utils.go b/pkg/actuators/machine/utils.go index 09981e1f63..a06740919c 100644 --- a/pkg/actuators/machine/utils.go +++ b/pkg/actuators/machine/utils.go @@ -113,6 +113,22 @@ func getInstances(machine *machinev1.Machine, client awsclient.Client, instanceS return instances, nil } +func getVolume(client awsclient.Client, volumeID string) (*ec2.Volume, error) { + request := &ec2.DescribeVolumesInput{ + VolumeIds: []*string{&volumeID}, + } + result, err := client.DescribeVolumes(request) + if err != nil { + return &ec2.Volume{}, err + } + + if len(result.Volumes) != 1 { + return &ec2.Volume{}, fmt.Errorf("unable to get volume ID: %q", volumeID) + } + + return result.Volumes[0], nil +} + // terminateInstances terminates all provided instances with a single EC2 request. func terminateInstances(client awsclient.Client, instances []*ec2.Instance) error { instanceIDs := []*string{} diff --git a/pkg/client/client.go b/pkg/client/client.go index 58b22d7492..ce7092a4cd 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -56,6 +56,7 @@ type Client interface { RunInstances(*ec2.RunInstancesInput) (*ec2.Reservation, error) DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) TerminateInstances(*ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) + DescribeVolumes(*ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) ELBv2DescribeLoadBalancers(*elbv2.DescribeLoadBalancersInput) (*elbv2.DescribeLoadBalancersOutput, error) @@ -101,6 +102,10 @@ func (c *awsClient) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2 return c.ec2Client.TerminateInstances(input) } +func (c *awsClient) DescribeVolumes(input *ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) { + return c.ec2Client.DescribeVolumes(input) +} + func (c *awsClient) RegisterInstancesWithLoadBalancer(input *elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) { return c.elbClient.RegisterInstancesWithLoadBalancer(input) } diff --git a/pkg/client/fake/fake.go b/pkg/client/fake/fake.go index fad60c5f5d..bb9c6ce5ab 100644 --- a/pkg/client/fake/fake.go +++ b/pkg/client/fake/fake.go @@ -97,6 +97,11 @@ func (c *awsClient) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2 return &ec2.TerminateInstancesOutput{}, nil } +func (c *awsClient) DescribeVolumes(input *ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) { + // Feel free to extend the returned values + return &ec2.DescribeVolumesOutput{}, nil +} + func (c *awsClient) RegisterInstancesWithLoadBalancer(input *elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) { // Feel free to extend the returned values return &elb.RegisterInstancesWithLoadBalancerOutput{}, nil diff --git a/pkg/client/mock/client_generated.go b/pkg/client/mock/client_generated.go index 90245af706..11233bcb91 100644 --- a/pkg/client/mock/client_generated.go +++ b/pkg/client/mock/client_generated.go @@ -139,6 +139,19 @@ func (mr *MockClientMockRecorder) TerminateInstances(arg0 interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateInstances", reflect.TypeOf((*MockClient)(nil).TerminateInstances), arg0) } +// DescribeVolumes mocks base method +func (m *MockClient) DescribeVolumes(arg0 *ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) { + ret := m.ctrl.Call(m, "DescribeVolumes", arg0) + ret0, _ := ret[0].(*ec2.DescribeVolumesOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeVolumes indicates an expected call of DescribeVolumes +func (mr *MockClientMockRecorder) DescribeVolumes(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeVolumes", reflect.TypeOf((*MockClient)(nil).DescribeVolumes), arg0) +} + // RegisterInstancesWithLoadBalancer mocks base method func (m *MockClient) RegisterInstancesWithLoadBalancer(arg0 *elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) { ret := m.ctrl.Call(m, "RegisterInstancesWithLoadBalancer", arg0) diff --git a/test/machines/machines_test.go b/test/machines/machines_test.go index 0d935633db..024cee48a0 100644 --- a/test/machines/machines_test.go +++ b/test/machines/machines_test.go @@ -201,6 +201,37 @@ var _ = framework.SigKubeDescribe("Machines", func() { f.DeleteMachineAndWait(testMachine, acw) }) + It("Can create EBS volumes", func() { + testMachineProviderSpec, err := utils.TestingMachineProviderSpecWithEBS(awsCredSecret.Name, cluster.Name) + Expect(err).NotTo(HaveOccurred()) + testMachine := manifests.TestingMachine(cluster.Name, cluster.Namespace, testMachineProviderSpec) + f.CreateMachineAndWait(testMachine, acw) + machinesToDelete.AddMachine(testMachine, f, acw) + + volumes, err := acw.GetVolumes(testMachine) + Expect(err).NotTo(HaveOccurred()) + + By("Checking EBS volume mount", func() { + Expect(volumes).To(HaveKey("/dev/sda1")) + }) + + By("Checking EBS volume size", func() { + Expect(volumes["/dev/sda1"]["size"].(int64)).To(Equal(int64(142))) + }) + + By("Checking EBS volume type", func() { + Expect(volumes["/dev/sda1"]["type"].(string)).To(Equal("gp2")) + }) + + By("Checking only root volume get's modified", func() { + for dev, volume := range volumes { + if dev != "/dev/sda1" { + Expect(volume["size"].(int64)).ToNot(Equal(int64(142))) + } + } + }) + }) + It("Can deploy compute nodes through machineset", func() { // Any controller linking kubernetes node with its machine // needs to run inside the cluster where the node is registered. diff --git a/test/utils/manifests.go b/test/utils/manifests.go index ea39b997d3..bc1fb400a9 100644 --- a/test/utils/manifests.go +++ b/test/utils/manifests.go @@ -25,9 +25,9 @@ func GenerateAwsCredentialsSecretFromEnv(secretName, namespace string) *apiv1.Se } } -func TestingMachineProviderSpec(awsCredentialsSecretName string, clusterID string) (machinev1beta1.ProviderSpec, error) { +func testingAWSMachineProviderSpec(awsCredentialsSecretName string, clusterID string) *providerconfigv1.AWSMachineProviderConfig { publicIP := true - machinePc := &providerconfigv1.AWSMachineProviderConfig{ + return &providerconfigv1.AWSMachineProviderConfig{ AMI: providerconfigv1.AWSResourceReference{ Filters: []providerconfigv1.Filter{ { @@ -86,7 +86,33 @@ func TestingMachineProviderSpec(awsCredentialsSecretName string, clusterID strin }, PublicIP: &publicIP, } +} +func TestingMachineProviderSpec(awsCredentialsSecretName string, clusterID string) (machinev1beta1.ProviderSpec, error) { + machinePc := testingAWSMachineProviderSpec(awsCredentialsSecretName, clusterID) + codec, err := providerconfigv1.NewCodec() + if err != nil { + return machinev1beta1.ProviderSpec{}, fmt.Errorf("failed creating codec: %v", err) + } + config, err := codec.EncodeProviderSpec(machinePc) + if err != nil { + return machinev1beta1.ProviderSpec{}, fmt.Errorf("EncodeToProviderConfig failed: %v", err) + } + return *config, nil +} + +func TestingMachineProviderSpecWithEBS(awsCredentialsSecretName string, clusterID string) (machinev1beta1.ProviderSpec, error) { + volumeSize := int64(142) + volumeType := "gp2" + machinePc := testingAWSMachineProviderSpec(awsCredentialsSecretName, clusterID) + machinePc.BlockDevices = []providerconfigv1.BlockDeviceMappingSpec{ + { + EBS: &providerconfigv1.EBSBlockDeviceSpec{ + VolumeSize: &volumeSize, + VolumeType: &volumeType, + }, + }, + } codec, err := providerconfigv1.NewCodec() if err != nil { return machinev1beta1.ProviderSpec{}, fmt.Errorf("failed creating codec: %v", err)