-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Changes from all commits
1e548ee
5610570
8a923bc
c214eae
bb80b4f
56d8fd3
2924a1a
51d7a9f
421da33
4e5078c
7541620
1f0774b
88f7034
61767fe
7e63b8c
eed433b
dc827d5
16c3ff5
688507a
7bb6564
c6dd0e8
f0bf137
b3e8b2d
964deae
c1bf412
bcf30a3
c885611
bcb8ee8
c518c03
96ee3a0
230432a
823ecb1
6b647ae
6f33d04
b1758d0
b6d9259
90038a8
01cf276
2a88180
afe8f3c
6f34ce7
8551199
3b29055
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
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 | ||
|
||
``` |
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 { | ||||
DescribeAutoScalingGroups(input *autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one achieves same functionality as DescribeAutoScalingGroupsPages.
I think we can reuse interface. |
||||
} | ||||
|
||||
// EC2AutoscalingGroup holds AWS Autoscaling Group information | ||||
type EC2AutoscalingGroup struct { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||
// 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) | ||||
} |
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 { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
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 | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||||
} |
There was a problem hiding this comment.
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?