Skip to content

Commit

Permalink
release: v0.5.0 with 1Config and fix for mulog
Browse files Browse the repository at this point in the history
  • Loading branch information
BrunoBonacci committed Jan 10, 2021
1 parent 7c5ce37 commit b668a39
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 135 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,39 @@ Chaos testing and infrastructure hardening tool.

Bad stuff in progress, more to come...!


- for production environments
```
# 30% of chance to be killed on a daily basis.
1cfg -k bad-boy -e prd -v 0.5.0 -t edn SET '{:groups {:all {:attack-rate [0.30 :daily]}}}'
```


- for event logging data
```
# 30% of chance to be killed on a daily basis.
1cfg -k bad-boy -e prd -v 0.5.0 -t edn SET '
{:groups {:all {:attack-rate [0.30 :daily]}}
;; send log events to the following destinations
:mulog
{:type :multi
:publishers
[{:type :console :pretty? true}
{:type :cloudwatch :group-name "mulog"}
{:type :elasticsearch :url "http://localhost:9200/"}]}
}'
```


- for bad-boy development
```
# For delevelopment with 100% probability to kill
1cfg -b fs -k bad-boy -e local -v 0.5.0 -t edn SET '{:groups {:all {:attack-rate [1 :minute]}}}'
export DRY_RUN=1
lein run
```

## License

Copyright © 2019-2020 Bruno Bonacci - Distributed under the [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0)
7 changes: 4 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
[instaparse "1.4.10"]
[com.brunobonacci/where "0.5.5"]
[com.brunobonacci/safely "0.7.0-alpha1"]
[com.brunobonacci/mulog "0.5.0"]
[com.brunobonacci/mulog-elasticsearch "0.5.0"]
[com.brunobonacci/mulog-cloudwatch "0.5.0"]]
[com.brunobonacci/mulog "0.6.2"]
[com.brunobonacci/mulog-elasticsearch "0.6.2"]
[com.brunobonacci/mulog-cloudwatch "0.6.2"]
[com.brunobonacci/oneconfig "0.20.0"]]

:global-vars {*warn-on-reflection* true}

Expand Down
2 changes: 1 addition & 1 deletion resources/bad-boy.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.0
0.5.0
6 changes: 4 additions & 2 deletions resources/cli-grammar.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ command = atom? (<ws> atom)* <ws>*
<atom> = global-options / target

<global-options> = global-option ( <ws> global-option )*
<global-option> = help | version | dry-run | killer-run
<global-option> = help | version | dry-run | killer-run | oneconfig
help = <'-h'> | <'--help'>
version = <'-v'> | <'--version'>
dry-run = <'--dryrun'> | <'--dry-run'>
killer-run = <'--killer-run'> [ <ws> group-name ]
group-name = #'[a-zA-Z0-9_:-]+'
oneconfig = <'--1config'> [ <ws> cfg-app-name ]
cfg-app-name = #'[a-zA-Z0-9_:-]+'

target = target-name / tag / preset
target-name = glob

tag = <'tag:'> tag-name <'='> tag-value
tag-name = #'[a-zA-Z0-9_-]+'
tag-value = (<"'"> #"[^\']*" <"'">) | (<'"'> #'[^\"]*' <'"'>) | #'[a-zA-Z0-9_-]+'
Expand Down
75 changes: 75 additions & 0 deletions resources/help-old.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
NAME
bad-boy - Chaos testing and infrastructure hardening tool.
(C) 2019-2021 - Bruno Bonacci - v%s

SYNOPSIS

bad-boy [global-options] [TARGET...]

DESCRIPTION

bad-boy is a Chaos testing tool. It generates artificial failures
and other perturbations is the infrastructure to test how
applications handle such scenarios.

GLOBAL-OPTIONS

-h, --help
It displays this page.


-v, --version
It shows bad-boy's version.


--dryrun, --dry-run
It simulate the execution without performing the attack.

--1config [<app-name>]
It retrieve the configuration from 1Config table. For more
info see: https://github.com/BrunoBonacci/1config

MODES

--killer-run [<group>], (default: all)
It runs continuously and attacks based on a configuration
group. The optional parameter is the name of the configuration
in which you can define the attack rate in terms of the
percentage of the targets that will be attacked in a given
period. The default group is `all` and the attack rate is set
as `[0.30 :daily]` which means that 30%% of the targets will be
attacked on daily basis.
It can be used in conjunction with the targets selection, and
the default selection is same as preset `--default-selection`


TARGET

There are multiple ways to select the which part of your
infrastructure will be under attack.

By name: web-app* or booking-asg
It supports glob matching (? for any singe char, * for any
number of any char). If provided it will be matched against
the auto-scaling group names.

By tag: tag:<tag-name>=<tag-value>
It matches the auto-scaling groups with have a tag which
matches the given tag-name and tag-value pair.
Examples:
tag:Foo=bar
tag:resilient=true
tag:KillMe='try it'

--default-selection
It matches the auto-scaling groups with have a tag with
the following name/value `chaos-testing=opt-in`.
Practically, it is the same as: `tag:chaos-testing=opt-in`

ATTACKS

The only supported attack currently is the termination of one
instance in a auto-scaling group.


For more info please visit: https://github.com/BrunoBonacci/bad-boy
30 changes: 6 additions & 24 deletions resources/help.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
NAME
bad-boy - Chaos testing and infrastructure hardening tool.
(C) 2019 - Bruno Bonacci - v%s
(C) 2019-2021 - Bruno Bonacci - v%s

SYNOPSIS

Expand All @@ -25,6 +25,11 @@ GLOBAL-OPTIONS
--dryrun, --dry-run
It simulate the execution without performing the attack.

--1config [<app-name>]
It retrieve the configuration from 1Config table. For more
info see: https://github.com/BrunoBonacci/1config

MODES

--killer-run [<group>], (default: all)
It runs continuously and attacks based on a configuration
Expand All @@ -38,29 +43,6 @@ GLOBAL-OPTIONS
the default selection is same as preset `--default-selection`


TARGET

There are multiple ways to select the which part of your
infrastructure will be under attack.

By name: web-app* or booking-asg
It supports glob matching (? for any singe char, * for any
number of any char). If provided it will be matched against
the auto-scaling group names.

By tag: tag:<tag-name>=<tag-value>
It matches the auto-scaling groups with have a tag which
matches the given tag-name and tag-value pair.
Examples:
tag:Foo=bar
tag:resilient=true
tag:KillMe='try it'

--default-selection
It matches the auto-scaling groups with have a tag with
the following name/value `chaos-testing=opt-in`.
Practically, it is the same as: `tag:chaos-testing=opt-in`

ATTACKS

The only supported attack currently is the termination of one
Expand Down
9 changes: 5 additions & 4 deletions src/com/brunobonacci/bad_boy/command_line.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
(defn parse-options
[cli]
(->> cli
(parser)
(insta/transform
(parser)
(insta/transform
{:help #(vector :help true)
:version #(vector :version true)
:dry-run #(vector :dry-run true)
Expand All @@ -24,6 +24,7 @@

:preset (fn [[p]] {:preset p})
:killer-run (fn [& [[_ g]]] [:killer-run (keyword (str/replace (or g "all") #"^:+" ""))])
:oneconfig (fn [& [[_ g]]] [:oneconfig (or g "bad-boy")])

:command (fn [& args]
(loop [cmd {} [arg & args] args]
Expand Down Expand Up @@ -83,5 +84,5 @@
(defn build-filters
[{:keys [targets]}]
(where
(cons :or
(map build-filter targets))))
(cons :or
(map build-filter targets))))
64 changes: 34 additions & 30 deletions src/com/brunobonacci/bad_boy/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@

(def DEFAULT-CONFIG
{:run-cycle 60000 ;; 1 min

:groups
{:all
{:targets (where [(comp :chaos-testing :Tags) :is? "opt-in"])
:attack-rate [0.30 :daily]}}})
{:targets [{:tag {"chaos-testing" "opt-in"}}]
:attack-rate [0.30 :daily]}}

:default-selection {:targets [{:tag {"chaos-testing" "opt-in"}}]}
})



Expand Down Expand Up @@ -51,17 +55,17 @@
(let [response (aws/invoke client request)]
(when (-> response :Response :Errors :Error)
(throw (ex-info (-> response :Response :Errors :Error :Message)
(-> response :Response :Errors))))
(-> response :Response :Errors))))
response))



(defn tags-map
[tags & {:keys [key-prefix] :or {key-prefix nil}}]
(->> tags
(map (juxt :Key :Value))
(map (fn [[k v]] [(keyword (str key-prefix k)) v]))
(into {})))
(map (juxt :Key :Value))
(map (fn [[k v]] [(keyword (str key-prefix k)) v]))
(into {})))



Expand All @@ -71,10 +75,10 @@
lazily concatenate all the results."
[f coll]
(lazy-seq
(if (not-empty coll)
(concat
(f (first coll))
(lazy-mapcat f (rest coll))))))
(if (not-empty coll)
(concat
(f (first coll))
(lazy-mapcat f (rest coll))))))



Expand All @@ -86,13 +90,13 @@
(lazy-mapcat result-fn (lazy-query nil query)))
([page-token query]
(let [result (query-fn
(cond-> query
page-token (assoc token-name page-token)))]
(cond-> query
page-token (assoc token-name page-token)))]
(lazy-seq
(if-let [next-page (get result token-name)]
(cons result
(lazy-query next-page query))
[result]))))))
(if-let [next-page (get result token-name)]
(cons result
(lazy-query next-page query))
[result]))))))



Expand All @@ -101,9 +105,9 @@
(let [desc-asg #(aws-request asg {:op :DescribeAutoScalingGroups :request %})
lazy-desc-asg (lazy-paginated-query desc-asg :NextToken :AutoScalingGroups)]
(->> (lazy-desc-asg {:MaxRecords 100})
(map #(select-keys % [:AutoScalingGroupName :MinSize :MaxSize :DesiredCapacity :Instances :Tags]))
(map #(update % :Tags tags-map))
(filter filters))))
(map #(select-keys % [:AutoScalingGroupName :MinSize :MaxSize :DesiredCapacity :Instances :Tags]))
(map #(update % :Tags tags-map))
(filter filters))))



Expand Down Expand Up @@ -135,7 +139,7 @@
(log/infof "[attack: kill1] Found %d groups" (count groups))
(log/infof "[attack: kill1] Selected group: %s" (:AutoScalingGroupName group))
(log/infof "[attack: kill1] Selected target: %s"
(or target "There is nothing to do here. :-(, lucky day!"))
(or target "There is nothing to do here. :-(, lucky day!"))
(u/with-context
{:total-groups (count groups)
:group-instances (count ists)
Expand All @@ -151,17 +155,17 @@
[asg ec2 rand-selector filters]
(let [groups (auto-scaling-groups asg filters)
instances (->> groups
(map (juxt #(select-keys % [:AutoScalingGroupName :MinSize :MaxSize :DesiredCapacity]) :Instances))
(mapcat (fn [[asg insts]] (map (partial merge asg) insts))))
(map (juxt #(select-keys % [:AutoScalingGroupName :MinSize :MaxSize :DesiredCapacity]) :Instances))
(mapcat (fn [[asg insts]] (map (partial merge asg) insts))))
dead (rand-selector instances)]

(when (seq dead)
(log/infof "[attack: random-kill] Found %d groups and %d instances, killing: %d"
(count groups) (count instances) (count dead)))
(log/debugf "[attack: random-kill] Found %d groups and %d instances, killing: %d"
(count groups) (count instances) (count dead)))

(doseq [instance dead]
(log/infof "[attack: random-kill] Selected target: %s / %s"
(:AutoScalingGroupName instance) (:InstanceId instance))
(:AutoScalingGroupName instance) (:InstanceId instance))

(u/with-context
{:total-groups (count groups)
Expand All @@ -170,10 +174,10 @@
:group (:AutoScalingGroupName instance)
:attack-name :random-kill}
(safely
(kill-instances ec2 [(:InstanceId instance)])
:on-error
:max-retries 3
:default nil)))))
(kill-instances ec2 [(:InstanceId instance)])
:on-error
:max-retries 3
:default nil)))))



Expand All @@ -193,7 +197,7 @@
(defn killer-run
[{:keys [run-cycle] :as cfg} group]
(let [filters (get-in cfg [:groups group :targets])
selector (random-selection-by-rate DEFAULT-CONFIG group)
selector (random-selection-by-rate cfg group)
sleep* (sleeper :fix run-cycle)]
(println "Killer on the run, press CTRL-c to stop it!")
(loop []
Expand Down
Loading

0 comments on commit b668a39

Please sign in to comment.