Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added OnDemand and Spot Price models addressing #131 #486

Closed
wants to merge 43 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1e548ee
Added OnDemand and Spot Price models addressing #131
mrcrgl Nov 22, 2017
5610570
gofmt -s
mrcrgl Nov 22, 2017
8a923bc
Fixed broken instance-info test; Added areness of tenancy and effecti…
mrcrgl Nov 23, 2017
c214eae
Merge branch 'master' into issue/131
mrcrgl Nov 23, 2017
bb80b4f
Add comments to conversion functions to clarify the use
Nov 23, 2017
56d8fd3
Added field comments to EC2LaunchConfiguration
Nov 23, 2017
2924a1a
Added comments and apot review suggestions
mrcrgl Nov 23, 2017
51d7a9f
Fixed awsCloudProvider > priceModel binding
mrcrgl Nov 25, 2017
421da33
Add ec2 instance information sync info logging
mrcrgl Nov 25, 2017
4e5078c
Removed `goto` statement
mrcrgl Dec 2, 2017
7541620
Merge branch 'issue/131'
mrcrgl Dec 5, 2017
1f0774b
Add new aws ec2 instance types
mrcrgl Dec 5, 2017
88f7034
Fixed OnDemand price descriptor to use aws regions instead of availab…
mrcrgl Jan 4, 2018
61767fe
Fixed NPE when asking for unlinked instance types
mrcrgl Jan 8, 2018
7e63b8c
Renewed static instance type reference list
mrcrgl Jan 8, 2018
eed433b
Fixed AWS filter
mrcrgl Jan 10, 2018
dc827d5
Merge remote-tracking branch 'origin/master'
mrcrgl Mar 26, 2018
16c3ff5
Merge remote-tracking branch 'origin/master'
mrcrgl Mar 26, 2018
688507a
Provide ASG information in node template
mrcrgl Mar 26, 2018
7bb6564
Merge remote-tracking branch 'fid-dev/issue/131' into issue/131
mrcrgl Mar 26, 2018
c6dd0e8
go fmt
mrcrgl Mar 26, 2018
f0bf137
Merge remote-tracking branch 'origin/master'
mrcrgl May 27, 2018
b3e8b2d
Merge remote-tracking branch 'origin/master'
mrcrgl May 27, 2018
964deae
Merge branch 'master' into issue/131
mrcrgl May 27, 2018
c1bf412
Merge branch 'master' into issue/131
mrcrgl Jul 30, 2018
bcf30a3
Fixed interfaces according changes in master
mrcrgl Jul 30, 2018
c885611
Compact mock data
mrcrgl Jul 30, 2018
bcb8ee8
optimized instance info test cases
mrcrgl Jul 30, 2018
c518c03
optimized instance info test cases
mrcrgl Jul 30, 2018
96ee3a0
go fmt
mrcrgl Jul 30, 2018
230432a
always keep the last item in history
Jan 9, 2019
823ecb1
Use converters from aws package
mrcrgl Jan 9, 2019
6b647ae
Don't filter by time as long as the cache is empty
mrcrgl Jan 9, 2019
6f33d04
Merge remote-tracking branch 'fid-dev/issue/131' into issue/131
mrcrgl Jan 9, 2019
b1758d0
added handling for nil prices
Jan 9, 2019
b6d9259
go fmt
Jan 9, 2019
90038a8
fixed price calculation and tests
ylallemant Jan 11, 2019
01cf276
added boilerplate header
Jan 11, 2019
2a88180
Merge remote-tracking branch 'kubernetes/master' into issue/131
Jan 18, 2019
afe8f3c
go fmt...
Jan 18, 2019
6f34ce7
use AWS SDK for OnDemand Nodes (#5)
ylallemant Jan 23, 2019
8551199
split AWS sessions for OnDemand and Spot pricing API
Jan 24, 2019
3b29055
removed old REST API calls
Feb 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,093 changes: 1,551 additions & 1,542 deletions cluster-autoscaler/Godeps/Godeps.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions cluster-autoscaler/cloudprovider/aws/api/UPDATE_TEST_DATA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Update AWS test data
=====

Install AWS CLI Tools

Update

```bash
cd ${GOPATH}/src/k8s.io/autoscaler

# update on demand data
aws pricing get-products \
--region=us-east-1 \
--service-code=AmazonEC2 \
--filter Type=TERM_MATCH,Field=preInstalledSw,Value=NA \
Type=TERM_MATCH,Field=location,Value="EU (Ireland)" \
Type=TERM_MATCH,Field=instanceType,Value="m4.xlarge" \
Type=TERM_MATCH,Field=capacitystatus,Value=Used \
> ./cluster-autoscaler/cloudprovider/aws/api/pricing_ondemand_eu-west-1.json

# update spot data

```
79 changes: 79 additions & 0 deletions cluster-autoscaler/cloudprovider/aws/api/ec2_autoscaling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/autoscaling"
)

type awsEC2AutoscalingGroupService interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move to cloudprovider/aws/auto_scaling.go?

DescribeAutoScalingGroups(input *autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one achieves same functionality as DescribeAutoScalingGroupsPages.

DescribeAutoScalingGroupsPages(input *autoscaling.DescribeAutoScalingGroupsInput, fn func(*autoscaling.DescribeAutoScalingGroupsOutput, bool) bool) error

I think we can reuse interface.

}

// EC2AutoscalingGroup holds AWS Autoscaling Group information
type EC2AutoscalingGroup struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In auto_scaling.go it returns aws autoscaling group which would be used later in buildAsgFromAWS to build struct in CA. All these field are covered. Again, please move to existing API and only build required struct if needed.

// Name of the autoscaling group
Name string
// LaunchConfigurationName describes the name of the corresponding launch configuration
LaunchConfigurationName string
// AvailabilityZones that are used by this autoscaling group
AvailabilityZones []string
}

// NewEC2AutoscalingService is the constructor of autoscalingService which is a wrapper for the AWS EC2
// Autoscaling Group API
func NewEC2AutoscalingService(awsEC2Service awsEC2AutoscalingGroupService) *autoscalingService {
return &autoscalingService{service: awsEC2Service}
}

type autoscalingService struct {
service awsEC2AutoscalingGroupService
}

// DescribeAutoscalingGroup returns the corresponding EC2AutoscalingGroup by the given autoscaling group name
func (ass *autoscalingService) DescribeAutoscalingGroup(autoscalingGroupName string) (*EC2AutoscalingGroup, error) {
req := &autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []*string{&autoscalingGroupName},
}

for {
res, err := ass.service.DescribeAutoScalingGroups(req)
if err != nil {
return nil, err
}

for _, group := range res.AutoScalingGroups {
if *group.AutoScalingGroupName == autoscalingGroupName {
return &EC2AutoscalingGroup{
Name: *group.AutoScalingGroupName,
LaunchConfigurationName: *group.LaunchConfigurationName,
AvailabilityZones: aws.StringValueSlice(group.AvailabilityZones),
}, nil
}
}

req.NextToken = res.NextToken
if req.NextToken == nil {
break
}
}

return nil, fmt.Errorf("autoscaling group named %s not found", autoscalingGroupName)
}
159 changes: 159 additions & 0 deletions cluster-autoscaler/cloudprovider/aws/api/ec2_autoscaling_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
"errors"
"testing"

"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/stretchr/testify/assert"
)

func TestAutoscalingService_DescribeAutoscalingGroup(t *testing.T) {
type testCase struct {
asName string
expectError bool
expectResult bool
}
type cases []testCase

asName1 := "k8s-AutoscalingGroupWorker-TTTTTTTTTTTTT"
asName2 := "k8s-AutoscalingGroupWorker-YYYYYYYYYYYYY"
asName3 := "k8s-AutoscalingGroupWorker-XXXXXXXXXXXXX"
lcName1 := "k8s-LaunchConfigurationWorker-TTTTTTTTTTTTT"
lcName2 := "k8s-LaunchConfigurationWorker-YYYYYYYYYYYYY"
azName1 := "us-east-1a"
azName2 := "us-east-1b"

service := NewEC2AutoscalingService(newFakeAutoscalingService(
newAutoscalingMock(asName1, lcName1, azName1, azName2),
newAutoscalingMock(asName2, lcName2, azName1, azName2),
))

tcs := cases{
{ // good case: common case
asName1,
false,
true,
},
{ // good case: common case
asName2,
false,
true,
},
{ // error case: unknown autoscaling group
asName3,
true,
false,
},
}

for id, tc := range tcs {
out, err := service.DescribeAutoscalingGroup(tc.asName)
if tc.expectError {
assert.Error(t, err, fmt.Sprintf("case %d", id))
assert.Nil(t, out, fmt.Sprintf("case %d", id))
} else {
assert.NoError(t, err, fmt.Sprintf("case %d", id))
assert.NotNil(t, out, fmt.Sprintf("case %d", id))
}
if tc.expectResult {
assert.Equal(t, tc.asName, out.Name, fmt.Sprintf("case %d", id))
}

}
}

func newFakeAutoscalingService(ams ...autoscalingMock) *fakeAutoscalingService {
m := make(map[string]*autoscaling.Group)

for _, am := range ams {
m[am.name] = am.asg
}

return &fakeAutoscalingService{m, []string{"token-a", "token-b"}}
}

func newAutoscalingMock(asName, lcName string, availabilityZones ...string) autoscalingMock {
return autoscalingMock{
asg: &autoscaling.Group{
AvailabilityZones: aws.StringSlice(availabilityZones),
LaunchConfigurationName: aws.String(lcName),
AutoScalingGroupName: aws.String(asName),
},
name: asName,
}
}

type autoscalingMock struct {
asg *autoscaling.Group
name string
}

type fakeAutoscalingService struct {
mocks map[string]*autoscaling.Group
tokens []string
}

func (lcs *fakeAutoscalingService) DescribeAutoScalingGroups(input *autoscaling.DescribeAutoScalingGroupsInput) (output *autoscaling.DescribeAutoScalingGroupsOutput, err error) {
output = new(autoscaling.DescribeAutoScalingGroupsOutput)

output.NextToken, err = nextToken(lcs.tokens, input.NextToken)
if err != nil {
return
}

if output.NextToken != nil {
return
}

output.AutoScalingGroups = make([]*autoscaling.Group, 0)
for _, name := range input.AutoScalingGroupNames {
if item, found := lcs.mocks[*name]; found {
output.AutoScalingGroups = append(output.AutoScalingGroups, item)
}
}

return
}

func nextToken(tokenChain []string, userToken *string) (*string, error) {
tokenChainLen := len(tokenChain)
if tokenChainLen == 0 {
return nil, nil
}

if userToken == nil {
return &tokenChain[0], nil
}

for i, token := range tokenChain {
if *userToken == token {
next := i + 1
if next < tokenChainLen {
return &tokenChain[next], nil
}
return nil, nil
}
}

return nil, errors.New("invalid token")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
"fmt"
"strconv"

"github.com/aws/aws-sdk-go/aws"

"github.com/aws/aws-sdk-go/service/autoscaling"
)

type awsEC2LaunchConfigurationService interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this PR is outdated. It's included in cloudprivider/aws/autoscaling.go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DescribeLaunchConfigurations(*autoscaling.DescribeLaunchConfigurationsInput) (*autoscaling.DescribeLaunchConfigurationsOutput, error)

DescribeLaunchConfigurations(input *autoscaling.DescribeLaunchConfigurationsInput) (*autoscaling.DescribeLaunchConfigurationsOutput, error)
}

// EC2LaunchConfiguration holds AWS EC2 Launch Configuration information
type EC2LaunchConfiguration struct {
// HasSpotMarkedBid is true if the launch configuration uses the spot marked
HasSpotMarkedBid bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add comments to the fields.

// SpotPrice is the bid price on the spot marked the autoscaling group uses
SpotPrice float64
// Name of the launch configuration
Name string
// InstanceType of the underlying instance described in the launch configuration
InstanceType string
}

// NewEC2LaunchConfigurationService is the constructor of launchConfigurationService which is a wrapper for
// the AWS EC2 LaunchConfiguration API
func NewEC2LaunchConfigurationService(awsEC2Service awsEC2LaunchConfigurationService) *launchConfigurationService {
return &launchConfigurationService{service: awsEC2Service}
}

type launchConfigurationService struct {
service awsEC2LaunchConfigurationService
}

// DescribeLaunchConfiguration returns the corresponding launch configuration by the given launch configuration name.
func (lcs *launchConfigurationService) DescribeLaunchConfiguration(launchConfigurationName string) (*EC2LaunchConfiguration, error) {
req := &autoscaling.DescribeLaunchConfigurationsInput{
LaunchConfigurationNames: []*string{&launchConfigurationName},
}

for {
res, err := lcs.service.DescribeLaunchConfigurations(req)
if err != nil {
return nil, err
}

for _, lc := range res.LaunchConfigurations {

if *lc.LaunchConfigurationName == launchConfigurationName {
var p float64

if lc.SpotPrice != nil {
p, err = strconv.ParseFloat(aws.StringValue(lc.SpotPrice), 64)
if err != nil {
return nil, fmt.Errorf("failed to parse price: %v", err)
}
}

return &EC2LaunchConfiguration{
HasSpotMarkedBid: lc.SpotPrice != nil,
SpotPrice: p,
Name: *lc.LaunchConfigurationName,
InstanceType: *lc.InstanceType,
}, nil
}
}

req.NextToken = res.NextToken
if req.NextToken == nil {
break
}
}

return nil, fmt.Errorf("launch configuration named %s not found", launchConfigurationName)
}
Loading