spinnaker/fromjson.go (88 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 spinnaker import ( "encoding/json" "fmt" "github.com/Netflix/chaosmonkey/v2" "github.com/pkg/errors" ) // FromJSON takes a Spinnaker JSON representation of an app // and returns a Chaos Monkey config // Example: // // { // "name": "abc", // "attributes": { // "chaosMonkey": { // "enabled": true, // "meanTimeBetweenKillsInWorkDays": 5, // "minTimeBetweenKillsInWorkDays": 1, // "grouping": "cluster", // "regionsAreIndependent": false, // }, // "exceptions" : [ // { // "account": "test", // "stack": "*", // "cluster": "*", // "region": "*" // }, // { // "account": "prod", // "stack": "*", // "cluster": "*", // "region": "eu-west-1" // }, // ] // } // } // // Example of disabled app: // // { // "name": "abc", // "attributes": { // "chaosMonkey": { // "enabled": false // } // } // } // // Example with whitelist // // { // "enabled": true, // "grouping": "app", // "meanTimeBetweenKillsInWorkDays": 4, // "minTimeBetweenKillsInWorkDays": 1, // "regionsAreIndependent": true, // "exceptions": [ // { // "account": "prod", // "region": "us-west-2", // "stack": "foo", // "detail": "bar" // } // ], // "whitelist": [ // { // "account": "test", // "stack": "*", // "region": "*", // "detail": "*" // } // ] // } func fromJSON(js []byte) (*chaosmonkey.AppConfig, error) { parsed := new(parsedJSON) err := json.Unmarshal(js, parsed) if err != nil { return nil, errors.Wrap(err, "json unmarshal failed") } if parsed.Attributes == nil { return nil, errors.New("'attributes' field missing") } if parsed.Attributes.ChaosMonkey == nil { return nil, errors.New("'attributes.chaosMonkey' field missing") } cm := parsed.Attributes.ChaosMonkey if cm.Enabled == nil { return nil, errors.New("'attributes.chaosMonkey.enabled' field missing") } // Check if mean time between kills is missing. // If not enabled, it's ok if it's missing if *cm.Enabled && cm.MeanTimeBetweenKillsInWorkDays == nil { return nil, errors.New("attributes.chaosMonkey.meanTimeBetweenKillsInWorkDays missing") } if *cm.Enabled && cm.MinTimeBetweenKillsInWorkDays == nil { return nil, errors.New("attributes.chaosMonkey.minTimeBetweenKillsInWorkDays missing") } if *cm.Enabled && (*cm.MeanTimeBetweenKillsInWorkDays <= 0) { return nil, fmt.Errorf("invalid attributes.chaosMonkey.meanTimeBetweenKillsInWorkDays: %d", cm.MeanTimeBetweenKillsInWorkDays) } grouping := chaosmonkey.Cluster switch cm.Grouping { case "app": grouping = chaosmonkey.App case "stack": grouping = chaosmonkey.Stack case "cluster": grouping = chaosmonkey.Cluster default: // If not enabled, the user may not have specified a grouping at all, // in which case we stick with the default if *cm.Enabled { return nil, errors.Errorf("Unknown grouping: %s", cm.Grouping) } } var meanTime int var minTime int if cm.MeanTimeBetweenKillsInWorkDays != nil { meanTime = *cm.MeanTimeBetweenKillsInWorkDays } if cm.MinTimeBetweenKillsInWorkDays != nil { minTime = *cm.MinTimeBetweenKillsInWorkDays } // Exceptions must have a non-blank region field for _, exception := range cm.Exceptions { if exception.Account == "" { return nil, errors.New("missing account field in exception") } if exception.Region == "" { return nil, errors.New("missing region field in exception") } } cfg := chaosmonkey.AppConfig{ Enabled: *cm.Enabled, RegionsAreIndependent: cm.RegionsAreIndependent, Grouping: grouping, MeanTimeBetweenKillsInWorkDays: meanTime, MinTimeBetweenKillsInWorkDays: minTime, Exceptions: cm.Exceptions, Whitelist: cm.Whitelist, } return &cfg, nil } // parsedJson is the parsed JSON representation type parsedJSON struct { Name string `json:"name"` Attributes *parsedAttr `json:"attributes"` } type parsedAttr struct { ChaosMonkey *parsedChaosMonkey `json:"chaosmonkey"` } type parsedChaosMonkey struct { Enabled *bool `json:"enabled"` Grouping string `json:"grouping"` MeanTimeBetweenKillsInWorkDays *int `json:"meanTimeBetweenKillsInWorkDays"` MinTimeBetweenKillsInWorkDays *int `json:"minTimeBetweenKillsInWorkDays"` RegionsAreIndependent bool `json:"regionsAreIndependent"` Exceptions []chaosmonkey.Exception `json:"exceptions"` Whitelist *[]chaosmonkey.Exception `json:"whitelist"` }