grp/grp.go (163 lines of code) (raw):

// Copyright 2016 Netflix, Inc. // // 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 grp holds the InstanceGroup interface package grp import ( "bytes" "encoding/json" "fmt" "github.com/SmartThingsOSS/frigga-go" "log" ) // New generates an InstanceGroup. // region, stack, and cluster may be empty strings, in which case // the group is cross-region, cross-stack, or cross-cluster // Note that stack and cluster are mutually exclusive, can specify one // but not both func New(app, account, region, stack, cluster string) InstanceGroup { return group{ app: app, account: account, region: region, stack: stack, cluster: cluster, } } // InstanceGroup represents a group of instances type InstanceGroup interface { // App returns the name of the app App() string // Account returns the name of the account Account() string // Region returns (region name, region present) // If the group is cross-region, the boolean will be false Region() (name string, ok bool) // Stack returns (region name, region present) // If the group is cross-stack, the boolean will be false Stack() (name string, ok bool) // Cluster returns (cluster name, cluster present) // If the group is cross-cluster, the boolean will be false Cluster() (name string, ok bool) // String outputs a stringified rep String() string } // Equal returns true if g1 and g2 represent the same group of instances func Equal(g1, g2 InstanceGroup) bool { if g1 == g2 { return true } if g1.App() != g2.App() { return false } if g1.Account() != g2.Account() { return false } r1, ok1 := g1.Region() r2, ok2 := g2.Region() if ok1 != ok2 { return false } if ok1 && (r1 != r2) { return false } s1, ok1 := g1.Stack() s2, ok2 := g2.Stack() if ok1 != ok2 { return false } if ok1 && (s1 != s2) { return false } c1, ok1 := g1.Cluster() c2, ok2 := g2.Cluster() if ok1 != ok2 { return false } if ok1 && (c1 != c2) { return false } return true } // String outputs a string representation of InstanceGroup suitable for logging func String(group InstanceGroup) string { var buffer bytes.Buffer writeString := func(s string) { _, _ = buffer.WriteString(s) } writeString("app=") writeString(group.App()) writeString(" account=") writeString(group.Account()) region, ok := group.Region() if ok { writeString(" region=") writeString(region) } stack, ok := group.Stack() if ok { writeString(" stack=") writeString(stack) } cluster, ok := group.Cluster() if ok { writeString(" cluster=") writeString(cluster) } return buffer.String() } type group struct { app, account, region, stack, cluster string } func (g group) String() string { return fmt.Sprintf("InstanceGroup{app=%s account=%s region=%s stack=%s cluster=%s}", g.app, g.account, g.region, g.stack, g.cluster) } func (g group) MarshalJSON() ([]byte, error) { var s = struct { App string `json:"app"` Account string `json:"account"` Region string `json:"region,omitempty"` Stack string `json:"stack,omitempty"` Cluster string `json:"cluster,omitempty"` }{ App: g.app, Account: g.account, Region: g.region, Stack: g.stack, Cluster: g.cluster, } return json.Marshal(s) } // App implements InstanceGroup.App func (g group) App() string { return g.app } // Account implements InstanceGroup.Account func (g group) Account() string { return g.account } // Region implements InstanceGroup.Region func (g group) Region() (string, bool) { if g.region == "" { return "", false } return g.region, true } // Stack implements InstanceGroup.Stack func (g group) Stack() (string, bool) { if g.stack == "" { return "", false } return g.stack, true } // Cluster implements InstanceGroup.Cluster func (g group) Cluster() (string, bool) { if g.cluster == "" { return "", false } return g.cluster, true } // AnyRegion is true if the group matches any region func AnyRegion(g InstanceGroup) bool { _, specific := g.Region() return !specific } // AnyStack is true if the group matches any stack func AnyStack(g InstanceGroup) bool { _, specific := g.Stack() return !specific } // AnyCluster is true if the group matches any cluster func AnyCluster(g InstanceGroup) bool { _, specific := g.Cluster() return !specific } // Contains returns true if the (account, region, cluster) is within the instance group func Contains(g InstanceGroup, account, region, cluster string) bool { names, err := frigga.Parse(cluster) if err != nil { log.Printf("WARNING: could not parse cluster name: %s", cluster) return false } return names.App == g.App() && string(account) == g.Account() && (AnyRegion(g) || string(region) == must(g.Region())) && (AnyStack(g) || names.Stack == must(g.Stack())) && (AnyCluster(g) || string(cluster) == must(g.Cluster())) } // must returns val if ok is true // panics otherwise func must(val string, specific bool) string { if !specific { panic("specific was unexpectedly false") } return val }