forked from kubernetes/autoscaler
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cluster-autoscaler: Refactor AwsManager for less complexity by extrac…
…ting types Accordingly to the discussion made [here](kubernetes#46 (comment))
- Loading branch information
Showing
4 changed files
with
298 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/* | ||
Copyright 2016 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 aws | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/autoscaling" | ||
"github.com/golang/glog" | ||
) | ||
|
||
// autoScaling is the interface represents a specific aspect of the auto-scaling service provided by AWS SDK for use in CA | ||
type autoScaling interface { | ||
DescribeAutoScalingGroups(input *autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) | ||
DescribeTags(input *autoscaling.DescribeTagsInput) (*autoscaling.DescribeTagsOutput, error) | ||
SetDesiredCapacity(input *autoscaling.SetDesiredCapacityInput) (*autoscaling.SetDesiredCapacityOutput, error) | ||
TerminateInstanceInAutoScalingGroup(input *autoscaling.TerminateInstanceInAutoScalingGroupInput) (*autoscaling.TerminateInstanceInAutoScalingGroupOutput, error) | ||
} | ||
|
||
// autoScalingWrapper provides several utility methods over the auto-scaling service provided by AWS SDK | ||
type autoScalingWrapper struct { | ||
autoScaling | ||
} | ||
|
||
func (m autoScalingWrapper) getAutoscalingGroupByName(name string) (*autoscaling.Group, error) { | ||
params := &autoscaling.DescribeAutoScalingGroupsInput{ | ||
AutoScalingGroupNames: []*string{aws.String(name)}, | ||
MaxRecords: aws.Int64(1), | ||
} | ||
groups, err := m.DescribeAutoScalingGroups(params) | ||
if err != nil { | ||
glog.V(4).Infof("Failed ASG info request for %s: %v", name, err) | ||
return nil, err | ||
} | ||
if len(groups.AutoScalingGroups) < 1 { | ||
return nil, fmt.Errorf("Unable to get first autoscaling.Group for %s", name) | ||
} | ||
return groups.AutoScalingGroups[0], nil | ||
} | ||
|
||
func (m *autoScalingWrapper) getAutoscalingGroupsByNames(names []string) ([]*autoscaling.Group, error) { | ||
glog.V(6).Infof("Starting getAutoscalingGroupsByNames with names=%v", names) | ||
|
||
nameRefs := []*string{} | ||
for _, n := range names { | ||
nameRefs = append(nameRefs, aws.String(n)) | ||
} | ||
params := &autoscaling.DescribeAutoScalingGroupsInput{ | ||
AutoScalingGroupNames: nameRefs, | ||
MaxRecords: aws.Int64(maxRecordsReturnedByAPI), | ||
} | ||
description, err := m.DescribeAutoScalingGroups(params) | ||
if err != nil { | ||
glog.V(4).Infof("Failed to describe ASGs : %v", err) | ||
return nil, err | ||
} | ||
if len(description.AutoScalingGroups) < 1 { | ||
return nil, errors.New("No ASGs found") | ||
} | ||
|
||
asgs := description.AutoScalingGroups | ||
for description.NextToken != nil { | ||
description, err = m.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{ | ||
NextToken: description.NextToken, | ||
MaxRecords: aws.Int64(maxRecordsReturnedByAPI), | ||
}) | ||
if err != nil { | ||
glog.V(4).Infof("Failed to describe ASGs : %v", err) | ||
return nil, err | ||
} | ||
asgs = append(asgs, description.AutoScalingGroups...) | ||
} | ||
|
||
glog.V(6).Infof("Finishing getAutoscalingGroupsByNames asgs=%v", asgs) | ||
|
||
return asgs, nil | ||
} | ||
|
||
func (m *autoScalingWrapper) getAutoscalingGroupsByTag(key string) ([]*autoscaling.Group, error) { | ||
glog.V(6).Infof("Starting getAutoscalingGroupsByTag with key=%v", key) | ||
|
||
tags := []*autoscaling.TagDescription{} | ||
|
||
description, err := m.DescribeTags(&autoscaling.DescribeTagsInput{ | ||
Filters: []*autoscaling.Filter{ | ||
{ | ||
Name: aws.String("key"), | ||
Values: []*string{aws.String(key)}, | ||
}, | ||
}, | ||
MaxRecords: aws.Int64(maxRecordsReturnedByAPI), | ||
}) | ||
if err != nil { | ||
glog.V(4).Infof("Failed to describe ASG tags for key %s : %v", key, err) | ||
return nil, err | ||
} | ||
if len(description.Tags) < 1 { | ||
return nil, fmt.Errorf("Unable to find ASGs for tag key %s", key) | ||
} | ||
tags = append(tags, description.Tags...) | ||
|
||
for description.NextToken != nil { | ||
description, err = m.DescribeTags(&autoscaling.DescribeTagsInput{ | ||
NextToken: description.NextToken, | ||
MaxRecords: aws.Int64(maxRecordsReturnedByAPI), | ||
}) | ||
if err != nil { | ||
glog.V(4).Infof("Failed to describe ASG tags for key %s: %v", key, err) | ||
return nil, err | ||
} | ||
tags = append(tags, description.Tags...) | ||
} | ||
|
||
asgNames := []string{} | ||
for _, t := range tags { | ||
asgName := t.ResourceId | ||
asgNames = append(asgNames, *asgName) | ||
} | ||
|
||
asgs, err := m.getAutoscalingGroupsByNames(asgNames) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
glog.V(6).Infof("Finishing getAutoscalingGroupsByTag with asgs=%v", asgs) | ||
|
||
return asgs, nil | ||
} |
108 changes: 108 additions & 0 deletions
108
cluster-autoscaler/cloudprovider/aws/auto_scaling_groups.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
Copyright 2016 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 aws | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
"github.com/golang/glog" | ||
"k8s.io/apimachinery/pkg/util/wait" | ||
) | ||
|
||
type autoScalingGroups struct { | ||
registeredAsgs []*asgInformation | ||
instanceToAsg map[AwsRef]*Asg | ||
cacheMutex sync.Mutex | ||
instancesNotInManagedAsg map[AwsRef]struct{} | ||
service autoScalingWrapper | ||
} | ||
|
||
func newAutoScalingGroups(service autoScalingWrapper) *autoScalingGroups { | ||
registry := &autoScalingGroups{ | ||
registeredAsgs: make([]*asgInformation, 0), | ||
service: service, | ||
instanceToAsg: make(map[AwsRef]*Asg), | ||
instancesNotInManagedAsg: make(map[AwsRef]struct{}), | ||
} | ||
|
||
go wait.Forever(func() { | ||
registry.cacheMutex.Lock() | ||
defer registry.cacheMutex.Unlock() | ||
if err := registry.regenerateCache(); err != nil { | ||
glog.Errorf("Error while regenerating Asg cache: %v", err) | ||
} | ||
}, time.Hour) | ||
|
||
return registry | ||
} | ||
|
||
// Register registers asg in Aws Manager. | ||
func (m *autoScalingGroups) Register(asg *Asg) { | ||
m.cacheMutex.Lock() | ||
defer m.cacheMutex.Unlock() | ||
|
||
m.registeredAsgs = append(m.registeredAsgs, &asgInformation{ | ||
config: asg, | ||
}) | ||
} | ||
|
||
// FindForInstance returns AsgConfig of the given Instance | ||
func (m *autoScalingGroups) FindForInstance(instance *AwsRef) (*Asg, error) { | ||
m.cacheMutex.Lock() | ||
defer m.cacheMutex.Unlock() | ||
if config, found := m.instanceToAsg[*instance]; found { | ||
return config, nil | ||
} | ||
if _, found := m.instancesNotInManagedAsg[*instance]; found { | ||
// The instance is already known to not belong to any configured ASG | ||
// Skip regenerateCache so that we won't unnecessarily call DescribeAutoScalingGroups | ||
// See https://github.com/kubernetes/contrib/issues/2541 | ||
return nil, nil | ||
} | ||
if err := m.regenerateCache(); err != nil { | ||
return nil, fmt.Errorf("Error while looking for ASG for instance %+v, error: %v", *instance, err) | ||
} | ||
if config, found := m.instanceToAsg[*instance]; found { | ||
return config, nil | ||
} | ||
// instance does not belong to any configured ASG | ||
glog.V(6).Infof("Instance %+v is not in any ASG managed by CA. CA is now memorizing the fact not to unnecessarily call AWS API afterwards trying to find the unexistent managed ASG for the instance", *instance) | ||
m.instancesNotInManagedAsg[*instance] = struct{}{} | ||
return nil, nil | ||
} | ||
|
||
func (m *autoScalingGroups) regenerateCache() error { | ||
newCache := make(map[AwsRef]*Asg) | ||
|
||
for _, asg := range m.registeredAsgs { | ||
glog.V(4).Infof("Regenerating ASG information for %s", asg.config.Name) | ||
|
||
group, err := m.service.getAutoscalingGroupByName(asg.config.Name) | ||
if err != nil { | ||
return err | ||
} | ||
for _, instance := range group.Instances { | ||
ref := AwsRef{Name: *instance.InstanceId} | ||
newCache[ref] = asg.config | ||
} | ||
} | ||
|
||
m.instanceToAsg = newCache | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.