Skip to content

Commit

Permalink
Merge pull request #44 from Netflix/region-specific-names
Browse files Browse the repository at this point in the history
Handle case with app-level configs and region-specific cluster names
  • Loading branch information
lorin committed Jul 16, 2018
2 parents e4186a3 + 7712f2d commit 017d43a
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 15 deletions.
1 change: 1 addition & 0 deletions constrainer/constrainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/Netflix/chaosmonkey/schedule"
)

// NullConstrainer is a no-op constrainer
type NullConstrainer struct{}

func init() {
Expand Down
44 changes: 32 additions & 12 deletions eligible/eligible.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,12 @@ func clusters(group grp.InstanceGroup, cloudProvider deploy.CloudProvider, exs [
return nil, err
}

var regions []deploy.RegionName
region, ok := group.Region()
if ok {
// group is associated with a single region
regions = []deploy.RegionName{deploy.RegionName(region)}
} else {
// group is multi-region, we need to query the deployment to figure out which regions the cluster is in
regions, err = dep.GetRegionNames(names.App, account, clusterName)
if err != nil {
return nil, err
}
deployedRegions, err := dep.GetRegionNames(names.App, account, clusterName)
if err != nil {
return nil, err
}

for _, region := range regions {
for _, region := range regions(group, deployedRegions) {

if isException(exs, account, names, region) {
continue
Expand All @@ -155,6 +147,34 @@ func clusters(group grp.InstanceGroup, cloudProvider deploy.CloudProvider, exs [
return result, nil
}

// regions returns list of candidate regions for termination given app config and where cluster is deployed
func regions(group grp.InstanceGroup, deployedRegions []deploy.RegionName) []deploy.RegionName {
region, ok := group.Region()
if ok {
return regionsWhenTermScopedtoSingleRegion(region, deployedRegions)
}

return deployedRegions
}

// regionsWhenTermScopedtoSingleRegion returns a list containing either the region or empty, depending on whether the region is one of the deployed ones
func regionsWhenTermScopedtoSingleRegion(region string, deployedRegions []deploy.RegionName) []deploy.RegionName {
if contains(region, deployedRegions) {
return []deploy.RegionName{deploy.RegionName(region)}
}

return nil
}

func contains(region string, regions []deploy.RegionName) bool {
for _, r := range regions {
if region == string(r) {
return true
}
}
return false
}

const whiteListErrorMessage = "whitelist is not supported"

// isWhiteList returns true if an error is related to a whitelist
Expand Down
45 changes: 45 additions & 0 deletions eligible/eligible_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,51 @@ func TestGroupings(t *testing.T) {
}
}

func TestAppLevelGroupingWhereClustersAreRegionSpecific(t *testing.T) {
dep := &mock.Deployment{AppMap: map[string]D.AppMap{
"foo": {"prod": D.AccountInfo{CloudProvider: "aws", Clusters: D.ClusterMap{
"foo-useast1": {
"us-east-1": {"foo-useast1-v001": []D.InstanceID{"i-11111111", "i-22222222", "i-33333333"}},
},
"foo-uswest2": {
"us-west-2": {"foo-uswest2-v005": []D.InstanceID{"i-cccccccc", "i-dddddddd"}},
},
}},
}}}

group := grp.New("foo", "prod", "us-east-1", "", "")

instances, err := Instances(group, nil, dep)
if err != nil {
t.Fatalf("%+v", err)
}

if got, want := len(instances), 3; got != want {
t.Errorf("got: %d, want: %d", got, want)
}
}

func TestAppLevelGroupingWhereClusterIsInTwoRegions(t *testing.T) {
dep := &mock.Deployment{AppMap: map[string]D.AppMap{
"foo": {"prod": D.AccountInfo{CloudProvider: "aws", Clusters: D.ClusterMap{
"foo-prod": {
"us-east-1": {"foo-prod-v001": []D.InstanceID{"i-11111111", "i-22222222", "i-33333333"}},
"us-west-2": {"foo-prod-v001": []D.InstanceID{"i-aaaaaaaa", "i-bbbbbbbb", "i-cccccccc"}},
},
}}}}}

group := grp.New("foo", "prod", "", "", "")

instances, err := Instances(group, nil, dep)
if err != nil {
t.Fatalf("%+v", err)
}

if got, want := len(instances), 6; got != want {
t.Errorf("got: %d, want: %d", got, want)
}
}

func TestExceptions(t *testing.T) {
tests := []struct {
label string
Expand Down
29 changes: 26 additions & 3 deletions mock/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

package mock

import D "github.com/Netflix/chaosmonkey/deploy"
import (
"github.com/pkg/errors"

D "github.com/Netflix/chaosmonkey/deploy"
)

const cloudProvider = "aws"

Expand Down Expand Up @@ -109,8 +113,27 @@ func (d Deployment) CloudProvider(account string) (string, error) {

// GetInstanceIDs implements deploy.Deployment.GetInstanceIDs
func (d Deployment) GetInstanceIDs(app string, account D.AccountName, cloudProvider string, region D.RegionName, cluster D.ClusterName) (D.ASGName, []D.InstanceID, error) {
// asgs associated with the cluster
asgs := d.AppMap[app][account].Clusters[cluster][region]
// Return an error if the cluster doesn't exist in the region

appInfo, ok := d.AppMap[app]
if !ok {
return "", nil, errors.Errorf("no app %s", app)
}

accountInfo, ok := appInfo[account]
if !ok {
return "", nil, errors.Errorf("app %s not deployed in account %s", app, account)
}

clusterInfo, ok := accountInfo.Clusters[cluster]
if !ok {
return "", nil, errors.Errorf("no cluster %s in app:%s, account:%s", cluster)
}

asgs, ok := clusterInfo[region]
if !ok {
return "", nil, errors.Errorf("cluster %s in account %s not deployed in region %s", cluster, account, region)
}

instances := make([]D.InstanceID, 0)

Expand Down

0 comments on commit 017d43a

Please sign in to comment.