Showing preview only (430K chars total). Download the full file or copy to clipboard to get everything.
Repository: Netflix/chaosmonkey
Branch: master
Commit: eaa28fb761c0
Files: 102
Total size: 404.5 KB
Directory structure:
gitextract_082sx3zv/
├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── NOTICE
├── OSSMETADATA
├── README.md
├── cal/
│ ├── cal.go
│ └── cal_test.go
├── chaosmonkey.go
├── chaosmonkey_test.go
├── clock/
│ └── clock.go
├── cmd/
│ └── chaosmonkey/
│ └── main.go
├── command/
│ ├── chaosmonkey.go
│ ├── command.go
│ ├── dumpconfig.go
│ ├── dumpmonkeyconfig.go
│ ├── eligible.go
│ ├── fetchschedule.go
│ ├── install.go
│ ├── install_test.go
│ ├── migrate.go
│ ├── osutil.go
│ ├── outage.go
│ ├── regions.go
│ ├── schedule.go
│ ├── schedule_int_test.go
│ ├── schedule_test.go
│ └── terminate.go
├── config/
│ ├── config.go
│ ├── config_test.go
│ ├── monkey.go
│ ├── monkey_test.go
│ └── param/
│ └── param.go
├── constrainer/
│ └── constrainer.go
├── decryptor/
│ └── decryptor.go
├── deploy/
│ ├── app.go
│ ├── asg.go
│ ├── deploy_test.go
│ ├── deployment.go
│ ├── eligible_instance_groups.go
│ └── eligible_instance_groups_test.go
├── deps/
│ └── deps.go
├── docKey.enc
├── docs/
│ ├── Configuration-file-format.md
│ ├── Configuring-behavior-via-Spinnaker.md
│ ├── How-to-deploy.md
│ ├── Running-locally.md
│ ├── Termination-behavior.md
│ ├── dev/
│ │ ├── Running-tests.md
│ │ └── Vendoring-dependencies.md
│ ├── index.md
│ └── plugins/
│ ├── Constrainer.md
│ ├── Decryptor.md
│ ├── Error-counter.md
│ ├── Outage-checker.md
│ ├── Tracker.md
│ └── index.md
├── eligible/
│ ├── eligible.go
│ ├── eligible_test.go
│ ├── instances_canary_test.go
│ └── instances_test.go
├── env/
│ └── env.go
├── errorcounter/
│ └── errorcounter.go
├── go.mod
├── go.sum
├── grp/
│ ├── grp.go
│ └── grp_test.go
├── migration/
│ ├── migrations.go
│ └── mysql/
│ └── 1.0.0_initial_schema.sql
├── mkdocs.yml
├── mock/
│ ├── configgetter.go
│ ├── deployment.go
│ ├── deps.go
│ ├── install.go
│ ├── instance.go
│ ├── mock.go
│ ├── outage.go
│ └── terminator.go
├── mysql/
│ ├── checker_test.go
│ ├── mysql.go
│ ├── mysql_test.go
│ ├── no_kills_since_test.go
│ └── schedstore_test.go
├── outage/
│ └── outage.go
├── schedstore/
│ └── schedstore.go
├── schedule/
│ ├── constrainer.go
│ ├── schedule.go
│ └── schedule_test.go
├── spinnaker/
│ ├── config.go
│ ├── fromjson.go
│ ├── fromjson_test.go
│ ├── spinnaker.go
│ ├── terminator.go
│ ├── terminator_test.go
│ └── urls.go
├── term/
│ ├── term.go
│ ├── term_ext_test.go
│ ├── terminate_test.go
│ └── terminator.go
├── tracker/
│ └── tracker.go
└── update-docs.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
coverage.out
.idea/
================================================
FILE: .travis.yml
================================================
# sudo is required for docker
sudo: required
language: go
go:
- "1.20.x"
env:
- DEPLOY_DOCS="$(if [[ $TRAVIS_BRANCH == 'master' && $TRAVIS_PULL_REQUEST == 'false' ]]; then echo -n 'true' ; else echo -n 'false' ; fi)"
services:
- docker
install:
- docker pull mysql:8.0
- go install golang.org/x/lint/golint@latest
- go install github.com/kisielk/errcheck@latest
- go get github.com/spf13/afero@v0.0.0-20160919210114-52e4a6cfac46
- go get github.com/fsnotify/fsnotify@v1.3.2-0.20160816051541-f12c6236fe7b
# With the "docker" tag enabled on go test invocation (-tags docker)
# the mysql:5.6 docker container will be started
# and the mysql tests will connect to this container
# This requires us to stop the pre-installed mysql server
script:
- sudo service mysql stop
- diff -u <(echo -n) <(gofmt -d `find . -name '*.go' | grep -Ev '/vendor/|/migration'`)
- go list ./... | grep -Ev '/vendor/|/migration' | xargs -L1 golint
- go vet `go list ./... | grep -v /vendor/`
- errcheck -ignore 'io:Close' -ignoretests `go list ./... | grep -v /vendor/`
- go test -v ./...
after_success:
- ./update-docs.sh
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 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.
================================================
FILE: Makefile
================================================
.PHONY: check fmt lint errcheck test build
SHELL:=/bin/bash
build: check
go build github.com/Netflix/chaosmonkey/cmd/chaosmonkey
check: fmt lint errcheck
gofmt: fmt
fmt:
diff -u <(echo -n) <(gofmt -d `find . -name '*.go' | grep -Ev '/vendor/|/migration'`)
lint:
go list ./... | grep -Ev '/vendor/|/migration' | xargs -L1 golint
errcheck:
errcheck -ignore 'io:Close' -ignoretests `go list ./... | grep -v /vendor/`
test:
go test -v ./...
# Coverage testing
cover:
echo 'mode: atomic' > coverage.out
go list ./... | grep -Ev '/vendor/|/migration' | xargs -n1 -I{} sh -c 'go test -covermode=atomic -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.out' && rm coverage.tmp
go tool cover -html=coverage.out
fix:
gofmt -w -s `find . -name '*.go' | grep -Ev '/vendor/|/migration'`
================================================
FILE: NOTICE
================================================
Chaos Monkey randomly terminates instances.
Copyright (C) 2016 Netflix, Inc.
Chaos Monkey makes use of several third-party OSS libraries that are included in
the vendor directory.
# go-spew
Go-spew is used for pretty-printing some Go data structures for debugging.
http://github.com/davecgh/go-spew
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# fsnotify
File system notifications for Go
http://github.com/fsnotify/fsnotify
Copyright (c) 2012 The Go Authors. All rights reserved.
Copyright (c) 2012 fsnotify Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Go MySQL Driver
Go MySQL Driver implements a MySQL driver.
http://github.com/go-sql-driver/mysql
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
# HCL
HCL (HashiCorp Configuration Language) is a configuration language built by HashiCorp.
http://github.com/hashicorp/hcl
Mozilla Public License, version 2.0
1. Definitions
1.1. “Contributor”
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. “Contributor Version”
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor’s Contribution.
1.3. “Contribution”
means Covered Software of a particular Contributor.
1.4. “Covered Software”
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. “Incompatible With Secondary Licenses”
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of version
1.1 or earlier of the License, but not also under the terms of a
Secondary License.
1.6. “Executable Form”
means any form of the work other than Source Code Form.
1.7. “Larger Work”
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
1.8. “License”
means this document.
1.9. “Licensable”
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed by
this License.
1.10. “Modifications”
means any of the following:
a. any file in Source Code Form that results from an addition to, deletion
from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. “Patent Claims” of a Contributor
means any patent claim(s), including without limitation, method, process,
and apparatus claims, in any patent Licensable by such Contributor that
would be infringed, but for the grant of the License, by the making,
using, selling, offering for sale, having made, import, or transfer of
either its Contributions or its Contributor Version.
1.12. “Secondary License”
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. “Source Code Form”
means the form of the work preferred for making modifications.
1.14. “You” (or “Your”)
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, “control” means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or as
part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions
or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party’s
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of its
Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the
notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this License
(see Section 10.2) or under the terms of a Secondary License (if permitted
under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its Contributions
are its original creation(s) or it has sufficient rights to grant the
rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form
of the Covered Software is governed by the terms of this License, and how
they can obtain a copy of this License. You may not attempt to alter or
restrict the recipients’ rights in the Source Code Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this License,
or sublicense it under different terms, provided that the license for
the Executable Form does not attempt to limit or alter the recipients’
rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software
with a work governed by one or more Secondary Licenses, and the Covered
Software is not Incompatible With Secondary Licenses, this License permits
You to additionally distribute such Covered Software under the terms of
such Secondary License(s), so that the recipient of the Larger Work may, at
their option, further distribute the Covered Software under the terms of
either this License or such Secondary License(s).
3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations
of liability) contained within the Source Code Form of the Covered
Software, except that You may alter any license notices to the extent
required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on behalf
of any Contributor. You must make it absolutely clear that any such
warranty, support, indemnity, or liability obligation is offered by You
alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: (a) comply with the terms of this License
to the maximum extent possible; and (b) describe the limitations and the code
they affect. Such description must be placed in a text file included with all
distributions of the Covered Software under this License. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
if such Contributor fails to notify You of the non-compliance by some
reasonable means prior to 60 days after You have come back into compliance.
Moreover, Your grants from a particular Contributor are reinstated on an
ongoing basis if such Contributor notifies You of the non-compliance by
some reasonable means, this is the first time You have received notice of
non-compliance with this License from such Contributor, and You become
compliant prior to 30 days after Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims,
and cross-claims) alleging that a Contributor Version directly or
indirectly infringes any patent, then the rights granted to You by any and
all Contributors for the Covered Software under Section 2.1 of this License
shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an “as is” basis, without
warranty of any kind, either expressed, implied, or statutory, including,
without limitation, warranties that the Covered Software is free of defects,
merchantable, fit for a particular purpose or non-infringing. The entire
risk as to the quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You (not any
Contributor) assume the cost of any necessary servicing, repair, or
correction. This disclaimer of warranty constitutes an essential part of this
License. No use of any Covered Software is authorized under this License
except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from such
party’s negligence to the extent applicable law prohibits such limitation.
Some jurisdictions do not allow the exclusion or limitation of incidental or
consequential damages, so this exclusion and limitation may not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts of
a jurisdiction where the defendant maintains its principal place of business
and such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a party’s ability to bring cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of
the License under which You originally received the Covered Software, or
under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a modified
version of this License if you rename the license and remove any
references to the name of the license steward (except to note that such
modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is “Incompatible
With Secondary Licenses”, as defined by
the Mozilla Public License, v. 2.0.
# fs
Package fs provides filesystem-related functions.
http://github.com/kr/fs
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# properties
properties is a Go library for reading and writing properties files.
http://github.com/magiconair/properties
goproperties - properties file decoder for Go
Copyright (c) 2013-2014 - Frank Schroeder
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# mapstructure
Go library for decoding generic map values into native Go structures.
http://github.com/mitchellh/mapstructure
The MIT License (MIT)
Copyright (c) 2013 Mitchell Hashimoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Go-toml
Go library for the TOML language
http://github.com/pelletier/go-toml
The MIT License (MIT)
Copyright (c) 2013 - 2016 Thomas Pelletier, Eric Anderton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# errors
Simple error handling primitives
http://github.com/pkg/errors
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# sftp
SFTP support for the go.crypto/ssh package
http://github.com/pkg/sftp
Copyright (c) 2013, Dave Cheney
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# frigga-go
A selective Golang port of Netflix's Frigga project.
http://github.com/SmartThingsOSS/frigga-go
Copyright 2015 SmartThings, 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.
# Afero
A FileSystem Abstraction System for Go
http://github.com/spf13/afero
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
# cast
Easy and safe casting from one type to another in Go
http://github.com/spf13/cast
The MIT License (MIT)
Copyright (c) 2014 Steve Francia
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# jWalterWeatherman
Seamless printing to the terminal (stdout) and logging to a io.Writer (file)
that’s as easy to use as fmt.Println.
http://github.com/spf13/jwalterweatherman
The MIT License (MIT)
Copyright (c) 2014 Steve Francia
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# pflag
pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags.
http://github.com/spf13/pflag
Copyright (c) 2012 Alex Ogier. All rights reserved.
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# viper
Go configuration with fangs
http://github.com/spf13/viper
The MIT License (MIT)
Copyright (c) 2014 Steve Francia
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# crypto
Go supplementary cryptography libraries
http://golang.org/x/crypto
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# sys
Go packages for low-level interaction with the operating system
http://golang.org/x/sys
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# text
Go text processing support
http://golang.org/x/text
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# yaml
YAML support for the Go language
https://github.com/go-yaml/yaml
Copyright 2011-2016 Canonical Ltd.
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.
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original copyright and license:
apic.go
emitterc.go
parserc.go
readerc.go
scannerc.go
writerc.go
yamlh.go
yamlprivateh.go
Copyright (c) 2006 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# sql-migrate
SQL Schema migration tool for Go
https://github.com/rubenv/sql-migrate
(The MIT License)
Copyright (C) 2014-2016 by Ruben Vermeersch <ruben@rocketeer.be>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# gorp
Go Relational Persistence
https://github.com/go-gorp/gorp
(The MIT License)
Copyright (c) 2012 James Cooper <james@bitmechanic.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: OSSMETADATA
================================================
osslifecycle=active
================================================
FILE: README.md
================================================

[](OSSMETADATA) [![Build Status][travis-badge]][travis] [![GoDoc][godoc-badge]][godoc] [![GoReportCard][report-badge]][report]
[travis-badge]: https://travis-ci.com/Netflix/chaosmonkey.svg?branch=master
[travis]: https://travis-ci.com/Netflix/chaosmonkey
[godoc-badge]: https://godoc.org/github.com/Netflix/chaosmonkey?status.svg
[godoc]: https://godoc.org/github.com/Netflix/chaosmonkey
[report-badge]: https://goreportcard.com/badge/github.com/Netflix/chaosmonkey
[report]: https://goreportcard.com/report/github.com/Netflix/chaosmonkey
Chaos Monkey randomly terminates virtual machine instances and containers that
run inside of your production environment. Exposing engineers to
failures more frequently incentivizes them to build resilient services.
See the [documentation][docs] for info on how to use Chaos Monkey.
Chaos Monkey is an example of a tool that follows the
[Principles of Chaos Engineering][PoC].
[PoC]: http://principlesofchaos.org/
### Requirements
This version of Chaos Monkey is fully integrated with [Spinnaker], the
continuous delivery platform that we use at Netflix. You must be managing your
apps with Spinnaker to use Chaos Monkey to terminate instances.
Chaos Monkey should work with any backend that Spinnaker supports (AWS, Google
Compute Engine, Azure, Kubernetes, Cloud Foundry). It has been tested with
AWS, [GCE][gce-blogpost], and Kubernetes.
### Install locally
To install the Chaos Monkey binary on your local machine:
```
go get github.com/netflix/chaosmonkey/cmd/chaosmonkey
```
### How to deploy
See the [docs] for instructions on how to configure and deploy Chaos Monkey.
### Support
[Simian Army Google group](http://groups.google.com/group/simianarmy-users).
[Spinnaker]: http://www.spinnaker.io/
[docs]: https://netflix.github.io/chaosmonkey
[gce-blogpost]: https://medium.com/continuous-delivery-scale/running-chaos-monkey-on-spinnaker-google-compute-engine-gce-155dc52f20ef
================================================
FILE: cal/cal.go
================================================
// 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 cal has calendar-related functions
package cal
import "fmt"
import "time"
// IsWorkday returns true if the date associated with t is a work day
// Uses the location associated with t to make this calculation
func IsWorkday(t time.Time) bool {
return isWeekday(t)
}
func isWeekday(t time.Time) bool {
switch t.Weekday() {
case time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday:
return true
case time.Saturday, time.Sunday:
return false
}
panic(fmt.Sprintf("Unknown weekday: %s", t.Weekday()))
}
================================================
FILE: cal/cal_test.go
================================================
// 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 cal_test
import (
"testing"
"time"
"github.com/Netflix/chaosmonkey/v2/cal"
)
var weekdayTests = []struct {
date string
pred bool
}{
{"Mon Dec 14 11:33:58 PST 2015", true},
{"Tue Dec 15 11:33:58 PST 2015", true},
{"Wed Dec 16 11:33:58 PST 2015", true},
{"Thu Dec 17 11:33:58 PST 2015", true},
{"Fri Dec 18 11:33:58 PST 2015", true},
{"Sat Dec 19 11:33:58 PST 2015", false},
{"Sun Dec 20 11:33:58 PST 2015", false},
}
func TestIsWorkday(t *testing.T) {
for _, tt := range weekdayTests {
if got, want := cal.IsWorkday(parse(tt.date)), tt.pred; got != want {
t.Fatalf("isWeekday(\"%s\")=%t, want %t", tt.date, got, want)
}
}
}
// parse returns a time formatted as the standard output of "date", e.g.:
// Thu Dec 17 15:18:30 PST 2015
func parse(s string) time.Time {
t, err := time.Parse("Mon Jan 2 15:04:05 PST 2006", s)
if err != nil {
panic(err)
}
return t
}
================================================
FILE: chaosmonkey.go
================================================
// 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 chaosmonkey contains our domain models
package chaosmonkey
import (
"fmt"
"time"
)
const (
// App grouping: Chaos Monkey kills one instance per app per day
App Group = iota
// Stack grouping: Chaos Monkey kills one instance per stack per day
Stack
// Cluster grouping: Chaos Monkey kills one instance per cluster per day
Cluster
)
type (
// AppConfig contains app-specific configuration parameters for Chaos Monkey
AppConfig struct {
Enabled bool
RegionsAreIndependent bool
MeanTimeBetweenKillsInWorkDays int
MinTimeBetweenKillsInWorkDays int
Grouping Group
Exceptions []Exception
Whitelist *[]Exception
}
// Group describes what Chaos Monkey considers a group of instances
// Chaos Monkey will randomly kill an instance from each group.
// The group generally maps onto what the service owner considers
// a "cluster", which is different from Spinnaker's notion of a cluster.
Group int
// Exception describes clusters that have been opted out of chaos monkey
// If one of the members is a "*", it matches everything. That is the only
// wildcard value
// For example, this will opt-out all of the cluters in the test account:
// Exception{ Account:"test", Stack:"*", Cluster:"*", Region: "*"}
Exception struct {
Account string
Stack string
Detail string
Region string
}
// Instance contains naming info about an instance
Instance interface {
// AppName is the name of the Netflix app
AppName() string
// AccountName is the name of the account the instance is running in (e.g., prod, test)
AccountName() string
// RegionName is the name of the AWS region (e.g., us-east-1
RegionName() string
// StackName returns the "stack" part of app-stack-detail in cluster names
StackName() string
// ClusterName is the full cluster name: app-stack-detail
ClusterName() string
// ASGName is the name of the ASG associated with the instance
ASGName() string
// ID is the instance ID, e.g. i-dbcba24c
ID() string
// CloudProvider returns the cloud provider (e.g., "aws")
CloudProvider() string
}
// Termination contains information about an instance termination.
Termination struct {
Instance Instance // The instance that will be terminated
Time time.Time // Termination time
Leashed bool // If true, track the termination but do not execute it
}
// Tracker records termination events an a tracking system such as Chronos
Tracker interface {
// Track pushes a termination event to the tracking system
Track(t Termination) error
}
// ErrorCounter counts when errors occur.
ErrorCounter interface {
Increment() error
}
// Decryptor decrypts encrypted text. It is used for decrypting
// sensitive credentials that are stored encrypted
Decryptor interface {
Decrypt(ciphertext string) (string, error)
}
// Env provides information about the environment that Chaos Monkey has been
// deployed to.
Env interface {
// InTest returns true if Chaos Monkey is running in a test environment
InTest() bool
}
// AppConfigGetter retrieves App configuration info
AppConfigGetter interface {
// Get returns the App config info by app name
Get(app string) (*AppConfig, error)
}
// Checker checks to see if a termination is permitted given min time between terminations
//
// if the termination is permitted, returns (true, nil)
// otherwise, returns false with an error
//
// Returns ErrViolatesMinTime if violates min time between terminations
//
// Note that this call may change the state of the server: if the checker returns true, the termination will be recorded.
Checker interface {
// Check checks if a termination is permitted and, if so, records the
// termination time on the server.
// The endHour (hour time when Chaos Monkey stops killing) is in the
// time zone specified by loc.
Check(term Termination, appCfg AppConfig, endHour int, loc *time.Location) error
}
// Terminator provides an interface for killing instances
Terminator interface {
// Kill terminates a running instance
Execute(trm Termination) error
}
// Outage provides an interface for checking if there is currently an outage
// This provides a mechanism to check if there's an ongoing outage, since
// Chaos Monkey doesn't run during outages
Outage interface {
// Outage returns true if there is an ongoing outage
Outage() (bool, error)
}
// ErrViolatesMinTime represents an error when trying to record a termination
// that violates the min time between terminations for that particular app
ErrViolatesMinTime struct {
InstanceID string // the most recent terminated instance id
KilledAt time.Time // the time that the most recent instance was terminated
Loc *time.Location // local time zone location
}
)
// String returns a string representation for a Group
func (g Group) String() string {
switch g {
case App:
return "app"
case Stack:
return "stack"
case Cluster:
return "cluster"
}
panic("Unknown Group value")
}
// NewAppConfig constructs a new app configuration with reasonable defaults
// with specified accounts enabled/disabled
func NewAppConfig(exceptions []Exception) AppConfig {
result := AppConfig{
Enabled: true,
RegionsAreIndependent: true,
MeanTimeBetweenKillsInWorkDays: 5,
Grouping: Cluster,
Exceptions: exceptions,
}
return result
}
// Matches returns true if an exception matches an ASG
func (ex Exception) Matches(account, stack, detail, region string) bool {
return exFieldMatches(ex.Account, account) &&
exFieldMatches(ex.Stack, stack) &&
exFieldMatches(ex.Detail, detail) &&
exFieldMatches(ex.Region, region)
}
// exFieldMatches checks if an exception field matches a given value
// It's true if field is "*" or if the field is the same string as the value
func exFieldMatches(field, value string) bool {
return field == "*" || field == value
}
func (e ErrViolatesMinTime) Error() string {
s := fmt.Sprintf("Would violate min between kills: instance %s was killed at %s", e.InstanceID, e.KilledAt)
// If we know the time zone, report that as well
if e.Loc != nil {
s += fmt.Sprintf(" (%s)", e.KilledAt.In(e.Loc))
}
return s
}
================================================
FILE: chaosmonkey_test.go
================================================
// 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 chaosmonkey_test
import (
"testing"
"github.com/Netflix/chaosmonkey/v2"
)
func TestExceptionMatches(t *testing.T) {
ex := chaosmonkey.Exception{Account: "test", Stack: "*", Detail: "*", Region: "*"}
if !ex.Matches("test", "cl", "app-cl-test", "us-east-1") {
t.Error("Expected exception match")
}
}
================================================
FILE: clock/clock.go
================================================
// 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 clock provides the Clock interface for getting the current time
package clock
import "time"
// Clock provides an interface to the current time, useful for testing
type Clock interface {
// Now returns the current time
Now() time.Time
}
// New returns an implementation of Clock that uses the system time
func New() Clock {
return SystemClock{}
}
// SystemClock uses the system clock to return the time
type SystemClock struct{}
// Now implements Clock.Now
func (cl SystemClock) Now() time.Time {
return time.Now()
}
================================================
FILE: cmd/chaosmonkey/main.go
================================================
// 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.
/*
Chaos Monkey randomly terminates instances.
*/
package main
import (
"github.com/Netflix/chaosmonkey/v2/command"
// These are anonymous imported so that the related Get* methods (e.g.,
// GetDecryptor) are picked up.
_ "github.com/Netflix/chaosmonkey/v2/constrainer"
_ "github.com/Netflix/chaosmonkey/v2/decryptor"
_ "github.com/Netflix/chaosmonkey/v2/env"
_ "github.com/Netflix/chaosmonkey/v2/errorcounter"
_ "github.com/Netflix/chaosmonkey/v2/outage"
_ "github.com/Netflix/chaosmonkey/v2/tracker"
)
func main() {
command.Execute()
}
================================================
FILE: command/chaosmonkey.go
================================================
// 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 command
import (
"fmt"
"log"
"math"
"os"
"runtime/debug"
"strings"
"time"
flag "github.com/spf13/pflag"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/clock"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/config/param"
"github.com/Netflix/chaosmonkey/v2/deploy"
"github.com/Netflix/chaosmonkey/v2/deps"
"github.com/Netflix/chaosmonkey/v2/mysql"
"github.com/Netflix/chaosmonkey/v2/schedstore"
"github.com/Netflix/chaosmonkey/v2/schedule"
"github.com/Netflix/chaosmonkey/v2/spinnaker"
)
// Version is the version number
const Version = "2.0.2"
func printVersion() {
fmt.Printf("%s\n", Version)
}
var (
// configPaths is where Chaos Monkey will look for a chaosmonkey.toml
// configuration file
configPaths = [...]string{".", "/apps/chaosmonkey", "/etc", "/etc/chaosmonkey"}
)
// Usage prints usage
func Usage() {
usage := `
Chaos Monkey
Usage:
chaosmonkey <command> ...
command: migrate | schedule | terminate | fetch-schedule | outage | config | email | eligible | intest
Install
-------
Installs chaosmonkey with all the setup required, e.g setting up the cron, appling database migration etc.
migrate
-------
Applies database migration to the database defined in the configuration file.
schedule [--max-apps=<N>] [--apps=foo,bar,baz] [--no-record-schedule]
--------------------------------------------------------------------
Generates a schedule of terminations for the day and installs the
terminations as local cron jobs that call "chaosmonkey terminate ..."
--apps=foo,bar,baz Optionally specify an explicit list of apps to schedule.
This is primarily used for debugging.
--max-apps=<N> Optionally specify the maximum number of apps that Chaos Monkey
will schedule. This is primarily used for debugging.
--no-record-schedule Do not record the schedule with the database.
This is primarily used for debugging.
terminate <app> <account> [--region=<region>] [--stack=<stack>] [--cluster=<cluster>] [--leashed]
-----------------------------------------------------------------------------------------------------------------
Terminates an instance from a given app and account.
Optionally specify a region, stack, cluster.
The --leashed flag forces chaosmonkey to run in leashed mode. When leashed,
Chaos Monkey will check if an instance should be terminated, but will not
actually terminate it.
fetch-schedule
--------------
Queries the database to see if there is an existing schedule of
terminations for today. If so, downloads the schedule and sets up cron jobs to
implement the schedule.
outage
------
Output "true" if there is an ongoing outage, otherwise "false". Used for debugging.
config [<app>]
------------
Query Spinnaker for the config for a specific app and dump it to
standard out. This is only used for debugging.
If no app is specified, dump the Monkey-level configuration options to standard out.
Examples:
chaosmonkey config chaosguineapig
chaosmonkey config
eligible <app> <account> [--region=<region>] [--stack=<stack>] [--cluster=<cluster>]
-------------------------------------------------------------------------------------
Dump a list of instance-ids that are eligible for termination for a given app, account,
and optionally region, stack, and cluster.
intest
------
Outputs "true" on standard out if running within a test environment, otherwise outputs "false"
account <name>
--------------
Look up an cloud account ID by name.
Example:
chaosmonkey account test
provider <name>
---------------
Look up the cloud provider by account name.
Example:
chaosmonkey provider test
clusters <app> <account>
------------------------
List the clusters for a given app and account
Example:
chaosmonkey clusters chaosguineapig test
regions <cluster> <account>
---------------------------
List the regions for a given cluster and account
Example:
chaosmonkey regions chaosguineapig test
`
fmt.Printf(usage)
}
func init() {
// Prepend the pid to log statements
log.SetPrefix(fmt.Sprintf("[%5d] ", os.Getpid()))
}
// Execute is the main entry point for the chaosmonkey cli.
func Execute() {
regionPtr := flag.String("region", "", "region of termination group")
stackPtr := flag.String("stack", "", "stack of termination group")
clusterPtr := flag.String("cluster", "", "cluster of termination group")
appsPtr := flag.String("apps", "", "comma-separated list of apps to schedule for termination")
noRecordSchedulePtr := flag.Bool("no-record-schedule", false, "do not record schedule")
versionPtr := flag.BoolP("version", "v", false, "show version")
flag.Usage = Usage
// These flags, if specified, override config values
maxAppsFlag := "max-apps"
leashedFlag := "leashed"
flag.Int(maxAppsFlag, math.MaxInt32, "max number of apps to examine for termination")
flag.Bool(leashedFlag, false, "force leashed mode")
flag.Parse()
if len(flag.Args()) == 0 {
if *versionPtr {
printVersion()
os.Exit(0)
}
flag.Usage()
os.Exit(1)
}
cmd := flag.Arg(0)
cfg, err := getConfig()
if err != nil {
log.Fatalf("FATAL: failed to load config: %v", err)
}
// Associate config values with flags
err = cfg.BindPFlag(param.MaxApps, flag.Lookup(maxAppsFlag))
if err != nil {
log.Fatalf("FATAL: failed to bind flag: --%s: %v", maxAppsFlag, err)
}
err = cfg.BindPFlag(param.Leashed, flag.Lookup(leashedFlag))
if err != nil {
log.Fatalf("FATAL: failed to bind flag: --%s: %v", leashedFlag, err)
}
spin, err := spinnaker.NewFromConfig(cfg)
if err != nil {
log.Fatalf("FATAL: spinnaker.New failed: %+v", err)
}
outage, err := deps.GetOutage(cfg)
if err != nil {
log.Fatalf("FATAL: deps.GetOutage fail: %+v", err)
}
sql, err := mysql.NewFromConfig(cfg)
if err != nil {
log.Fatalf("FATAL: could not initialize mysql connection: %+v", err)
}
cons, err := deps.GetConstrainer(cfg)
if err != nil {
log.Fatalf("FATAL: deps.GetConstrainer failed: %+v", err)
}
// Ensure mysql object gets closed
defer func() {
_ = sql.Close()
}()
switch cmd {
case "install":
executable := ChaosmonkeyExecutable{}
Install(cfg, executable, sql)
case "migrate":
Migrate(sql)
case "schedule":
log.Println("chaosmonkey schedule starting")
defer log.Println("chaosmonkey schedule done")
var apps []string
if *appsPtr != "" {
// User explicitly specified list of apps on the command line
apps = strings.Split(*appsPtr, ",")
} else {
// User did not explicitly specify list of apps, get 'em all
var err error
apps, err = spin.AppNames()
if err != nil {
log.Fatalf("FATAL: could not retrieve list of app names: %v", err)
}
}
var schedStore schedstore.SchedStore
schedStore = sql
if *noRecordSchedulePtr {
schedStore = nullSchedStore{}
}
Schedule(spin, schedStore, cfg, spin, cons, apps)
case "fetch-schedule":
FetchSchedule(sql, cfg)
case "terminate":
if len(flag.Args()) != 3 {
flag.Usage()
os.Exit(1)
}
app := flag.Arg(1)
account := flag.Arg(2)
trackers, err := deps.GetTrackers(cfg)
if err != nil {
log.Fatalf("FATAL: could not create trackers: %+v", err)
}
errCounter, err := deps.GetErrorCounter(cfg)
if err != nil {
log.Fatalf("FATAL: could not create error counter: %+v", err)
}
env, err := deps.GetEnv(cfg)
if err != nil {
log.Fatalf("FATAL: could not determine environment: %+v", err)
}
defer logOnPanic(errCounter) // Handler in case of panic
deps := deps.Deps{
MonkeyCfg: cfg,
Checker: sql,
ConfGetter: spin,
Cl: clock.New(),
Dep: spin,
T: spin,
Trackers: trackers,
Ou: outage,
ErrCounter: errCounter,
Env: env,
}
Terminate(deps, app, account, *regionPtr, *stackPtr, *clusterPtr)
case "outage":
Outage(outage)
case "config":
if len(flag.Args()) != 2 {
DumpMonkeyConfig(cfg)
return
}
app := flag.Arg(1)
DumpConfig(spin, app)
case "eligible":
if len(flag.Args()) != 3 {
flag.Usage()
os.Exit(1)
}
app := flag.Arg(1)
account := flag.Arg(2)
Eligible(spin, spin, app, account, *regionPtr, *stackPtr, *clusterPtr)
case "intest":
env, err := deps.GetEnv(cfg)
if err != nil {
log.Fatalf("FATAL: could not determine environment: %+v", err)
}
fmt.Println(env.InTest())
case "account":
if len(flag.Args()) != 2 {
flag.Usage()
os.Exit(1)
}
account := flag.Arg(1)
id, err := spin.AccountID(account)
if err != nil {
fmt.Printf("ERROR: Could not retrieve id for account: %s. Reason: %v\n", account, err)
return
}
fmt.Println(id)
case "provider":
if len(flag.Args()) != 2 {
flag.Usage()
os.Exit(1)
}
account := flag.Arg(1)
provider, err := spin.CloudProvider(account)
if err != nil {
fmt.Printf("ERROR: Could not retrieve provider for account: %s. Reason: %v\n", account, err)
return
}
fmt.Println(provider)
case "clusters":
if len(flag.Args()) != 3 {
flag.Usage()
os.Exit(1)
}
app := flag.Arg(1)
account := flag.Arg(2)
clusters, err := spin.GetClusterNames(app, deploy.AccountName(account))
if err != nil {
fmt.Printf("ERROR: %v\n", err)
os.Exit(1)
}
for _, cluster := range clusters {
fmt.Println(cluster)
}
case "regions":
if len(flag.Args()) != 3 {
flag.Usage()
os.Exit(1)
}
cluster := flag.Arg(1)
account := flag.Arg(2)
DumpRegions(cluster, account, spin)
default:
flag.Usage()
os.Exit(1)
}
}
func init() {
// All logs to stdout
log.SetOutput(os.Stdout)
}
// logOnPanic increments an error metric and logs if a panic happens
func logOnPanic(errCounter chaosmonkey.ErrorCounter) {
if e := recover(); e != nil {
log.Printf("FATAL: panic: %s: %s", e, debug.Stack())
err := errCounter.Increment()
if err != nil {
log.Printf("failed to increment error counter: %s", err)
}
}
}
// return configuration info
func getConfig() (*config.Monkey, error) {
cfg, err := config.Load(configPaths[:])
if err != nil {
return nil, err
}
return cfg, nil
}
// nullSchedStore is a no-op implementation of api.SchedStore
type nullSchedStore struct{}
// Retrieve implements api.SchedStore.Retrieve
func (n nullSchedStore) Retrieve(date time.Time) (*schedule.Schedule, error) {
return nil, fmt.Errorf("nullSchedStore does not support Retrieve function")
}
// Publish implements api.SchedStore.Publish
func (n nullSchedStore) Publish(date time.Time, sched *schedule.Schedule) error {
return nil
}
================================================
FILE: command/command.go
================================================
// 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 command contains functions that can be invoked via command-line
// e.g. "chaosmonkey schedule" invokes command.Schedule
package command
================================================
FILE: command/dumpconfig.go
================================================
// 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 command
import (
"fmt"
"os"
"github.com/Netflix/chaosmonkey/v2"
"github.com/davecgh/go-spew/spew"
)
// DumpConfig dumps the config for an app to stdout
func DumpConfig(c chaosmonkey.AppConfigGetter, app string) {
cfg, err := c.Get(app)
if err != nil {
fmt.Printf("%+v", err)
os.Exit(1)
}
spew.Dump(cfg)
}
================================================
FILE: command/dumpmonkeyconfig.go
================================================
// 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 command
import (
"fmt"
"github.com/Netflix/chaosmonkey/v2/config"
)
// DumpMonkeyConfig dumps the monkey-level config parameters to stdout
func DumpMonkeyConfig(cfg *config.Monkey) {
var enabled, leashed, sched bool
var accounts []string
var err error
if enabled, err = cfg.Enabled(); err != nil {
fmt.Printf("ERROR getting enabled: %v", err)
} else {
fmt.Printf("enabled: %t\n", enabled)
}
if leashed, err = cfg.Leashed(); err != nil {
fmt.Printf("ERROR getting leashed: %v", err)
} else {
fmt.Printf("leashed: %t\n", leashed)
}
if sched, err = cfg.ScheduleEnabled(); err != nil {
fmt.Printf("ERROR getting schedule enabled: %v", err)
} else {
fmt.Printf("schedule enabled: %t\n", sched)
}
if accounts, err = cfg.Accounts(); err != nil {
fmt.Printf("ERROR getting accounts: %v\n", err)
} else {
fmt.Printf("accounts: %v\n", accounts)
}
fmt.Printf("start hour: %d\n", cfg.StartHour())
fmt.Printf("end hour: %d\n", cfg.EndHour())
loc, _ := cfg.Location()
fmt.Printf("location: %s\n", loc)
fmt.Printf("cron path: %s\n", cfg.CronPath())
fmt.Printf("term path: %s\n", cfg.TermPath())
fmt.Printf("term account: %s\n", cfg.TermAccount())
fmt.Printf("max apps: %d\n", cfg.MaxApps())
}
================================================
FILE: command/eligible.go
================================================
// 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 command
import (
"fmt"
"os"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/deploy"
"github.com/Netflix/chaosmonkey/v2/eligible"
"github.com/Netflix/chaosmonkey/v2/grp"
)
// Eligible prints out a list of instance ids eligible for termination
// It is intended only for testing
func Eligible(g chaosmonkey.AppConfigGetter, d deploy.Deployment, app, account, region, stack, cluster string) {
cfg, err := g.Get(app)
if err != nil {
fmt.Printf("Failed to retrieve config for app %s\n%+v", app, err)
os.Exit(1)
}
group := grp.New(app, account, region, stack, cluster)
instances, err := eligible.Instances(group, cfg.Exceptions, d)
if err != nil {
fmt.Print(err)
os.Exit(1)
}
for _, instance := range instances {
fmt.Println(instance.ID())
}
}
================================================
FILE: command/fetchschedule.go
================================================
// 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 command
import (
"log"
"time"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/schedstore"
)
// FetchSchedule executes the "fetch-schedule" command. This checks if there
// is an existing schedule for today that was previously registered
// in chaosmonkey-api. If so, it downloads the schedule from chaosmonkey-api
// and installs it locally.
func FetchSchedule(s schedstore.SchedStore, cfg *config.Monkey) {
log.Println("chaosmonkey fetch-schedule starting")
sched, err := s.Retrieve(today(cfg))
if err != nil {
log.Fatalf("FATAL: could not fetch schedule: %v", err)
}
if sched == nil {
log.Println("no schedule to retrieve")
return
}
err = registerWithCron(sched, cfg)
if err != nil {
log.Fatalf("FATAL: could not register with cron: %v", err)
}
defer log.Println("chaosmonkey fetch-schedule done")
}
// today returns a date in local time
func today(cfg *config.Monkey) time.Time {
loc, err := cfg.Location()
if err != nil {
log.Fatalf("FATAL: Could not get local timezone: %v", err)
}
return time.Now().In(loc)
}
================================================
FILE: command/install.go
================================================
// 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 command
import (
"fmt"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/mysql"
"io/ioutil"
"log"
"os"
)
const (
scheduleCommand = "schedule"
terminateCommand = "terminate"
scriptContent = `#!/bin/bash
%s %s "$@" >> %s/chaosmonkey-%s.log 2>&1
`
)
// CurrentExecutable provides an interface to extract information about the current executable
type CurrentExecutable interface {
// ExecutablePath returns the path to current executable
ExecutablePath() (string, error)
}
// Install installs chaosmonkey and runs database migration
func Install(cfg *config.Monkey, exec CurrentExecutable, db mysql.MySQL) {
InstallCron(cfg, exec)
Migrate(db)
log.Println("installation done!")
}
// InstallCron installs chaosmonkey schedule generation cron
func InstallCron(cfg *config.Monkey, exec CurrentExecutable) {
executablePath, err := exec.ExecutablePath()
if err != nil {
log.Fatalf("FATAL: %v", err)
}
err = setupTerminationScript(cfg, executablePath)
if err != nil {
log.Fatalf("FATAL: %v", err)
}
err = setupCron(cfg, executablePath)
if err != nil {
log.Fatalf("FATAL: %v", err)
}
log.Println("chaosmonkey cron is installed successfully")
}
func setupCron(cfg *config.Monkey, executablePath string) error {
err := EnsureFileAbsent(cfg.SchedulePath())
if err != nil {
return err
}
err = EnsureFileAbsent(cfg.ScheduleCronPath())
if err != nil {
return err
}
var scriptPerms os.FileMode = 0755 // -rwx-rx--rx-- : scripts should be executable
log.Printf("Creating %s\n", cfg.SchedulePath())
content, err := generateScriptContent(scheduleCommand, cfg, executablePath)
if err != nil {
return err
}
err = ioutil.WriteFile(cfg.SchedulePath(), content, scriptPerms)
if err != nil {
return err
}
cronExpr, err := cfg.CronExpression()
if err != nil {
return err
}
crontab := fmt.Sprintf("%s %s %s\n", cronExpr, cfg.TermAccount(), cfg.SchedulePath())
var cronPerms os.FileMode = 0644 // -rw-r--r-- : cron config file shouldn't have write perm
log.Printf("Creating %s\n", cfg.ScheduleCronPath())
err = ioutil.WriteFile(cfg.ScheduleCronPath(), []byte(crontab), cronPerms)
return err
}
func setupTerminationScript(cfg *config.Monkey, executablePath string) error {
err := EnsureFileAbsent(cfg.TermPath())
if err != nil {
return err
}
var perms os.FileMode = 0755 // -rwx-rx--rx-- : scripts should be executable
log.Printf("Creating %s\n", cfg.TermPath())
content, err := generateScriptContent(terminateCommand, cfg, executablePath)
if err != nil {
return err
}
err = ioutil.WriteFile(cfg.TermPath(), content, perms)
return err
}
func generateScriptContent(cmdName string, cfg *config.Monkey, executablePath string) ([]byte, error) {
content := fmt.Sprintf(scriptContent, executablePath, cmdName, cfg.LogPath(), cmdName)
return []byte(content), nil
}
================================================
FILE: command/install_test.go
================================================
// 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 command
import (
"fmt"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/config/param"
"github.com/Netflix/chaosmonkey/v2/mock"
"github.com/pkg/errors"
"io/ioutil"
"testing"
)
func assertHasSameContent(fileName string, expectedContent string) error {
cronContent, err := ioutil.ReadFile(fileName)
if err != nil {
return err
}
actualContent := string(cronContent)
if actualContent != expectedContent {
return errors.Errorf("\nFile : %s\nExpected:\n%s\nActual:\n%s", fileName, expectedContent, actualContent)
}
return nil
}
func initInstallationConfig(script string, cron string, log string, term string) (*config.Monkey, error) {
defaultConfig := config.Defaults()
defaultConfig.Set(param.SchedulePath, script)
defaultConfig.Set(param.ScheduleCronPath, cron)
defaultConfig.Set(param.LogPath, log)
defaultConfig.Set(param.StartHour, 9)
defaultConfig.Set(param.TermAccount, "root")
defaultConfig.Set(param.TermPath, term)
return defaultConfig, nil
}
func TestInstallationWithDefaultCron(t *testing.T) {
scriptPath := "/tmp/chaosmonkey-schedule.sh"
termPath := "/tmp/chaosmonkey-terminate.sh"
cronPath := "/tmp/chaosmonkey-schedule"
execPath := "/tmp/chaosmonkey"
logPath := "/var/log"
defaultConfig, err := initInstallationConfig(scriptPath, cronPath, logPath, termPath)
if err != nil {
t.Error(err.Error())
return
}
executable := mock.Executable{Path: execPath}
InstallCron(defaultConfig, executable)
expectedCron := fmt.Sprintf("0 7 * * 1-5 root %s\n", scriptPath)
err = assertHasSameContent(cronPath, expectedCron)
if err != nil {
t.Error(err.Error())
return
}
expectedScript := fmt.Sprintf(`#!/bin/bash
%s %s "$@" >> %s/chaosmonkey-%s.log 2>&1
`, execPath, "schedule", logPath, "schedule")
err = assertHasSameContent(scriptPath, expectedScript)
if err != nil {
t.Error(err.Error())
return
}
}
func TestInstallationWithUserDefinedCron(t *testing.T) {
scriptPath := "/tmp/chaosmonkey-schedule.sh"
termPath := "/tmp/chaosmonkey-terminate.sh"
cronPath := "/tmp/chaosmonkey-schedule"
execPath := "/tmp/chaosmonkey"
logPath := "/var/log"
userDefinedCron := "0 15 * * 1-5"
defaultConfig, err := initInstallationConfig(scriptPath, cronPath, logPath, termPath)
defaultConfig.Set(param.CronExpression, userDefinedCron)
if err != nil {
t.Error(err.Error())
return
}
executable := mock.Executable{Path: execPath}
InstallCron(defaultConfig, executable)
expectedCron := fmt.Sprintf("%s root %s\n", userDefinedCron, scriptPath)
err = assertHasSameContent(cronPath, expectedCron)
if err != nil {
t.Error(err.Error())
return
}
expectedScript := fmt.Sprintf(`#!/bin/bash
%s %s "$@" >> %s/chaosmonkey-%s.log 2>&1
`, execPath, "schedule", logPath, "schedule")
err = assertHasSameContent(scriptPath, expectedScript)
if err != nil {
t.Error(err.Error())
return
}
}
================================================
FILE: command/migrate.go
================================================
// 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 command
import (
"github.com/Netflix/chaosmonkey/v2/mysql"
"log"
)
// Migrate executes database migration
func Migrate(db mysql.MySQL) {
err := mysql.Migrate(db)
if err != nil {
log.Fatalf("ERROR - couldn't apply database migration: %v", err)
}
log.Println("database migration applied successfully")
}
================================================
FILE: command/osutil.go
================================================
// 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 command
import (
"github.com/kardianos/osext"
"os"
)
// ChaosmonkeyExecutable is a representation of Chaosmonkey executable
type ChaosmonkeyExecutable struct {
}
// ExecutablePath implements command.CurrentExecutable.ExecutablePath
func (e ChaosmonkeyExecutable) ExecutablePath() (string, error) {
return osext.Executable()
}
// EnsureFileAbsent ensures that a file is absent, returning an error otherwise
func EnsureFileAbsent(path string) error {
err := os.Remove(path)
// If it's an IsNotExist error, we can ignore it, since it
// satisfies the contract of the file being absent
if os.IsNotExist(err) {
return nil
}
return err
}
================================================
FILE: command/outage.go
================================================
// 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 command
import (
"fmt"
"os"
"github.com/Netflix/chaosmonkey/v2"
)
// Outage prints out "true" if an ongoing outage, else "false"
func Outage(ou chaosmonkey.Outage) {
down, err := ou.Outage()
if err != nil {
fmt.Printf("ERROR: %v", err)
os.Exit(1)
}
fmt.Printf("%t\n", down)
}
================================================
FILE: command/regions.go
================================================
// Copyright 2017 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 command
import (
"fmt"
"github.com/Netflix/chaosmonkey/v2/deploy"
"github.com/Netflix/chaosmonkey/v2/spinnaker"
"github.com/SmartThingsOSS/frigga-go"
"os"
)
// DumpRegions lists the regions that a cluster is in
func DumpRegions(cluster, account string, spin spinnaker.Spinnaker) {
names, err := frigga.Parse(cluster)
if err != nil {
fmt.Printf("ERROR: %s", err)
os.Exit(1)
}
regions, err := spin.GetRegionNames(names.App, deploy.AccountName(account), deploy.ClusterName(cluster))
if err != nil {
fmt.Printf("ERROR: %v", err)
os.Exit(1)
}
for _, region := range regions {
fmt.Println(region)
}
}
================================================
FILE: command/schedule.go
================================================
// 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 command
import (
"fmt"
"io/ioutil"
"log"
"os"
"time"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/deploy"
"github.com/Netflix/chaosmonkey/v2/schedstore"
"github.com/Netflix/chaosmonkey/v2/schedule"
)
// Schedule executes the "schedule" command. This defines the schedule
// of terminations for the day and records them as cron jobs
func Schedule(g chaosmonkey.AppConfigGetter, ss schedstore.SchedStore, cfg *config.Monkey, d deploy.Deployment, cons schedule.Constrainer, apps []string) {
enabled, err := cfg.ScheduleEnabled()
if err != nil {
log.Fatalf("FATAL: cannot determine if schedule is enabled: %v", err)
}
if !enabled {
log.Println("schedule disabled, not running")
return
}
/*
Note: We don't check for the enable flag during scheduling, only
during terminations. That way, if chaos monkey is disabled during
scheduling time but later in the day becomes enabled, it still
functions correctly.
*/
err = do(d, g, ss, cfg, cons, apps)
if err != nil {
log.Fatalf("FATAL: %v", err)
}
}
// do is the actual implementation for the Schedule function
func do(d deploy.Deployment, g chaosmonkey.AppConfigGetter, ss schedstore.SchedStore, cfg *config.Monkey, cons schedule.Constrainer, apps []string) error {
s := schedule.New()
err := s.Populate(d, g, cfg, apps)
if err != nil {
return fmt.Errorf("failed to populate schedule: %v", err)
}
// Filter out terminations that violate constrains
sched := cons.Filter(*s)
err = deploySchedule(&sched, ss, cfg)
if err != nil {
return fmt.Errorf("failed to deploy schedule: %v", err)
}
return nil
}
// deploySchedule publishes the schedule to chaosmonkey-api
// and registers the schedule with the local cron
func deploySchedule(s *schedule.Schedule, ss schedstore.SchedStore, cfg *config.Monkey) error {
loc, err := cfg.Location()
if err != nil {
return fmt.Errorf("deploySchedule: could not retrieve local timezone: %v", err)
}
today := time.Now().In(loc)
err = ss.Publish(today, s)
if err != nil {
return fmt.Errorf("deploySchedule: could not publish schedule: %v", err)
}
err = registerWithCron(s, cfg)
return err
}
// registerWithCron registers the schedule of terminations with cron on the local machine
//
// Creates or overwrites the file specified by config.Chaos.CronPath()
func registerWithCron(s *schedule.Schedule, cfg *config.Monkey) error {
crontab := s.Crontab(cfg.TermPath(), cfg.TermAccount())
var perms os.FileMode = 0644 // -rw-r--r--
log.Printf("Writing %s\n", cfg.CronPath())
err := ioutil.WriteFile(cfg.CronPath(), crontab, perms)
return err
}
================================================
FILE: command/schedule_int_test.go
================================================
// 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 command
import (
"bytes"
"io/ioutil"
"testing"
"time"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/config/param"
"github.com/Netflix/chaosmonkey/v2/constrainer"
"github.com/Netflix/chaosmonkey/v2/mock"
"github.com/Netflix/chaosmonkey/v2/schedule"
)
// TestSchedule verifies the schedule command generates a cron file with
// the appropriate number of entries
func TestScheduleCommand(t *testing.T) {
// Setup
cronFile := "/tmp/chaoscron"
err := EnsureFileAbsent(cronFile)
if err != nil {
t.Fatal(err)
}
d := mock.Dep() // mock that returns four apps
a := new(mockAPI)
cfg := config.Defaults()
cfg.Set(param.Enabled, true)
cfg.Set(param.CronPath, cronFile)
cfg.Set(param.Accounts, []string{"prod", "test"})
// Code under test
appNames, err := d.AppNames()
if err != nil {
t.Fatalf("%v", err)
}
err = do(d, a, a, cfg, constrainer.NullConstrainer{}, appNames)
if err != nil {
t.Errorf("%v", err)
}
// Assertions
expectedCount := 4
cronFileContents, err := ioutil.ReadFile(cronFile)
if err != nil {
t.Fatal(err)
}
actualCount := countEntries(cronFileContents)
if actualCount != expectedCount {
t.Errorf("\nExpected:\n%d\nActual:\n%d", expectedCount, actualCount)
}
}
// countEntries counts the number of entries in a cron file's contents
func countEntries(buf []byte) int {
return bytes.Count(buf, []byte("\n"))
}
// mockAPI acts as a fake implementation of ChaosMonkeyAPI
type mockAPI struct {
}
// Publish implements ChaosMonkeyAPI.Publish
func (a mockAPI) Publish(date time.Time, sched *schedule.Schedule) error {
return nil
}
func (a mockAPI) Retrieve(date time.Time) (*schedule.Schedule, error) {
return nil, nil
}
// Get implements chaosmonkey.Getter.Get
func (a mockAPI) Get(name string) (*chaosmonkey.AppConfig, error) {
cfg := chaosmonkey.NewAppConfig(nil)
cfg.MeanTimeBetweenKillsInWorkDays = 1
return &cfg, nil
}
// Check implements api.Checker.Check
func (a mockAPI) Check(term chaosmonkey.Termination, appCfg *chaosmonkey.AppConfig, endHour int, loc *time.Location) (bool, error) {
return true, nil
}
================================================
FILE: command/schedule_test.go
================================================
// 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 command
import (
"io/ioutil"
"testing"
"time"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/config/param"
"github.com/Netflix/chaosmonkey/v2/grp"
"github.com/Netflix/chaosmonkey/v2/schedule"
)
// addToSchedule schedules instanceId for termination at timeString
// where timeString is formatted in RFC3339 format
func addToSchedule(t *testing.T, sched *schedule.Schedule, timeString string, group grp.InstanceGroup) {
tm, err := time.Parse(time.RFC3339, timeString)
if err != nil {
t.Fatal("Could not parse time string:", tm, err.Error())
}
sched.Add(tm, group)
}
func newClusterGroup(app, account, cluster, region string) grp.InstanceGroup {
return grp.New(app, account, region, "", cluster)
}
func TestRegisterWithCron(t *testing.T) {
// setup
// Ensure the file isn't there from a previous run
fname := "/tmp/chaoscron"
err := EnsureFileAbsent(fname)
if err != nil {
t.Error(err.Error())
return
}
config := config.Defaults()
config.Set(param.Enabled, true)
config.Set(param.CronPath, fname)
config.Set(param.Accounts, []string{"prod"})
sched := schedule.New()
// Thu Oct 1, 2015 10:15 AM PDT -> 17:15 UTC (7 hours)
addToSchedule(t, sched, "2015-10-01T10:15:00-07:00", newClusterGroup("abc", "prod", "abc-prod", "us-east-1"))
// Thu Oct 1, 2015 11:23 AM PDT -> 18:23 UTC (7 hours)
addToSchedule(t, sched, "2015-10-01T11:23:00-07:00", newClusterGroup("abc", "prod", "abc-prod", "us-west-2"))
// code under test
err = registerWithCron(sched, config)
if err != nil {
t.Fatal(err.Error())
}
// assertions
dat, err := ioutil.ReadFile(fname)
if err != nil {
t.Error(err.Error())
return
}
actual := string(dat)
expected := `15 17 1 10 4 root /apps/chaosmonkey/chaosmonkey-terminate.sh abc prod --cluster=abc-prod --region=us-east-1
23 18 1 10 4 root /apps/chaosmonkey/chaosmonkey-terminate.sh abc prod --cluster=abc-prod --region=us-west-2
`
if actual != expected {
t.Errorf("\nExpected:\n%s\nActual:\n%s", expected, actual)
}
}
// same as TestRegisterWithCron, but reverses the order that
// things are added to schedule.
func TestCronOutputInSortedOrder(t *testing.T) {
// setup
// Ensure the file isn't there from a previous run
fname := "/tmp/chaoscron"
err := EnsureFileAbsent(fname)
if err != nil {
t.Fatal(err.Error())
}
config := config.Defaults()
config.Set(param.Enabled, true)
config.Set(param.CronPath, fname)
config.Set(param.Accounts, []string{"prod"})
schedule := schedule.New()
// Thu Oct 1, 2015 11:23 AM PDT -> 18:23 UTC (7 hours)
addToSchedule(t, schedule, "2015-10-01T11:23:00-07:00", newClusterGroup("abc", "prod", "abc-prod", "us-east-1"))
// Thu Oct 1, 2015 10:15 AM PDT -> 17:15 UTC (7 hours)
addToSchedule(t, schedule, "2015-10-01T10:15:00-07:00", newClusterGroup("abc", "prod", "abc-prod", "us-west-2"))
// code under test
err = registerWithCron(schedule, config)
if err != nil {
t.Fatal(err.Error())
}
// assertions
dat, err := ioutil.ReadFile(fname)
if err != nil {
t.Error(err.Error())
return
}
actual := string(dat)
expected := `15 17 1 10 4 root /apps/chaosmonkey/chaosmonkey-terminate.sh abc prod --cluster=abc-prod --region=us-west-2
23 18 1 10 4 root /apps/chaosmonkey/chaosmonkey-terminate.sh abc prod --cluster=abc-prod --region=us-east-1
`
if actual != expected {
t.Errorf("\nExpected:\n%s\nActual:\n%s", expected, actual)
}
}
================================================
FILE: command/terminate.go
================================================
// 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 command
import (
"log"
"github.com/Netflix/chaosmonkey/v2/deps"
"github.com/Netflix/chaosmonkey/v2/term"
)
// Terminate executes the "terminate" command. This selects an instance
// based on the app, account, region, stack, cluster passed
//
// region, stack, and cluster may be blank
func Terminate(d deps.Deps, app string, account string, region string, stack string, cluster string) {
err := term.Terminate(d, app, account, region, stack, cluster)
if err != nil {
cerr := d.ErrCounter.Increment()
if cerr != nil {
log.Printf("WARNING could not increment error counter: %v", cerr)
}
log.Fatalf("FATAL %v\n\nstack trace:\n%+v", err, err)
}
}
================================================
FILE: config/config.go
================================================
// 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 config exposes configuration information
package config
================================================
FILE: config/config_test.go
================================================
// 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 config
import "testing"
func TestGetStringSlice(t *testing.T) {
cfg := Defaults()
cfg.Set("myparam1", `["foo", "bar"]`)
cfg.Set("myparam2", []string{"foo", "bar"})
cfg.Set("myparam3", []interface{}{interface{}("foo"), interface{}("bar")})
for _, param := range []string{"myparam1", "myparam2", "myparam3"} {
got, err := cfg.getStringSlice(param)
if err != nil {
t.Error(err)
}
if len(got) != 2 || got[0] != "foo" || got[1] != "bar" {
t.Errorf(`param %s, got %+v want ["foo", "bar"]`, param, got)
}
}
}
================================================
FILE: config/monkey.go
================================================
// 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 config
import (
"encoding/json"
"fmt"
"io"
"log"
"math"
"os"
"strings"
"time"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/Netflix/chaosmonkey/v2/config/param"
)
// Monkey is is a config implementation backed by viper
type Monkey struct {
remote bool // if true, there's a remote provider
v *viper.Viper
}
const (
clockStartHour int = 0
clockEndHour int = 23
hoursInClock int = 24
cronBeforeStartHour int = 2
)
func (m *Monkey) setDefaults() {
m.v.SetDefault(param.Enabled, false)
m.v.SetDefault(param.Leashed, true)
m.v.SetDefault(param.ScheduleEnabled, false)
m.v.SetDefault(param.Accounts, []string{})
m.v.SetDefault(param.StartHour, 9)
m.v.SetDefault(param.EndHour, 15)
m.v.SetDefault(param.TimeZone, "America/Los_Angeles")
m.v.SetDefault(param.CronPath, "/etc/cron.d/chaosmonkey-daily-terminations")
m.v.SetDefault(param.TermPath, "/apps/chaosmonkey/chaosmonkey-terminate.sh")
m.v.SetDefault(param.TermAccount, "root")
m.v.SetDefault(param.MaxApps, math.MaxInt32)
m.v.SetDefault(param.Trackers, []string{})
m.v.SetDefault(param.Decryptor, "")
m.v.SetDefault(param.OutageChecker, "")
m.v.SetDefault(param.DatabasePort, 3306)
m.v.SetDefault(param.SpinnakerEndpoint, "")
m.v.SetDefault(param.SpinnakerCertificate, "")
m.v.SetDefault(param.SpinnakerEncryptedPassword, "")
m.v.SetDefault(param.SpinnakerUser, "")
m.v.SetDefault(param.SpinnakerX509Cert, "")
m.v.SetDefault(param.SpinnakerX509Key, "")
m.v.SetDefault(param.DynamicProvider, "")
m.v.SetDefault(param.DynamicEndpoint, "")
m.v.SetDefault(param.DynamicPath, "")
m.v.SetDefault(param.ScheduleCronPath, "/etc/cron.d/chaosmonkey-schedule")
m.v.SetDefault(param.SchedulePath, "/apps/chaosmonkey/chaosmonkey-schedule.sh")
m.v.SetDefault(param.LogPath, "/var/log")
}
func (m *Monkey) setupEnvVarReader() {
// read from environment variables
m.v.AutomaticEnv()
// Replace "." with "_" when reading environment variables
// e.g.: chaosmonkey.enabled -> CHAOSMONKEY_ENABLED
m.v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
}
// Load returns a Monkey config that loads config from a file
func Load(configPaths []string) (*Monkey, error) {
m := &Monkey{v: viper.New()}
m.setDefaults()
m.setupEnvVarReader()
for _, dir := range configPaths {
m.v.AddConfigPath(dir)
}
m.v.SetConfigType("toml")
m.v.SetConfigName("chaosmonkey")
err := m.v.ReadInConfig()
// It's ok if the config file doesn't exist, but we want to catch any
// other config-related issues
if err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "failed to read config file")
}
log.Printf("no config file found, proceeding without one")
}
err = m.configureRemote()
if err != nil {
return nil, err
}
return m, nil
}
// Defaults returns a Monkey config that just has the default values set
// it will not load local files or remote ones
func Defaults() *Monkey {
v := &Monkey{v: viper.New()}
v.setDefaults()
return v
}
// NewFromReader returns a Monkey config which parses the initial config
// from a reader. It may load remote if configured to
// Config file must be in toml format
func NewFromReader(in io.Reader) (*Monkey, error) {
m := &Monkey{v: viper.New()}
m.setDefaults()
m.v.SetConfigType("toml")
err := m.v.ReadConfig(in)
if err != nil {
return nil, errors.Wrap(err, "failed to parse config")
}
err = m.configureRemote()
if err != nil {
return nil, err
}
return m, nil
}
// configureRemote configures viper for a remote provider if the user has
// specified one
func (m *Monkey) configureRemote() error {
provider := m.v.GetString(param.DynamicProvider)
endpoint := m.v.GetString(param.DynamicEndpoint)
path := m.v.GetString(param.DynamicPath)
// If the user specified an external provider, use it
if provider != "" {
m.remote = true
m.v.SetConfigType("json")
err := m.v.AddRemoteProvider(provider, endpoint, path)
if err != nil {
return errors.Wrapf(err, "failed viper.AddRemoteProvider(provider=\"%s\", endpoint=\"%s\", path=\"%s\"):", provider, endpoint, path)
}
}
return nil
}
// SetRemoteProvider sets remote configuration parameters.
// These will typically be set by parsing the config files. This method
// exists to facilitate testing
func (m *Monkey) SetRemoteProvider(provider string, endpoint string, path string) error {
m.v.Set(param.DynamicProvider, provider)
m.v.Set(param.DynamicEndpoint, endpoint)
m.v.Set(param.DynamicPath, path)
return m.configureRemote()
}
// Set overrides the config value. Used for testing
func (m *Monkey) Set(key string, value interface{}) {
m.v.Set(key, value)
}
// readRemoteConfig retrieves config parameters from a remote source
// If no remote source has been configured, this is a no-op
func (m *Monkey) readRemoteConfig() error {
if !m.remote {
return nil
}
return m.v.ReadRemoteConfig()
}
// Enabled returns true if Chaos Monkey is enabled
func (m *Monkey) Enabled() (bool, error) {
return m.getDynamicBool(param.Enabled)
}
// Leashed returns true if Chaos Monkey is leashed
// In leashed mode, Chaos Monkey records terminations but does not actually
// terminate
func (m *Monkey) Leashed() (bool, error) {
return m.getDynamicBool(param.Leashed)
}
// ScheduleEnabled returns true if Chaos Monkey termination scheduling is enabled
// if false, Chaos Monkey will not generate a termination schedule
func (m *Monkey) ScheduleEnabled() (bool, error) {
return m.getDynamicBool(param.ScheduleEnabled)
}
func (m *Monkey) getDynamicBool(param string) (bool, error) {
err := m.readRemoteConfig()
if err != nil {
return false, err
}
return m.v.GetBool(param), nil
}
// AccountEnabled returns true if Chaos Monkey is enabled for that account
func (m *Monkey) AccountEnabled(account string) (bool, error) {
accounts, err := m.Accounts()
if err != nil {
return false, err
}
for _, x := range accounts {
if account == x {
return true, nil
}
}
return false, nil
}
// Accounts return a list of accounts where Choas Monkey is enabled
func (m *Monkey) Accounts() ([]string, error) {
err := m.readRemoteConfig()
if err != nil {
return nil, err
}
return m.getStringSlice(param.Accounts)
}
// toStrings converts a slice of interfaces to a slice of strings
func toStrings(values []interface{}) ([]string, error) {
result := make([]string, len(values))
for i, x := range values {
x, valid := x.(string)
if !valid {
return nil, errors.Errorf("non-string in %v", values)
}
result[i] = x
}
return result, nil
}
// StartHour (o'clock) is when Chaos
// Monkey starts terminating this value is in [0,23] This is time-zone
// dependent, see the Location method
func (m *Monkey) StartHour() int { return m.v.GetInt(param.StartHour) }
// EndHour (o'clock) is the time after which Chaos Monkey will
// not terminate instances.
// this value is in [0,23]
// This is time-zone dependent, see the Location method
func (m *Monkey) EndHour() int {
return m.v.GetInt(param.EndHour)
}
// Location returns the time zone of StartHour and EndHour.
// May return an error if time.LoadLocation fails
func (m *Monkey) Location() (*time.Location, error) {
return time.LoadLocation(m.v.GetString(param.TimeZone))
}
// CronPath returns the path to where Chaos Monkey
// puts the cron job file with daily terminations
func (m *Monkey) CronPath() string {
return m.v.GetString(param.CronPath)
}
// TermPath returns the path to the executable that
// wraps the chaos monkey binary for terminating instances
func (m *Monkey) TermPath() string {
return m.v.GetString(param.TermPath)
}
// TermAccount returns the account that cron will use
// to execute the termination command
func (m *Monkey) TermAccount() string {
return m.v.GetString(param.TermAccount)
}
// MaxApps returns the maximum number of apps to
// examine for termination
func (m *Monkey) MaxApps() int {
return m.v.GetInt(param.MaxApps)
}
// Trackers returns the names of the backend implementation for
// termination trackers. Used for things like logging and metrics collection
func (m *Monkey) Trackers() ([]string, error) {
return m.getStringSlice(param.Trackers)
}
// ErrorCounter returns the names of the backend implementions for
// error counters. Intended for monitoring/alerting.
func (m *Monkey) ErrorCounter() string {
return m.v.GetString(param.ErrorCounter)
}
func (m *Monkey) getStringSlice(key string) ([]string, error) {
// This could be encoded natively as a list of strings, or as a string that
// represents a list of strings, so we need to handle both cases
t := m.v.Get(key)
if t == nil {
return nil, fmt.Errorf("%s not specified", param.Accounts)
}
switch t := t.(type) {
default:
return nil, fmt.Errorf("%s: unexpected type %T", param.Accounts, t)
case []string: // When set explicitly in code
return t, nil
case []interface{}: // When reading from config file
return toStrings(t)
case string: // When reading from prana, which uses string encoding
// Convert to list of strings
var result []string
err := json.Unmarshal([]byte(t), &result)
return result, err
}
}
// SpinnakerEndpoint returns the spinnaker endpoint
func (m *Monkey) SpinnakerEndpoint() string {
return m.v.GetString(param.SpinnakerEndpoint)
}
// SpinnakerCertificate retunrs a path to a .p12 file that contains a TLS cert
// for authenticating against Spinnaker
func (m *Monkey) SpinnakerCertificate() string {
return m.v.GetString(param.SpinnakerCertificate)
}
// SpinnakerEncryptedPassword returns an password that
// is used to decrypt the Spinnaker certificate. The encryption scheme
// is defined by the Decryptor parameter
func (m *Monkey) SpinnakerEncryptedPassword() string {
return m.v.GetString(param.SpinnakerEncryptedPassword)
}
// SpinnakerUser is sent in the "user" field in the terminateInstances task sent
// to Spinnaker when Spinnaker terminates an instance
func (m *Monkey) SpinnakerUser() string {
return m.v.GetString(param.SpinnakerUser)
}
// SpinnakerX509Cert retunrs a path to a X509 cert file
func (m *Monkey) SpinnakerX509Cert() string {
return m.v.GetString(param.SpinnakerX509Cert)
}
// SpinnakerX509Key retunrs a path to a X509 key file
func (m *Monkey) SpinnakerX509Key() string {
return m.v.GetString(param.SpinnakerX509Key)
}
// Decryptor returns an interface for decrypting secrets
func (m *Monkey) Decryptor() string {
return m.v.GetString(param.Decryptor)
}
// OutageChecker returns an interface for checking if there is an ongoing
// outage
func (m *Monkey) OutageChecker() string {
return m.v.GetString(param.OutageChecker)
}
// DatabaseHost returns the hostname the database is running on
func (m *Monkey) DatabaseHost() string {
return m.v.GetString(param.DatabaseHost)
}
// DatabasePort returns the port the database is listening on
func (m *Monkey) DatabasePort() int {
return m.v.GetInt(param.DatabasePort)
}
// DatabaseUser returns the database user associated with the credentials
func (m *Monkey) DatabaseUser() string {
return m.v.GetString(param.DatabaseUser)
}
// DatabaseName returns the name of the database that stores the Chaos Monkey
// state
func (m *Monkey) DatabaseName() string {
return m.v.GetString(param.DatabaseName)
}
// DatabaseEncryptedPassword returns an encrypted version of the database
// credentials
func (m *Monkey) DatabaseEncryptedPassword() string {
return m.v.GetString(param.DatabaseEncryptedPassword)
}
// BindPFlag binds a specific parameter to a pflag
func (m *Monkey) BindPFlag(parameter string, flag *pflag.Flag) (err error) {
return m.v.BindPFlag(parameter, flag)
}
// The code below is to provide a mechanism for adding a new remote config
// provider without directly viper. Viper wasn't designed for this use-case
// so this is a workaround.
// RemoteProvider is a type alias
type RemoteProvider viper.RemoteProvider
// RemoteConfigFactory is the same interface as viper.remoteConfigFactory
// This is a workaround to be able to support backends other than etc/consul
// without modifying viper
type RemoteConfigFactory interface {
Get(rp RemoteProvider) (io.Reader, error)
Watch(rp RemoteProvider) (io.Reader, error)
}
type proxy struct {
factory RemoteConfigFactory
}
func (p proxy) Get(rp viper.RemoteProvider) (io.Reader, error) {
return p.factory.Get(rp)
}
func (p proxy) Watch(rp viper.RemoteProvider) (io.Reader, error) {
return p.factory.Watch(rp)
}
// SetRemoteProvider sets viper's remote provider
func SetRemoteProvider(name string, factory RemoteConfigFactory) {
viper.RemoteConfig = proxy{factory}
viper.SupportedRemoteProviders = []string{name}
}
// CronExpression returns the chaosmonkey main run cron expression.
// It defaults to 2 hour before start_hour on weekdays, if no cron expression
// is specified in the config
func (m *Monkey) CronExpression() (string, error) {
defaultCron := "0 %d * * 1-5"
cron := m.v.Get(param.CronExpression)
if cron == nil {
runAtHour, err := calculateDefaultCronRunHour(m.StartHour())
if err != nil {
return "", err
}
return fmt.Sprintf(defaultCron, runAtHour), nil
}
switch cron := cron.(type) {
default:
return "", fmt.Errorf("%s: unexpected type %T", param.CronExpression, cron)
case string:
return cron, nil
}
}
// calculates the default cron run hour based on startHour.
// The default cron starts "cronBeforeStartHour" hours
// before "startHour"
func calculateDefaultCronRunHour(startHour int) (int, error) {
if (startHour < clockStartHour) || (startHour > clockEndHour) {
return -1, errors.Errorf("%d is not in cron range(0-23)", startHour)
}
runAtHour := startHour - cronBeforeStartHour
if runAtHour < 0 {
// assuming a 24 hour clock system(0 - 23), -ve values means going back to previous day
// e.g. if start hour is 0 (midnight), the "cronTime" time should be 22 hours
// on the previous day.
return hoursInClock + runAtHour, nil
}
return runAtHour, nil
}
// ScheduleCronPath returns the path to which
// main chaosmonkey crontab is located
func (m *Monkey) ScheduleCronPath() string {
return m.v.GetString(param.ScheduleCronPath)
}
// SchedulePath returns the path to which main
// chaosmonkey schedule script(invoked from cron) is located
func (m *Monkey) SchedulePath() string {
return m.v.GetString(param.SchedulePath)
}
// LogPath returns the path to which
// log files should be written
func (m *Monkey) LogPath() string {
return m.v.GetString(param.LogPath)
}
================================================
FILE: config/monkey_test.go
================================================
// 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 config
import (
"fmt"
"github.com/Netflix/chaosmonkey/v2/config/param"
"testing"
)
func TestDefaultCron(t *testing.T) {
monkey := Defaults()
monkey.Set(param.StartHour, 9)
actual, err := monkey.CronExpression()
if err != nil {
t.Error(err.Error())
return
}
expected := fmt.Sprintf("0 %d * * 1-5", 7)
if actual != expected {
t.Errorf("\nExpected:\n%s\nActual:\n%s", expected, actual)
}
}
func TestDefaultCronForStartHourMidnight(t *testing.T) {
monkey := Defaults()
monkey.Set(param.StartHour, 0)
actual, err := monkey.CronExpression()
if err != nil {
t.Error(err.Error())
return
}
expected := fmt.Sprintf("0 %d * * 1-5", 22)
if actual != expected {
t.Errorf("\nExpected:\n%s\nActual:\n%s", expected, actual)
}
}
func TestDefaultCronForStartHourOneAM(t *testing.T) {
monkey := Defaults()
monkey.Set(param.StartHour, 1)
actual, err := monkey.CronExpression()
if err != nil {
t.Error(err.Error())
return
}
expected := fmt.Sprintf("0 %d * * 1-5", 23)
if actual != expected {
t.Errorf("\nExpected:\n%s\nActual:\n%s", expected, actual)
}
}
func TestDefaultCronForStartHourBeforeClockStart(t *testing.T) {
monkey := Defaults()
monkey.Set(param.StartHour, -1)
_, err := monkey.CronExpression()
if err == nil {
t.Error("Expected InstalledCronExpression to return an error as start hour is before clock start hour")
return
}
}
func TestDefaultCronForStartHourAfterClockEnd(t *testing.T) {
monkey := Defaults()
monkey.Set(param.StartHour, 24)
_, err := monkey.CronExpression()
if err == nil {
t.Error("Expected InstalledCronExpression to return an error as start hour is after clock end hour")
return
}
}
================================================
FILE: config/param/param.go
================================================
// 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 param
// properties
const (
Enabled = "chaosmonkey.enabled"
Leashed = "chaosmonkey.leashed"
ScheduleEnabled = "chaosmonkey.schedule_enabled"
Accounts = "chaosmonkey.accounts"
StartHour = "chaosmonkey.start_hour"
EndHour = "chaosmonkey.end_hour"
TimeZone = "chaosmonkey.time_zone"
CronPath = "chaosmonkey.cron_path"
TermPath = "chaosmonkey.term_path"
TermAccount = "chaosmonkey.term_account"
MaxApps = "chaosmonkey.max_apps"
Trackers = "chaosmonkey.trackers"
ErrorCounter = "chaosmonkey.error_counter"
Decryptor = "chaosmonkey.decryptor"
OutageChecker = "chaosmonkey.outage_checker"
CronExpression = "chaosmonkey.cron_expression"
ScheduleCronPath = "chaosmonkey.schedule_cron_path"
SchedulePath = "chaosmonkey.schedule_path"
LogPath = "chaosmonkey.log_path"
// spinnaker
SpinnakerEndpoint = "spinnaker.endpoint"
SpinnakerCertificate = "spinnaker.certificate"
SpinnakerEncryptedPassword = "spinnaker.encrypted_password"
SpinnakerUser = "spinnaker.user"
SpinnakerX509Cert = "spinnaker.x509_cert"
SpinnakerX509Key = "spinnaker.x509_key"
// database
DatabaseHost = "database.host"
DatabasePort = "database.port"
DatabaseUser = "database.user"
DatabaseEncryptedPassword = "database.encrypted_password"
DatabaseName = "database.name"
// dynamic property provider
DynamicProvider = "dynamic.provider"
DynamicEndpoint = "dynamic.endpoint"
DynamicPath = "dynamic.path"
)
================================================
FILE: constrainer/constrainer.go
================================================
// Copyright 2017 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 constrainer
import (
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/deps"
"github.com/Netflix/chaosmonkey/v2/schedule"
)
// NullConstrainer is a no-op constrainer
type NullConstrainer struct{}
func init() {
deps.GetConstrainer = getNullConstrainer
}
// Filter implements schedule.Constrainer.Filter
// This is a no-op implementation
func (n NullConstrainer) Filter(s schedule.Schedule) schedule.Schedule {
return s
}
func getNullConstrainer(cfg *config.Monkey) (schedule.Constrainer, error) {
return NullConstrainer{}, nil
}
================================================
FILE: decryptor/decryptor.go
================================================
// 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 decryptor
import (
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/deps"
"github.com/pkg/errors"
)
type nullDecryptor struct{}
// Decrypt implements chaosmonkey.Decryptor.Decrypt
// This is a no-op implementation that simply returns the plaintext
func (n nullDecryptor) Decrypt(ciphertext string) (string, error) {
return ciphertext, nil
}
func init() {
deps.GetDecryptor = getNullDecryptor
}
func getNullDecryptor(cfg *config.Monkey) (chaosmonkey.Decryptor, error) {
kind := cfg.Decryptor()
if kind != "" {
return nil, errors.Errorf("unsupported decryptor: %s", kind)
}
return nullDecryptor{}, nil
}
================================================
FILE: deploy/app.go
================================================
// 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 deploy
// App represents an application
type App struct {
name string
accounts []*Account
}
// Name returns the name of an app
func (a App) Name() string {
return a.name
}
// Accounts returns a slice of accounts
func (a App) Accounts() []*Account {
return a.accounts
}
type (
// AppName is the name of an app
AppName string
// AccountName is the name of a cloud account
AccountName string
// ClusterName is the app-stack-detail name of a cluster
ClusterName string
// StackName is the stack part of the cluster name
StackName string
// RegionName is the name of an AWS region
RegionName string
// ASGName is the app-stack-detail-sequence name of an ASG
ASGName string
// InstanceID is the i-xxxxxx name of an AWS instance or uuid of a container
InstanceID string
// CloudProvider is the name of the cloud backend (e.g., aws)
CloudProvider string
// ClusterMap maps cluster name to information about instances by region and
// ASG
ClusterMap map[ClusterName]map[RegionName]map[ASGName][]InstanceID
// AccountInfo tracks the provider and the clusters
AccountInfo struct {
CloudProvider string
Clusters ClusterMap
}
// AppMap is a map that tracks info about an app
AppMap map[AccountName]AccountInfo
)
// NewApp constructs a new App
func NewApp(name string, data AppMap) *App {
app := App{name: name}
for accountName, accountInfo := range data {
account := Account{name: string(accountName), app: &app, cloudProvider: accountInfo.CloudProvider}
app.accounts = append(app.accounts, &account)
for clusterName, clusterValue := range accountInfo.Clusters {
cluster := Cluster{name: string(clusterName), account: &account}
account.clusters = append(account.clusters, &cluster)
for regionName, regionValue := range clusterValue {
for asgName, instanceIds := range regionValue {
asg := ASG{
name: string(asgName),
region: string(regionName),
cluster: &cluster,
}
cluster.asgs = append(cluster.asgs, &asg)
for _, id := range instanceIds {
instance := Instance{
id: string(id),
asg: &asg,
}
asg.instances = append(asg.instances, &instance)
}
}
}
}
}
return &app
}
================================================
FILE: deploy/asg.go
================================================
// 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 deploy
import frigga "github.com/SmartThingsOSS/frigga-go"
// ASG identifies an autoscaling group in the deployment
type ASG struct {
name string
region string
instances []*Instance
cluster *Cluster
}
// NewASG creates a new ASG
func NewASG(name, region string, instanceIDs []string, cluster *Cluster) *ASG {
result := ASG{
name: name,
region: region,
instances: make([]*Instance, len(instanceIDs)),
cluster: cluster,
}
for i, id := range instanceIDs {
result.instances[i] = &Instance{id, &result}
}
return &result
}
// Instances returns a slice of the instances associated with the ASG
func (a *ASG) Instances() []*Instance {
return a.instances
}
// Empty returns true if the ASG does not contain any instances
func (a *ASG) Empty() bool {
return len(a.instances) == 0
}
// AppName returns the name of the app associated with this ASG
func (a *ASG) AppName() string {
return a.cluster.AppName()
}
// AccountName returns the name of the AWS account associated with the ASG
func (a *ASG) AccountName() string {
return a.cluster.AccountName()
}
// ClusterName returns the name of the cluster associated with the ASG
func (a *ASG) ClusterName() string {
return a.cluster.name
}
// DetailName returns the name of the detail field associated with the ASG
func (a *ASG) DetailName() string {
asgName := a.Name()
if a.missingPushNumber() {
/*
ASGs that were launched before Spinnaker existed may be missing the -vXXX
push number at the end of the ASG. If this happens, we need to guard
against the case where the detail field happens to match the push
field syntax.
In this case, we work around it by appending a phony push number before
parsing with frigga.
*/
asgName += "-v000"
}
names, err := frigga.Parse(asgName)
if err != nil {
panic(err)
}
return names.Detail
}
// missingPushNumber returns true if the ASG does not have an associated push
// number
func (a *ASG) missingPushNumber() bool {
return a.Name() == a.ClusterName()
}
// RegionName returns the name of the region associated with the ASG
func (a *ASG) RegionName() string {
return a.region
}
// Name returns the name of the ASG
func (a *ASG) Name() string {
return a.name
}
// StackName returns the name of the stack
func (a *ASG) StackName() string {
return a.cluster.StackName()
}
// CloudProvider returns the cloud provider (e.g., "aws")
func (a *ASG) CloudProvider() string {
return a.cluster.CloudProvider()
}
================================================
FILE: deploy/deploy_test.go
================================================
// 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 deploy
import (
"reflect"
"runtime"
"testing"
)
func TestASGAndClusters(t *testing.T) {
nameOf := func(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}
type tcase struct {
appName string
accountName string
regionName string
clusterName string
asgName string
ids []string
}
makeClusterASG := func(tc tcase) (*Cluster, *ASG) {
var cluster Cluster
var account Account
var app App
cloudProvider := "aws"
asg := NewASG(tc.asgName, tc.regionName, tc.ids, &cluster)
cluster = Cluster{tc.clusterName, []*ASG{asg}, &account}
account = Account{tc.accountName, []*Cluster{&cluster}, &app, cloudProvider}
app = App{tc.appName, []*Account{&account}}
return &cluster, asg
}
type at struct {
f func(*ASG) string
want string
}
type ct struct {
f func(*Cluster) string
want string
}
var tests = []struct {
scenario string
t tcase
a []at
c []ct
}{
{
"stack and detail",
tcase{"foo", "test", "us-east-1", "foo-staging-bar", "foo-staging-bar-v031", []string{"i-ff075688", "i-d9165a77"}},
[]at{
{(*ASG).Name, "foo-staging-bar-v031"},
{(*ASG).AppName, "foo"},
{(*ASG).AccountName, "test"},
{(*ASG).RegionName, "us-east-1"},
{(*ASG).ClusterName, "foo-staging-bar"},
{(*ASG).StackName, "staging"},
{(*ASG).DetailName, "bar"},
},
[]ct{
{(*Cluster).Name, "foo-staging-bar"},
{(*Cluster).AppName, "foo"},
{(*Cluster).AccountName, "test"},
{(*Cluster).StackName, "staging"},
},
},
{
"no detail",
tcase{"chaosguineapig", "prod", "eu-west-1", "chaosguineapig-staging", "chaosguineapig-staging-v000", []string{"i-7f40bbf5", "i-7a61d6f2"}},
[]at{
{(*ASG).Name, "chaosguineapig-staging-v000"},
{(*ASG).AppName, "chaosguineapig"},
{(*ASG).AccountName, "prod"},
{(*ASG).RegionName, "eu-west-1"},
{(*ASG).ClusterName, "chaosguineapig-staging"},
{(*ASG).StackName, "staging"},
{(*ASG).DetailName, ""},
},
[]ct{
{(*Cluster).Name, "chaosguineapig-staging"},
{(*Cluster).AppName, "chaosguineapig"},
{(*Cluster).AccountName, "prod"},
{(*Cluster).StackName, "staging"},
},
},
{
"no stack",
tcase{"chaosguineapig", "test", "eu-west-1", "chaosguineapig", "chaosguineapig-v030", []string{"i-7f40bbf5", "i-7a61d6f2"}},
[]at{
{(*ASG).Name, "chaosguineapig-v030"},
{(*ASG).AppName, "chaosguineapig"},
{(*ASG).AccountName, "test"},
{(*ASG).RegionName, "eu-west-1"},
{(*ASG).ClusterName, "chaosguineapig"},
{(*ASG).StackName, ""},
{(*ASG).DetailName, ""},
},
[]ct{
{(*Cluster).Name, "chaosguineapig"},
{(*Cluster).AppName, "chaosguineapig"},
{(*Cluster).AccountName, "test"},
{(*Cluster).StackName, ""},
},
},
{
// We hit one case where there was a cluster with a name like foo-bar-v2, where the
// asg had the same name: foo-bar-v2. The ASG had no push number, and the
// detail looks like a push number.
"detail looks like push number",
tcase{"foo", "prod", "us-west-2", "foo-bar-v2", "foo-bar-v2", []string{"i-c7a513fc", "i-e06cfef1"}},
[]at{
{(*ASG).Name, "foo-bar-v2"},
{(*ASG).AppName, "foo"},
{(*ASG).AccountName, "prod"},
{(*ASG).RegionName, "us-west-2"},
{(*ASG).ClusterName, "foo-bar-v2"},
{(*ASG).StackName, "bar"},
{(*ASG).DetailName, "v2"},
},
[]ct{
{(*Cluster).Name, "foo-bar-v2"},
{(*Cluster).AppName, "foo"},
{(*Cluster).AccountName, "prod"},
{(*Cluster).StackName, "bar"},
},
},
}
for _, tt := range tests {
cluster, asg := makeClusterASG(tt.t)
// ASG tests
for _, att := range tt.a {
if got, want := att.f(asg), att.want; got != want {
t.Errorf("scenario %s: got %s()=%s, want: %s", tt.scenario, nameOf(att.f), got, want)
}
}
// cluster tests
for _, ctt := range tt.c {
if got, want := ctt.f(cluster), ctt.want; got != want {
t.Errorf("scenario %s: got %s()=%s, want: %s", tt.scenario, nameOf(ctt.f), got, want)
}
}
}
}
================================================
FILE: deploy/deployment.go
================================================
// 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 deploy contains information about all of the deployed instances, and how
// they are organized across accounts, apps, regions, clusters, and autoscaling
// groups.
package deploy
import (
"fmt"
"github.com/SmartThingsOSS/frigga-go"
)
// Deployment contains information about how apps are deployed
type Deployment interface {
// Apps sends App objects over a channel
Apps(c chan<- *App, appNames []string)
// GetApp retrieves a single App
GetApp(name string) (*App, error)
// AppNames returns the names of all apps
AppNames() ([]string, error)
// GetInstanceIDs returns the ids for instances in a cluster
GetInstanceIDs(app string, account AccountName, cloudProvider string, region RegionName, cluster ClusterName) (asgName ASGName, instances []InstanceID, err error)
// GetClusterNames returns the list of cluster names
GetClusterNames(app string, account AccountName) ([]ClusterName, error)
// GetRegionNames returns the list of regions associated with a cluster
GetRegionNames(app string, account AccountName, cluster ClusterName) ([]RegionName, error)
// CloudProvider returns the provider associated with an account
CloudProvider(account string) (provider string, err error)
}
// Account represents the set of clusters associated with an App that reside
// in one AWS account (e.g., "prod", "test").
type Account struct {
name string // e.g., "prod", "test"
clusters []*Cluster
app *App
cloudProvider string // e.g., "aws"
}
// Name returns the name of the account associated with this account
func (a *Account) Name() string {
return a.name
}
// Clusters returns a slice of clusters
func (a *Account) Clusters() []*Cluster {
return a.clusters
}
// AppName returns the name of the app associated with this Account
func (a *Account) AppName() string {
return a.app.name
}
// RegionNames returns the name of the regions that clusters in this account are
// running in
func (a *Account) RegionNames() []string {
m := make(map[string]bool)
// Get the region names of the clusters
for _, cluster := range a.Clusters() {
for _, name := range cluster.RegionNames() {
m[name] = true
}
}
result := make([]string, 0, len(m))
for name := range m {
result = append(result, name)
}
return result
}
// CloudProvider returns the cloud provider (e.g., "aws")
func (a *Account) CloudProvider() string {
return a.cloudProvider
}
type stringSet map[string]bool
func (s *stringSet) add(val string) {
(*s)[val] = true
}
// slice converts a stringSet to a string slice
func (s stringSet) slice() []string {
result := []string{}
for val := range s {
result = append(result, val)
}
return result
}
// StackNames returns the names of the stacks associated with this account
func (a *Account) StackNames() []string {
stacks := make(stringSet)
for _, cluster := range a.Clusters() {
stacks.add(cluster.StackName())
}
return stacks.slice()
}
// Cluster represents what Spinnaker refers to as a "cluster", which
// contains app-stack-detail.
// Every ASG is associated with exactly one cluster.
// Note that clusters can span regions
type Cluster struct {
name string
asgs []*ASG
account *Account
}
// Name returns the name of the cluster, convention: app-stack-detail
func (c *Cluster) Name() string {
return c.name
}
// AppName returns the name of the app associated with this cluster
func (c *Cluster) AppName() string {
return c.account.AppName()
}
// StackName returns the name of the stack, following the app-stack-detail convention
func (c *Cluster) StackName() string {
names, err := frigga.Parse(c.Name())
if err != nil {
panic(err)
}
return names.Stack
}
// AccountName returns the name of the account associated with this cluster
func (c *Cluster) AccountName() string {
return c.account.Name()
}
// ASGs returns a slice of ASGs
func (c *Cluster) ASGs() []*ASG {
return c.asgs
}
// RegionNames returns the name of the region that this cluster runs in
func (c *Cluster) RegionNames() []string {
m := make(map[string]bool)
for _, asg := range c.ASGs() {
m[asg.RegionName()] = true
}
result := []string{}
for name := range m {
result = append(result, name)
}
return result
}
// CloudProvider returns the cloud provider (e.g., "aws")
func (c *Cluster) CloudProvider() string {
return c.account.CloudProvider()
}
// Instance implements instance.Instance
type Instance struct {
// instance id (e.g., "i-74e93ddb")
id string
// ASG that this instance is part of
asg *ASG
}
func (i *Instance) String() string {
return fmt.Sprintf("app=%s account=%s region=%s stack=%s cluster=%s asg=%s instance-id=%s",
i.AppName(), i.AccountName(), i.RegionName(), i.StackName(), i.ClusterName(), i.ASGName(), i.ID())
}
// AppName returns the name of the app associated with this instance
func (i *Instance) AppName() string {
return i.asg.AppName()
}
// AccountName returns the name of the AWS account associated with the instance
func (i *Instance) AccountName() string {
return i.asg.AccountName()
}
// ClusterName returns the name of the cluster associated with the instance
func (i *Instance) ClusterName() string {
return i.asg.ClusterName()
}
// RegionName returns the name of the region associated with the instance
func (i *Instance) RegionName() string {
return i.asg.RegionName()
}
// ASGName returns the name of the ASG associated with the instance
func (i *Instance) ASGName() string {
return i.asg.Name()
}
// StackName returns the name of the stack associated with the instance
func (i *Instance) StackName() string {
return i.asg.StackName()
}
// CloudProvider returns the cloud provider (e.g., "aws")
func (i *Instance) CloudProvider() string {
return i.asg.CloudProvider()
}
// ID returns the instance id
func (i *Instance) ID() string {
return i.id
}
================================================
FILE: deploy/eligible_instance_groups.go
================================================
// 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 deploy
import (
"fmt"
"log"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/grp"
)
// EligibleInstanceGroups returns a slice of InstanceGroups that represent
// groups of instances that are eligible for termination.
//
// Note that this code does not check for violations of minimum time between
// terminations. Chaos Monkey checks that precondition immediately before
// termination, not when considering groups of eligible instances.
//
// The way instances are divided into group will depend on
// - the grouping configuration for the app (cluster, stack, app)
// - whether regions are independent
//
// The returned InstanceGroups are guaranteed to contain at least one instance
// each
//
// Preconditions:
// - app is enabled for Chaos Monkey
func (app *App) EligibleInstanceGroups(cfg chaosmonkey.AppConfig) []grp.InstanceGroup {
if !cfg.Enabled {
log.Fatalf("app %s unexpectedly disabled", app.Name())
}
grouping := cfg.Grouping
indep := cfg.RegionsAreIndependent
switch {
case grouping == chaosmonkey.App && indep:
return appIndep(app)
case grouping == chaosmonkey.App && !indep:
return appDep(app)
case grouping == chaosmonkey.Stack && indep:
return stackIndep(app)
case grouping == chaosmonkey.Stack && !indep:
return stackDep(app)
case grouping == chaosmonkey.Cluster && indep:
return clusterIndep(app)
case grouping == chaosmonkey.Cluster && !indep:
return clusterDep(app)
default:
panic(fmt.Sprintf("Unknown grouping: %d", grouping))
}
}
// appindep returns a list of groups grouped by (app, account, region)
func appIndep(app *App) []grp.InstanceGroup {
result := []grp.InstanceGroup{}
for _, account := range app.accounts {
for _, regionName := range account.RegionNames() {
result = append(result, grp.New(app.Name(), account.Name(), regionName, "", ""))
}
}
return result
}
// stackIndep returns a list of groups grouped by (app, account)
func appDep(app *App) []grp.InstanceGroup {
result := []grp.InstanceGroup{}
for _, account := range app.accounts {
result = append(result, grp.New(app.Name(), account.Name(), "", "", ""))
}
return result
}
// stackIndep returns a list of groups grouped by (app, account, stack, region)
func stackIndep(app *App) []grp.InstanceGroup {
type asr struct {
account string
stack string
region string
}
set := make(map[asr]bool)
for _, account := range app.Accounts() {
for _, cluster := range account.Clusters() {
stackName := cluster.StackName()
for _, regionName := range cluster.RegionNames() {
set[asr{account: account.Name(), stack: stackName, region: regionName}] = true
}
}
}
result := []grp.InstanceGroup{}
for x := range set {
result = append(result, grp.New(app.Name(), x.account, x.region, x.stack, ""))
}
return result
}
// stackDep returns a list of groups grouped by (app, account, stack)
func stackDep(app *App) []grp.InstanceGroup {
result := []grp.InstanceGroup{}
for _, account := range app.accounts {
for _, stackName := range account.StackNames() {
result = append(result, grp.New(app.Name(), account.Name(), "", stackName, ""))
}
}
return result
}
// clusterDep returns a list of groups grouped by (app, account, cluster, region)
func clusterIndep(app *App) []grp.InstanceGroup {
result := []grp.InstanceGroup{}
for _, account := range app.accounts {
for _, cluster := range account.Clusters() {
for _, regionName := range cluster.RegionNames() {
result = append(result, grp.New(app.Name(), account.Name(), regionName, "", cluster.Name()))
}
}
}
return result
}
// clusterDep returns a list of groups grouped by (app, account, cluster)
func clusterDep(app *App) []grp.InstanceGroup {
result := []grp.InstanceGroup{}
for _, account := range app.accounts {
for _, cluster := range account.Clusters() {
result = append(result, grp.New(app.Name(), account.Name(), "", "", cluster.Name()))
}
}
return result
}
================================================
FILE: deploy/eligible_instance_groups_test.go
================================================
// 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 deploy
import (
"reflect"
"testing"
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/grp"
)
type groupList []grp.InstanceGroup
var grouptests = []struct {
cfg chaosmonkey.AppConfig
groups []grp.InstanceGroup
}{
{conf(chaosmonkey.App, false), groupList{
grp.New("mock", "prod", "", "", ""),
grp.New("mock", "test", "", "", ""),
}},
{conf(chaosmonkey.App, true), groupList{
grp.New("mock", "prod", "us-east-1", "", ""),
grp.New("mock", "prod", "us-west-2", "", ""),
grp.New("mock", "test", "us-east-1", "", ""),
grp.New("mock", "test", "us-west-2", "", ""),
}},
{conf(chaosmonkey.Stack, false), groupList{
grp.New("mock", "prod", "", "prod", ""),
grp.New("mock", "prod", "", "staging", ""),
grp.New("mock", "test", "", "test", ""),
grp.New("mock", "test", "", "beta", ""),
}},
{conf(chaosmonkey.Stack, true), groupList{
grp.New("mock", "prod", "us-east-1", "prod", ""),
grp.New("mock", "prod", "us-west-2", "prod", ""),
grp.New("mock", "prod", "us-east-1", "staging", ""),
grp.New("mock", "prod", "us-west-2", "staging", ""),
grp.New("mock", "test", "us-east-1", "test", ""),
grp.New("mock", "test", "us-west-2", "test", ""),
grp.New("mock", "test", "us-east-1", "beta", ""),
grp.New("mock", "test", "us-west-2", "beta", ""),
}},
{conf(chaosmonkey.Cluster, false), groupList{
grp.New("mock", "prod", "", "", "mock-prod-a"),
grp.New("mock", "prod", "", "", "mock-prod-b"),
grp.New("mock", "prod", "", "", "mock-staging-a"),
grp.New("mock", "prod", "", "", "mock-staging-b"),
grp.New("mock", "test", "", "", "mock-test-a"),
grp.New("mock", "test", "", "", "mock-test-b"),
grp.New("mock", "test", "", "", "mock-beta-a"),
grp.New("mock", "test", "", "", "mock-beta-b"),
}},
{conf(chaosmonkey.Cluster, true), groupList{
grp.New("mock", "prod", "us-east-1", "", "mock-prod-a"),
grp.New("mock", "prod", "us-west-2", "", "mock-prod-a"),
grp.New("mock", "prod", "us-east-1", "", "mock-prod-b"),
grp.New("mock", "prod", "us-west-2", "", "mock-prod-b"),
grp.New("mock", "prod", "us-east-1", "", "mock-staging-a"),
grp.New("mock", "prod", "us-west-2", "", "mock-staging-a"),
grp.New("mock", "prod", "us-east-1", "", "mock-staging-b"),
grp.New("mock", "prod", "us-west-2", "", "mock-staging-b"),
grp.New("mock", "test", "us-east-1", "", "mock-test-a"),
grp.New("mock", "test", "us-west-2", "", "mock-test-a"),
grp.New("mock", "test", "us-east-1", "", "mock-test-b"),
grp.New("mock", "test", "us-west-2", "", "mock-test-b"),
grp.New("mock", "test", "us-east-1", "", "mock-beta-a"),
grp.New("mock", "test", "us-west-2", "", "mock-beta-a"),
grp.New("mock", "test", "us-east-1", "", "mock-beta-b"),
grp.New("mock", "test", "us-west-2", "", "mock-beta-b"),
}},
}
func TestEligibleInstanceGroups(t *testing.T) {
for i, tt := range grouptests {
groups := mockApp.EligibleInstanceGroups(tt.cfg)
if len(tt.groups) != len(groups) {
t.Errorf("test %d: incorrect number of groups. Expected: %d. Actual: %d", i, len(tt.groups), len(groups))
continue
}
if !same(tt.groups, groups) {
t.Errorf("test %d. Expected: %+v. Actual: %+v", i, tt.groups, groups)
}
}
}
//
// Test helper code
//
// conf creates a config file used for testing
func conf(grouping chaosmonkey.Group, regionsAreIndependent bool) chaosmonkey.AppConfig {
return chaosmonkey.AppConfig{
Enabled: true,
RegionsAreIndependent: regionsAreIndependent,
MeanTimeBetweenKillsInWorkDays: 5,
MinTimeBetweenKillsInWorkDays: 1,
Grouping: grouping,
}
}
type groupSet map[grp.InstanceGroup]bool
func (gs *groupSet) add(group grp.InstanceGroup) {
(*gs)[group] = true
}
func (gl groupList) toSet() groupSet {
result := make(groupSet)
for _, group := range gl {
result.add(group)
}
return result
}
// same return true if the two lists of groups contain the same elements,
// independent of order
func same(x, y groupList) bool {
sx := x.toSet()
sy := y.toSet()
return reflect.DeepEqual(sx, sy)
}
var usEast1 = RegionName("us-east-1")
var usWest2 = RegionName("us-west-2")
var mockApp = NewApp("mock", AppMap{
AccountName("prod"): {
CloudProvider: "aws",
Clusters: ClusterMap{
ClusterName("mock-prod-a"): {
usEast1: {
ASGName("mock-prod-a-v123"): []InstanceID{"i-4a003cd0"},
},
usWest2: {
ASGName("mock-prod-a-v111"): []InstanceID{"i-efdc42dc"},
},
},
ClusterName("mock-prod-b"): {
usEast1: {
ASGName("mock-prod-b-v002"): []InstanceID{"i-115ccc27"},
},
usWest2: {
ASGName("mock-prod-b-v001"): []InstanceID{"i-7881287e"},
},
},
ClusterName("mock-staging-a"): {
usEast1: {
ASGName("mock-staging-a-v123"): []InstanceID{"i-ff8e7e4b"},
},
usWest2: {
ASGName("mock-staging-a-v111"): []InstanceID{"i-6eed18a4"},
},
},
ClusterName("mock-staging-b"): {
usEast1: {
ASGName("mock-staging-b-v002"): []InstanceID{"i-13770e40"},
},
usWest2: {
ASGName("mock-staging-b-v001"): []InstanceID{"i-afb7595e"},
},
},
},
},
AccountName("test"): {
CloudProvider: "aws",
Clusters: ClusterMap{
ClusterName("mock-test-a"): {
usEast1: {
ASGName("mock-test-a-v123"): []InstanceID{"i-23b61f89"},
},
usWest2: {
ASGName("mock-test-a-v111"): []InstanceID{"i-fe7a0827"},
},
},
ClusterName("mock-test-b"): {
usEast1: {
ASGName("mock-test-b-v002"): []InstanceID{"i-f581d5c3"},
},
usWest2: {
ASGName("mock-test-b-v001"): []InstanceID{"i-986e988a"},
},
},
ClusterName("mock-beta-a"): {
usEast1: {
ASGName("mock-beta-a-v123"): []InstanceID{"i-4b359d5d"},
},
usWest2: {
ASGName("mock-beta-a-v111"): []InstanceID{"i-e751bdd2"},
},
},
ClusterName("mock-beta-b"): {
usEast1: {
ASGName("mock-beta-b-v002"): []InstanceID{"i-e5eeba5e"},
},
usWest2: {
ASGName("mock-beta-b-v001"): []InstanceID{"i-76013ffb"},
},
},
},
},
})
================================================
FILE: deps/deps.go
================================================
// 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 deps holds a set of interfaces
package deps
import (
"github.com/Netflix/chaosmonkey/v2"
"github.com/Netflix/chaosmonkey/v2/clock"
"github.com/Netflix/chaosmonkey/v2/config"
"github.com/Netflix/chaosmonkey/v2/deploy"
"github.com/Netflix/chaosmonkey/v2/schedule"
)
var (
// GetTrackers returns a list of trackers
// This variable must be set in the init() method of a module imported by
// the main module.
GetTrackers func(*config.Monkey) ([]chaosmonkey.Tracker, error)
// GetErrorCounter returns an error counter
GetErrorCounter func(*config.Monkey) (chaosmonkey.ErrorCounter, error)
// GetDecryptor returns a decryptor
GetDecryptor func(*config.Monkey) (chaosmonkey.Decryptor, error)
// GetEnv returns info about the deployed environment
GetEnv func(*config.Monkey) (chaosmonkey.Env, error)
// GetOutage returns an interface for checking if there is an outage
GetOutage func(*config.Monkey) (chaosmonkey.Outage, error)
// GetConstrainer returns an interface for constraining the schedule
GetConstrainer func(*config.Monkey) (schedule.Constrainer, error)
)
// Deps are a common set of external dependencies
type Deps struct {
MonkeyCfg *config.Monkey
Checker chaosmonkey.Checker
ConfGetter chaosmonkey.AppConfigGetter
Cl clock.Clock
Dep deploy.Deployment
T chaosmonkey.Terminator
Trackers []chaosmonkey.Tracker
Ou chaosmonkey.Outage
ErrCounter chaosmonkey.ErrorCounter
Env chaosmonkey.Env
}
================================================
FILE: docs/Configuration-file-format.md
================================================
The config file is in [TOML] format.
Chaos Monkey will look for a file named `chaosmonkey.toml` in the following
locations:
* `.` (current directory)
* `/apps/chaosmonkey`
* `/etc`
* `/etc/chaosmonkey`
## Example
Here is an example configuration file:
[TOML]: https://github.com/toml-lang/toml
```
[chaosmonkey]
enabled = true
schedule_enabled = true
leashed = false
accounts = ["production", "test"]
[database]
host = "dbhost.example.com"
name = "chaosmonkey"
user = "chaosmonkey"
encrypted_password = "securepasswordgoeshere"
[spinnaker]
endpoint = "http://spinnaker.example.com:8084"
```
Note that while the field is called "encrypted_password", you should put the
unencrypted version of your password here. Chaos Monkey currently only ships
with a no-op (do nothing) password decryptor.
### Defaults
The following example shows all of the default values:
```
[chaosmonkey]
enabled = false # if false, won't terminate instances when invoked
leashed = true # if true, terminations are only simulated (logged only)
schedule_enabled = false # if true, will generate schedule of terminations each weekday
accounts = [] # list of Spinnaker accounts with chaos monkey enabled, e.g.: ["prod", "test"]
start_hour = 9 # time during day when starts terminating
end_hour = 15 # time during day when stops terminating
# tzdata format, see TZ column in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# Other allowed values: "UTC", "Local"
time_zone = "America/Los_Angeles" # time zone used by start.hour and end.hour
term_account = "root" # account used to run the term_path command
max_apps = 2147483647 # max number of apps Chaos Monkey will schedule terminations for
# location of command Chaos Monkey uses for doing terminations
term_path = "/apps/chaosmonkey/chaosmonkey-terminate.sh"
# cron file that Chaos Monkey writes to each day for scheduling kills
cron_path = "/etc/cron.d/chaosmonkey-daily-terminations"
# decryption system for encrypted_password fields for spinnaker and database
decryptor = ""
# event tracking systems that records chaos monkey terminations
trackers = []
# metric collection systems that track errors for monitoring/alerting
error_counter = ""
# outage checking system that tells chaos monkey if there is an ongoing outage
outage_checker = ""
[database]
host = "" # database host
port = 3306 # tcp port that the database is listening on
user = "" # database user
encrypted_password = "" # password for database auth, encrypted by decryptor
name = "" # name of database that contains chaos monkey data
[spinnaker]
endpoint = "" # spinnaker api url
certificate = "" # path to p12 file when using client-side tls certs
encrypted_password = "" # password used for p12 certificate, encrypted by decryptor
user = "" # user associated with terminations, sent in API call to terminate
# For dynamic configuration options, see viper docs
[dynamic]
provider = "" # options: "etcd", "consul"
endpoint = "" # url for dynamic provider
path = "" # path for dynamic provider
```
Note that many of these configuration parameters (decryptor, trackers,
gitextract_082sx3zv/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── NOTICE ├── OSSMETADATA ├── README.md ├── cal/ │ ├── cal.go │ └── cal_test.go ├── chaosmonkey.go ├── chaosmonkey_test.go ├── clock/ │ └── clock.go ├── cmd/ │ └── chaosmonkey/ │ └── main.go ├── command/ │ ├── chaosmonkey.go │ ├── command.go │ ├── dumpconfig.go │ ├── dumpmonkeyconfig.go │ ├── eligible.go │ ├── fetchschedule.go │ ├── install.go │ ├── install_test.go │ ├── migrate.go │ ├── osutil.go │ ├── outage.go │ ├── regions.go │ ├── schedule.go │ ├── schedule_int_test.go │ ├── schedule_test.go │ └── terminate.go ├── config/ │ ├── config.go │ ├── config_test.go │ ├── monkey.go │ ├── monkey_test.go │ └── param/ │ └── param.go ├── constrainer/ │ └── constrainer.go ├── decryptor/ │ └── decryptor.go ├── deploy/ │ ├── app.go │ ├── asg.go │ ├── deploy_test.go │ ├── deployment.go │ ├── eligible_instance_groups.go │ └── eligible_instance_groups_test.go ├── deps/ │ └── deps.go ├── docKey.enc ├── docs/ │ ├── Configuration-file-format.md │ ├── Configuring-behavior-via-Spinnaker.md │ ├── How-to-deploy.md │ ├── Running-locally.md │ ├── Termination-behavior.md │ ├── dev/ │ │ ├── Running-tests.md │ │ └── Vendoring-dependencies.md │ ├── index.md │ └── plugins/ │ ├── Constrainer.md │ ├── Decryptor.md │ ├── Error-counter.md │ ├── Outage-checker.md │ ├── Tracker.md │ └── index.md ├── eligible/ │ ├── eligible.go │ ├── eligible_test.go │ ├── instances_canary_test.go │ └── instances_test.go ├── env/ │ └── env.go ├── errorcounter/ │ └── errorcounter.go ├── go.mod ├── go.sum ├── grp/ │ ├── grp.go │ └── grp_test.go ├── migration/ │ ├── migrations.go │ └── mysql/ │ └── 1.0.0_initial_schema.sql ├── mkdocs.yml ├── mock/ │ ├── configgetter.go │ ├── deployment.go │ ├── deps.go │ ├── install.go │ ├── instance.go │ ├── mock.go │ ├── outage.go │ └── terminator.go ├── mysql/ │ ├── checker_test.go │ ├── mysql.go │ ├── mysql_test.go │ ├── no_kills_since_test.go │ └── schedstore_test.go ├── outage/ │ └── outage.go ├── schedstore/ │ └── schedstore.go ├── schedule/ │ ├── constrainer.go │ ├── schedule.go │ └── schedule_test.go ├── spinnaker/ │ ├── config.go │ ├── fromjson.go │ ├── fromjson_test.go │ ├── spinnaker.go │ ├── terminator.go │ ├── terminator_test.go │ └── urls.go ├── term/ │ ├── term.go │ ├── term_ext_test.go │ ├── terminate_test.go │ └── terminator.go ├── tracker/ │ └── tracker.go └── update-docs.sh
SYMBOL INDEX (539 symbols across 74 files)
FILE: cal/cal.go
function IsWorkday (line 23) | func IsWorkday(t time.Time) bool {
function isWeekday (line 27) | func isWeekday(t time.Time) bool {
FILE: cal/cal_test.go
function TestIsWorkday (line 37) | func TestIsWorkday(t *testing.T) {
function parse (line 47) | func parse(s string) time.Time {
FILE: chaosmonkey.go
constant App (line 25) | App Group = iota
constant Stack (line 27) | Stack
constant Cluster (line 29) | Cluster
type AppConfig (line 35) | type AppConfig struct
type Group (line 49) | type Group
method String (line 167) | func (g Group) String() string {
type Exception (line 56) | type Exception struct
method Matches (line 195) | func (ex Exception) Matches(account, stack, detail, region string) bool {
type Instance (line 64) | type Instance interface
type Termination (line 91) | type Termination struct
type Tracker (line 98) | type Tracker interface
type ErrorCounter (line 104) | type ErrorCounter interface
type Decryptor (line 110) | type Decryptor interface
type Env (line 116) | type Env interface
type AppConfigGetter (line 122) | type AppConfigGetter interface
type Checker (line 135) | type Checker interface
type Terminator (line 144) | type Terminator interface
type Outage (line 152) | type Outage interface
type ErrViolatesMinTime (line 159) | type ErrViolatesMinTime struct
method Error (line 208) | func (e ErrViolatesMinTime) Error() string {
function NewAppConfig (line 182) | func NewAppConfig(exceptions []Exception) AppConfig {
function exFieldMatches (line 204) | func exFieldMatches(field, value string) bool {
FILE: chaosmonkey_test.go
function TestExceptionMatches (line 23) | func TestExceptionMatches(t *testing.T) {
FILE: clock/clock.go
type Clock (line 21) | type Clock interface
function New (line 27) | func New() Clock {
type SystemClock (line 32) | type SystemClock struct
method Now (line 35) | func (cl SystemClock) Now() time.Time {
FILE: cmd/chaosmonkey/main.go
function main (line 34) | func main() {
FILE: command/chaosmonkey.go
constant Version (line 41) | Version = "2.0.2"
function printVersion (line 43) | func printVersion() {
function Usage (line 54) | func Usage() {
function init (line 174) | func init() {
function Execute (line 180) | func Execute() {
function init (line 401) | func init() {
function logOnPanic (line 407) | func logOnPanic(errCounter chaosmonkey.ErrorCounter) {
function getConfig (line 418) | func getConfig() (*config.Monkey, error) {
type nullSchedStore (line 428) | type nullSchedStore struct
method Retrieve (line 431) | func (n nullSchedStore) Retrieve(date time.Time) (*schedule.Schedule, ...
method Publish (line 436) | func (n nullSchedStore) Publish(date time.Time, sched *schedule.Schedu...
FILE: command/dumpconfig.go
function DumpConfig (line 26) | func DumpConfig(c chaosmonkey.AppConfigGetter, app string) {
FILE: command/dumpmonkeyconfig.go
function DumpMonkeyConfig (line 24) | func DumpMonkeyConfig(cfg *config.Monkey) {
FILE: command/eligible.go
function Eligible (line 29) | func Eligible(g chaosmonkey.AppConfigGetter, d deploy.Deployment, app, a...
FILE: command/fetchschedule.go
function FetchSchedule (line 29) | func FetchSchedule(s schedstore.SchedStore, cfg *config.Monkey) {
function today (line 50) | func today(cfg *config.Monkey) time.Time {
FILE: command/install.go
constant scheduleCommand (line 27) | scheduleCommand = "schedule"
constant terminateCommand (line 28) | terminateCommand = "terminate"
constant scriptContent (line 29) | scriptContent = `#!/bin/bash
type CurrentExecutable (line 35) | type CurrentExecutable interface
function Install (line 41) | func Install(cfg *config.Monkey, exec CurrentExecutable, db mysql.MySQL) {
function InstallCron (line 48) | func InstallCron(cfg *config.Monkey, exec CurrentExecutable) {
function setupCron (line 66) | func setupCron(cfg *config.Monkey, executablePath string) error {
function setupTerminationScript (line 102) | func setupTerminationScript(cfg *config.Monkey, executablePath string) e...
function generateScriptContent (line 120) | func generateScriptContent(cmdName string, cfg *config.Monkey, executabl...
FILE: command/install_test.go
function assertHasSameContent (line 27) | func assertHasSameContent(fileName string, expectedContent string) error {
function initInstallationConfig (line 41) | func initInstallationConfig(script string, cron string, log string, term...
function TestInstallationWithDefaultCron (line 52) | func TestInstallationWithDefaultCron(t *testing.T) {
function TestInstallationWithUserDefinedCron (line 85) | func TestInstallationWithUserDefinedCron(t *testing.T) {
FILE: command/migrate.go
function Migrate (line 23) | func Migrate(db mysql.MySQL) {
FILE: command/osutil.go
type ChaosmonkeyExecutable (line 23) | type ChaosmonkeyExecutable struct
method ExecutablePath (line 27) | func (e ChaosmonkeyExecutable) ExecutablePath() (string, error) {
function EnsureFileAbsent (line 32) | func EnsureFileAbsent(path string) error {
FILE: command/outage.go
function Outage (line 25) | func Outage(ou chaosmonkey.Outage) {
FILE: command/regions.go
function DumpRegions (line 26) | func DumpRegions(cluster, account string, spin spinnaker.Spinnaker) {
FILE: command/schedule.go
function Schedule (line 33) | func Schedule(g chaosmonkey.AppConfigGetter, ss schedstore.SchedStore, c...
function do (line 59) | func do(d deploy.Deployment, g chaosmonkey.AppConfigGetter, ss schedstor...
function deploySchedule (line 80) | func deploySchedule(s *schedule.Schedule, ss schedstore.SchedStore, cfg ...
function registerWithCron (line 101) | func registerWithCron(s *schedule.Schedule, cfg *config.Monkey) error {
FILE: command/schedule_int_test.go
function TestScheduleCommand (line 33) | func TestScheduleCommand(t *testing.T) {
function countEntries (line 78) | func countEntries(buf []byte) int {
type mockAPI (line 83) | type mockAPI struct
method Publish (line 87) | func (a mockAPI) Publish(date time.Time, sched *schedule.Schedule) err...
method Retrieve (line 91) | func (a mockAPI) Retrieve(date time.Time) (*schedule.Schedule, error) {
method Get (line 96) | func (a mockAPI) Get(name string) (*chaosmonkey.AppConfig, error) {
method Check (line 103) | func (a mockAPI) Check(term chaosmonkey.Termination, appCfg *chaosmonk...
FILE: command/schedule_test.go
function addToSchedule (line 30) | func addToSchedule(t *testing.T, sched *schedule.Schedule, timeString st...
function newClusterGroup (line 39) | func newClusterGroup(app, account, cluster, region string) grp.InstanceG...
function TestRegisterWithCron (line 43) | func TestRegisterWithCron(t *testing.T) {
function TestCronOutputInSortedOrder (line 94) | func TestCronOutputInSortedOrder(t *testing.T) {
FILE: command/terminate.go
function Terminate (line 28) | func Terminate(d deps.Deps, app string, account string, region string, s...
FILE: config/config_test.go
function TestGetStringSlice (line 19) | func TestGetStringSlice(t *testing.T) {
FILE: config/monkey.go
type Monkey (line 35) | type Monkey struct
method setDefaults (line 47) | func (m *Monkey) setDefaults() {
method setupEnvVarReader (line 81) | func (m *Monkey) setupEnvVarReader() {
method configureRemote (line 153) | func (m *Monkey) configureRemote() error {
method SetRemoteProvider (line 173) | func (m *Monkey) SetRemoteProvider(provider string, endpoint string, p...
method Set (line 182) | func (m *Monkey) Set(key string, value interface{}) {
method readRemoteConfig (line 188) | func (m *Monkey) readRemoteConfig() error {
method Enabled (line 196) | func (m *Monkey) Enabled() (bool, error) {
method Leashed (line 203) | func (m *Monkey) Leashed() (bool, error) {
method ScheduleEnabled (line 209) | func (m *Monkey) ScheduleEnabled() (bool, error) {
method getDynamicBool (line 213) | func (m *Monkey) getDynamicBool(param string) (bool, error) {
method AccountEnabled (line 223) | func (m *Monkey) AccountEnabled(account string) (bool, error) {
method Accounts (line 239) | func (m *Monkey) Accounts() ([]string, error) {
method StartHour (line 264) | func (m *Monkey) StartHour() int { return m.v.GetInt(param.StartHour) }
method EndHour (line 270) | func (m *Monkey) EndHour() int {
method Location (line 276) | func (m *Monkey) Location() (*time.Location, error) {
method CronPath (line 282) | func (m *Monkey) CronPath() string {
method TermPath (line 288) | func (m *Monkey) TermPath() string {
method TermAccount (line 294) | func (m *Monkey) TermAccount() string {
method MaxApps (line 300) | func (m *Monkey) MaxApps() int {
method Trackers (line 306) | func (m *Monkey) Trackers() ([]string, error) {
method ErrorCounter (line 312) | func (m *Monkey) ErrorCounter() string {
method getStringSlice (line 316) | func (m *Monkey) getStringSlice(key string) ([]string, error) {
method SpinnakerEndpoint (line 340) | func (m *Monkey) SpinnakerEndpoint() string {
method SpinnakerCertificate (line 346) | func (m *Monkey) SpinnakerCertificate() string {
method SpinnakerEncryptedPassword (line 353) | func (m *Monkey) SpinnakerEncryptedPassword() string {
method SpinnakerUser (line 359) | func (m *Monkey) SpinnakerUser() string {
method SpinnakerX509Cert (line 364) | func (m *Monkey) SpinnakerX509Cert() string {
method SpinnakerX509Key (line 369) | func (m *Monkey) SpinnakerX509Key() string {
method Decryptor (line 374) | func (m *Monkey) Decryptor() string {
method OutageChecker (line 380) | func (m *Monkey) OutageChecker() string {
method DatabaseHost (line 385) | func (m *Monkey) DatabaseHost() string {
method DatabasePort (line 390) | func (m *Monkey) DatabasePort() int {
method DatabaseUser (line 395) | func (m *Monkey) DatabaseUser() string {
method DatabaseName (line 401) | func (m *Monkey) DatabaseName() string {
method DatabaseEncryptedPassword (line 407) | func (m *Monkey) DatabaseEncryptedPassword() string {
method BindPFlag (line 412) | func (m *Monkey) BindPFlag(parameter string, flag *pflag.Flag) (err er...
method CronExpression (line 452) | func (m *Monkey) CronExpression() (string, error) {
method ScheduleCronPath (line 489) | func (m *Monkey) ScheduleCronPath() string {
method SchedulePath (line 495) | func (m *Monkey) SchedulePath() string {
method LogPath (line 501) | func (m *Monkey) LogPath() string {
constant clockStartHour (line 41) | clockStartHour int = 0
constant clockEndHour (line 42) | clockEndHour int = 23
constant hoursInClock (line 43) | hoursInClock int = 24
constant cronBeforeStartHour (line 44) | cronBeforeStartHour int = 2
function Load (line 91) | func Load(configPaths []string) (*Monkey, error) {
function Defaults (line 124) | func Defaults() *Monkey {
function NewFromReader (line 133) | func NewFromReader(in io.Reader) (*Monkey, error) {
function toStrings (line 249) | func toStrings(values []interface{}) ([]string, error) {
type RemoteProvider (line 421) | type RemoteProvider
type RemoteConfigFactory (line 426) | type RemoteConfigFactory interface
type proxy (line 431) | type proxy struct
method Get (line 435) | func (p proxy) Get(rp viper.RemoteProvider) (io.Reader, error) {
method Watch (line 439) | func (p proxy) Watch(rp viper.RemoteProvider) (io.Reader, error) {
function SetRemoteProvider (line 444) | func SetRemoteProvider(name string, factory RemoteConfigFactory) {
function calculateDefaultCronRunHour (line 473) | func calculateDefaultCronRunHour(startHour int) (int, error) {
FILE: config/monkey_test.go
function TestDefaultCron (line 23) | func TestDefaultCron(t *testing.T) {
function TestDefaultCronForStartHourMidnight (line 39) | func TestDefaultCronForStartHourMidnight(t *testing.T) {
function TestDefaultCronForStartHourOneAM (line 55) | func TestDefaultCronForStartHourOneAM(t *testing.T) {
function TestDefaultCronForStartHourBeforeClockStart (line 71) | func TestDefaultCronForStartHourBeforeClockStart(t *testing.T) {
function TestDefaultCronForStartHourAfterClockEnd (line 82) | func TestDefaultCronForStartHourAfterClockEnd(t *testing.T) {
FILE: config/param/param.go
constant Enabled (line 19) | Enabled = "chaosmonkey.enabled"
constant Leashed (line 20) | Leashed = "chaosmonkey.leashed"
constant ScheduleEnabled (line 21) | ScheduleEnabled = "chaosmonkey.schedule_enabled"
constant Accounts (line 22) | Accounts = "chaosmonkey.accounts"
constant StartHour (line 23) | StartHour = "chaosmonkey.start_hour"
constant EndHour (line 24) | EndHour = "chaosmonkey.end_hour"
constant TimeZone (line 25) | TimeZone = "chaosmonkey.time_zone"
constant CronPath (line 26) | CronPath = "chaosmonkey.cron_path"
constant TermPath (line 27) | TermPath = "chaosmonkey.term_path"
constant TermAccount (line 28) | TermAccount = "chaosmonkey.term_account"
constant MaxApps (line 29) | MaxApps = "chaosmonkey.max_apps"
constant Trackers (line 30) | Trackers = "chaosmonkey.trackers"
constant ErrorCounter (line 31) | ErrorCounter = "chaosmonkey.error_counter"
constant Decryptor (line 32) | Decryptor = "chaosmonkey.decryptor"
constant OutageChecker (line 33) | OutageChecker = "chaosmonkey.outage_checker"
constant CronExpression (line 34) | CronExpression = "chaosmonkey.cron_expression"
constant ScheduleCronPath (line 35) | ScheduleCronPath = "chaosmonkey.schedule_cron_path"
constant SchedulePath (line 36) | SchedulePath = "chaosmonkey.schedule_path"
constant LogPath (line 37) | LogPath = "chaosmonkey.log_path"
constant SpinnakerEndpoint (line 40) | SpinnakerEndpoint = "spinnaker.endpoint"
constant SpinnakerCertificate (line 41) | SpinnakerCertificate = "spinnaker.certificate"
constant SpinnakerEncryptedPassword (line 42) | SpinnakerEncryptedPassword = "spinnaker.encrypted_password"
constant SpinnakerUser (line 43) | SpinnakerUser = "spinnaker.user"
constant SpinnakerX509Cert (line 44) | SpinnakerX509Cert = "spinnaker.x509_cert"
constant SpinnakerX509Key (line 45) | SpinnakerX509Key = "spinnaker.x509_key"
constant DatabaseHost (line 47) | DatabaseHost = "database.host"
constant DatabasePort (line 48) | DatabasePort = "database.port"
constant DatabaseUser (line 49) | DatabaseUser = "database.user"
constant DatabaseEncryptedPassword (line 50) | DatabaseEncryptedPassword = "database.encrypted_password"
constant DatabaseName (line 51) | DatabaseName = "database.name"
constant DynamicProvider (line 54) | DynamicProvider = "dynamic.provider"
constant DynamicEndpoint (line 55) | DynamicEndpoint = "dynamic.endpoint"
constant DynamicPath (line 56) | DynamicPath = "dynamic.path"
FILE: constrainer/constrainer.go
type NullConstrainer (line 24) | type NullConstrainer struct
method Filter (line 32) | func (n NullConstrainer) Filter(s schedule.Schedule) schedule.Schedule {
function init (line 26) | func init() {
function getNullConstrainer (line 36) | func getNullConstrainer(cfg *config.Monkey) (schedule.Constrainer, error) {
FILE: decryptor/decryptor.go
type nullDecryptor (line 24) | type nullDecryptor struct
method Decrypt (line 28) | func (n nullDecryptor) Decrypt(ciphertext string) (string, error) {
function init (line 32) | func init() {
function getNullDecryptor (line 36) | func getNullDecryptor(cfg *config.Monkey) (chaosmonkey.Decryptor, error) {
FILE: deploy/app.go
type App (line 18) | type App struct
method Name (line 24) | func (a App) Name() string {
method Accounts (line 29) | func (a App) Accounts() []*Account {
type AppName (line 35) | type AppName
type AccountName (line 38) | type AccountName
type ClusterName (line 41) | type ClusterName
type StackName (line 44) | type StackName
type RegionName (line 47) | type RegionName
type ASGName (line 50) | type ASGName
type InstanceID (line 53) | type InstanceID
type CloudProvider (line 56) | type CloudProvider
type ClusterMap (line 60) | type ClusterMap
type AccountInfo (line 63) | type AccountInfo struct
type AppMap (line 69) | type AppMap
function NewApp (line 73) | func NewApp(name string, data AppMap) *App {
FILE: deploy/asg.go
type ASG (line 20) | type ASG struct
method Instances (line 44) | func (a *ASG) Instances() []*Instance {
method Empty (line 49) | func (a *ASG) Empty() bool {
method AppName (line 54) | func (a *ASG) AppName() string {
method AccountName (line 59) | func (a *ASG) AccountName() string {
method ClusterName (line 64) | func (a *ASG) ClusterName() string {
method DetailName (line 69) | func (a *ASG) DetailName() string {
method missingPushNumber (line 96) | func (a *ASG) missingPushNumber() bool {
method RegionName (line 101) | func (a *ASG) RegionName() string {
method Name (line 106) | func (a *ASG) Name() string {
method StackName (line 111) | func (a *ASG) StackName() string {
method CloudProvider (line 116) | func (a *ASG) CloudProvider() string {
function NewASG (line 28) | func NewASG(name, region string, instanceIDs []string, cluster *Cluster)...
FILE: deploy/deploy_test.go
function TestASGAndClusters (line 23) | func TestASGAndClusters(t *testing.T) {
FILE: deploy/deployment.go
type Deployment (line 27) | type Deployment interface
type Account (line 52) | type Account struct
method Name (line 60) | func (a *Account) Name() string {
method Clusters (line 65) | func (a *Account) Clusters() []*Cluster {
method AppName (line 70) | func (a *Account) AppName() string {
method RegionNames (line 76) | func (a *Account) RegionNames() []string {
method CloudProvider (line 95) | func (a *Account) CloudProvider() string {
method StackNames (line 115) | func (a *Account) StackNames() []string {
type stringSet (line 99) | type stringSet
method add (line 101) | func (s *stringSet) add(val string) {
method slice (line 106) | func (s stringSet) slice() []string {
type Cluster (line 129) | type Cluster struct
method Name (line 136) | func (c *Cluster) Name() string {
method AppName (line 141) | func (c *Cluster) AppName() string {
method StackName (line 146) | func (c *Cluster) StackName() string {
method AccountName (line 155) | func (c *Cluster) AccountName() string {
method ASGs (line 160) | func (c *Cluster) ASGs() []*ASG {
method RegionNames (line 165) | func (c *Cluster) RegionNames() []string {
method CloudProvider (line 180) | func (c *Cluster) CloudProvider() string {
type Instance (line 185) | type Instance struct
method String (line 193) | func (i *Instance) String() string {
method AppName (line 199) | func (i *Instance) AppName() string {
method AccountName (line 204) | func (i *Instance) AccountName() string {
method ClusterName (line 209) | func (i *Instance) ClusterName() string {
method RegionName (line 214) | func (i *Instance) RegionName() string {
method ASGName (line 219) | func (i *Instance) ASGName() string {
method StackName (line 224) | func (i *Instance) StackName() string {
method CloudProvider (line 229) | func (i *Instance) CloudProvider() string {
method ID (line 234) | func (i *Instance) ID() string {
FILE: deploy/eligible_instance_groups.go
method EligibleInstanceGroups (line 41) | func (app *App) EligibleInstanceGroups(cfg chaosmonkey.AppConfig) []grp....
function appIndep (line 68) | func appIndep(app *App) []grp.InstanceGroup {
function appDep (line 79) | func appDep(app *App) []grp.InstanceGroup {
function stackIndep (line 88) | func stackIndep(app *App) []grp.InstanceGroup {
function stackDep (line 116) | func stackDep(app *App) []grp.InstanceGroup {
function clusterIndep (line 128) | func clusterIndep(app *App) []grp.InstanceGroup {
function clusterDep (line 142) | func clusterDep(app *App) []grp.InstanceGroup {
FILE: deploy/eligible_instance_groups_test.go
type groupList (line 25) | type groupList
method toSet (line 122) | func (gl groupList) toSet() groupSet {
function TestEligibleInstanceGroups (line 87) | func TestEligibleInstanceGroups(t *testing.T) {
function conf (line 106) | func conf(grouping chaosmonkey.Group, regionsAreIndependent bool) chaosm...
type groupSet (line 116) | type groupSet
method add (line 118) | func (gs *groupSet) add(group grp.InstanceGroup) {
function same (line 132) | func same(x, y groupList) bool {
FILE: deps/deps.go
type Deps (line 49) | type Deps struct
FILE: eligible/eligible.go
type cluster (line 31) | type cluster struct
type instance (line 39) | type instance struct
method AppName (line 51) | func (i instance) AppName() string {
method AccountName (line 55) | func (i instance) AccountName() string {
method RegionName (line 59) | func (i instance) RegionName() string {
method StackName (line 63) | func (i instance) StackName() string {
method ClusterName (line 67) | func (i instance) ClusterName() string {
method ASGName (line 71) | func (i instance) ASGName() string {
method Name (line 75) | func (i instance) Name() string {
method ID (line 79) | func (i instance) ID() string {
method CloudProvider (line 83) | func (i instance) CloudProvider() string {
function isException (line 87) | func isException(exs []chaosmonkey.Exception, account deploy.AccountName...
function isNeverEligible (line 97) | func isNeverEligible(cluster deploy.ClusterName) bool {
function clusters (line 106) | func clusters(group grp.InstanceGroup, cloudProvider deploy.CloudProvide...
function regions (line 151) | func regions(group grp.InstanceGroup, deployedRegions []deploy.RegionNam...
function regionsWhenTermScopedtoSingleRegion (line 161) | func regionsWhenTermScopedtoSingleRegion(region string, deployedRegions ...
function contains (line 169) | func contains(region string, regions []deploy.RegionName) bool {
constant whiteListErrorMessage (line 178) | whiteListErrorMessage = "whitelist is not supported"
function isWhitelist (line 181) | func isWhitelist(err error) bool {
function Instances (line 186) | func Instances(group grp.InstanceGroup, exs []chaosmonkey.Exception, dep...
function getInstances (line 211) | func getInstances(cl cluster, dep deploy.Deployment) ([]chaosmonkey.Inst...
FILE: eligible/eligible_test.go
function mockDeployment (line 12) | func mockDeployment() D.Deployment {
function ids (line 35) | func ids(instances []chaosmonkey.Instance) []string {
function TestGroupings (line 46) | func TestGroupings(t *testing.T) {
function TestAppLevelGroupingWhereClustersAreRegionSpecific (line 86) | func TestAppLevelGroupingWhereClustersAreRegionSpecific(t *testing.T) {
function TestAppLevelGroupingWhereClusterIsInTwoRegions (line 110) | func TestAppLevelGroupingWhereClusterIsInTwoRegions(t *testing.T) {
function TestExceptions (line 131) | func TestExceptions(t *testing.T) {
FILE: eligible/instances_canary_test.go
function TestNoKillCanaries (line 26) | func TestNoKillCanaries(t *testing.T) {
FILE: eligible/instances_test.go
function mockDep (line 27) | func mockDep() D.Deployment {
function TestInstances (line 110) | func TestInstances(t *testing.T) {
function TestSimpleException (line 128) | func TestSimpleException(t *testing.T) {
function TestMultipleExceptions (line 142) | func TestMultipleExceptions(t *testing.T) {
function abcloudMockDep (line 170) | func abcloudMockDep() D.Deployment {
FILE: env/env.go
type notTestEnv (line 26) | type notTestEnv struct
method InTest (line 29) | func (n notTestEnv) InTest() bool {
function init (line 33) | func init() {
function getNotTestEnv (line 37) | func getNotTestEnv(cfg *config.Monkey) (chaosmonkey.Env, error) {
FILE: errorcounter/errorcounter.go
type nullErrorCounter (line 28) | type nullErrorCounter struct
method Increment (line 30) | func (n nullErrorCounter) Increment() error {
function init (line 34) | func init() {
function getNullErrorCounter (line 38) | func getNullErrorCounter(cfg *config.Monkey) (chaosmonkey.ErrorCounter, ...
FILE: grp/grp.go
function New (line 31) | func New(app, account, region, stack, cluster string) InstanceGroup {
type InstanceGroup (line 42) | type InstanceGroup interface
function Equal (line 66) | func Equal(g1, g2 InstanceGroup) bool {
function String (line 115) | func String(group InstanceGroup) string {
type group (line 143) | type group struct
method String (line 147) | func (g group) String() string {
method MarshalJSON (line 151) | func (g group) MarshalJSON() ([]byte, error) {
method App (line 170) | func (g group) App() string {
method Account (line 175) | func (g group) Account() string {
method Region (line 180) | func (g group) Region() (string, bool) {
method Stack (line 188) | func (g group) Stack() (string, bool) {
method Cluster (line 196) | func (g group) Cluster() (string, bool) {
function AnyRegion (line 204) | func AnyRegion(g InstanceGroup) bool {
function AnyStack (line 210) | func AnyStack(g InstanceGroup) bool {
function AnyCluster (line 216) | func AnyCluster(g InstanceGroup) bool {
function Contains (line 222) | func Contains(g InstanceGroup, account, region, cluster string) bool {
function must (line 238) | func must(val string, specific bool) string {
FILE: grp/grp_test.go
function TestNewAppWithRegion (line 23) | func TestNewAppWithRegion(t *testing.T) {
function TestNewAppCrossRegion (line 48) | func TestNewAppCrossRegion(t *testing.T) {
function TestNewStackWithRegion (line 72) | func TestNewStackWithRegion(t *testing.T) {
function TestNewStackCrossRegion (line 98) | func TestNewStackCrossRegion(t *testing.T) {
function TestNewClusterWithRegion (line 123) | func TestNewClusterWithRegion(t *testing.T) {
function TestNewClusterCrossRegion (line 149) | func TestNewClusterCrossRegion(t *testing.T) {
function TestContains (line 174) | func TestContains(t *testing.T) {
function TestEqual (line 197) | func TestEqual(t *testing.T) {
FILE: migration/migrations.go
function bindataRead (line 20) | func bindataRead(data []byte, name string) ([]byte, error) {
type asset (line 40) | type asset struct
type bindataFileInfo (line 45) | type bindataFileInfo struct
method Name (line 52) | func (fi bindataFileInfo) Name() string {
method Size (line 55) | func (fi bindataFileInfo) Size() int64 {
method Mode (line 58) | func (fi bindataFileInfo) Mode() os.FileMode {
method ModTime (line 61) | func (fi bindataFileInfo) ModTime() time.Time {
method IsDir (line 64) | func (fi bindataFileInfo) IsDir() bool {
method Sys (line 67) | func (fi bindataFileInfo) Sys() interface{} {
function migrationMysql100_initial_schemaSqlBytes (line 73) | func migrationMysql100_initial_schemaSqlBytes() ([]byte, error) {
function migrationMysql100_initial_schemaSql (line 80) | func migrationMysql100_initial_schemaSql() (*asset, error) {
function Asset (line 94) | func Asset(name string) ([]byte, error) {
function MustAsset (line 108) | func MustAsset(name string) []byte {
function AssetInfo (line 120) | func AssetInfo(name string) (os.FileInfo, error) {
function AssetNames (line 133) | func AssetNames() []string {
function AssetDir (line 159) | func AssetDir(name string) ([]string, error) {
type bintree (line 181) | type bintree struct
function RestoreAsset (line 195) | func RestoreAsset(dir, name string) error {
function RestoreAssets (line 220) | func RestoreAssets(dir, name string) error {
function _filePath (line 236) | func _filePath(dir, name string) string {
FILE: migration/mysql/1.0.0_initial_schema.sql
type schedules (line 3) | CREATE TABLE IF NOT EXISTS schedules (
type terminations (line 16) | CREATE TABLE IF NOT EXISTS terminations (
FILE: mock/configgetter.go
type ConfigGetter (line 20) | type ConfigGetter struct
method Get (line 44) | func (c ConfigGetter) Get(app string) (*chaosmonkey.AppConfig, error) {
function NewConfigGetter (line 25) | func NewConfigGetter(config chaosmonkey.AppConfig) ConfigGetter {
function DefaultConfigGetter (line 30) | func DefaultConfigGetter() ConfigGetter {
FILE: mock/deployment.go
constant cloudProvider (line 23) | cloudProvider = "aws"
function Dep (line 35) | func Dep() D.Deployment {
function NewDeployment (line 57) | func NewDeployment(apps map[string]D.AppMap) D.Deployment {
type Deployment (line 62) | type Deployment struct
method Apps (line 67) | func (d Deployment) Apps(c chan<- *D.App, apps []string) {
method GetClusterNames (line 76) | func (d Deployment) GetClusterNames(app string, account D.AccountName)...
method GetRegionNames (line 86) | func (d Deployment) GetRegionNames(app string, account D.AccountName, ...
method AppNames (line 96) | func (d Deployment) AppNames() ([]string, error) {
method GetApp (line 108) | func (d Deployment) GetApp(name string) (*D.App, error) {
method CloudProvider (line 113) | func (d Deployment) CloudProvider(account string) (string, error) {
method GetInstanceIDs (line 118) | func (d Deployment) GetInstanceIDs(app string, account D.AccountName, ...
FILE: mock/deps.go
type Checker (line 30) | type Checker struct
method Check (line 54) | func (c Checker) Check(term chaosmonkey.Termination, appCfg chaosmonke...
type Tracker (line 35) | type Tracker struct
method Track (line 60) | func (t Tracker) Track(trm chaosmonkey.Termination) error {
type ErrorCounter (line 40) | type ErrorCounter struct
method Increment (line 65) | func (e ErrorCounter) Increment() error {
type Clock (line 43) | type Clock struct
method Now (line 70) | func (c Clock) Now() time.Time {
type Env (line 48) | type Env struct
method InTest (line 75) | func (e Env) InTest() bool {
function Deps (line 81) | func Deps() deps.Deps {
FILE: mock/install.go
type Executable (line 18) | type Executable struct
method ExecutablePath (line 24) | func (m Executable) ExecutablePath() (string, error) {
FILE: mock/instance.go
type Instance (line 18) | type Instance struct
method AppName (line 23) | func (i Instance) AppName() string {
method AccountName (line 28) | func (i Instance) AccountName() string {
method RegionName (line 33) | func (i Instance) RegionName() string {
method StackName (line 38) | func (i Instance) StackName() string {
method ClusterName (line 43) | func (i Instance) ClusterName() string {
method ASGName (line 48) | func (i Instance) ASGName() string {
method ID (line 53) | func (i Instance) ID() string {
method CloudProvider (line 58) | func (i Instance) CloudProvider() string {
FILE: mock/mock.go
type AppFactory (line 22) | type AppFactory struct
method App (line 26) | func (factory AppFactory) App() *D.App {
FILE: mock/outage.go
type Outage (line 18) | type Outage struct
method Outage (line 21) | func (o Outage) Outage() (bool, error) {
FILE: mock/terminator.go
type Terminator (line 20) | type Terminator struct
method Execute (line 27) | func (t *Terminator) Execute(trm chaosmonkey.Termination) error {
FILE: mysql/checker_test.go
function testSetup (line 36) | func testSetup(t *testing.T) (ins c.Instance, loc *time.Location, appCfg...
function TestCheckPermitted (line 67) | func TestCheckPermitted(t *testing.T) {
function TestCheckForbidden (line 90) | func TestCheckForbidden(t *testing.T) {
function TestCheckLeashed (line 125) | func TestCheckLeashed(t *testing.T) {
function TestConcurrentChecks (line 158) | func TestConcurrentChecks(t *testing.T) {
function TestCombinations (line 209) | func TestCombinations(t *testing.T) {
function TestCheckMinTimeEnforced (line 288) | func TestCheckMinTimeEnforced(t *testing.T) {
FILE: mysql/mysql.go
type MySQL (line 40) | type MySQL struct
method Close (line 96) | func (m MySQL) Close() error {
method Retrieve (line 110) | func (m MySQL) Retrieve(date time.Time) (sched *schedule.Schedule, err...
method Publish (line 146) | func (m MySQL) Publish(date time.Time, sched *schedule.Schedule) error {
method PublishWithDelay (line 152) | func (m MySQL) PublishWithDelay(date time.Time, sched *schedule.Schedu...
method Check (line 264) | func (m MySQL) Check(term chaosmonkey.Termination, appCfg chaosmonkey....
method CheckWithDelay (line 270) | func (m MySQL) CheckWithDelay(term chaosmonkey.Termination, appCfg cha...
function TxDeadlock (line 45) | func TxDeadlock(err error) bool {
function ViolatesMinTime (line 58) | func ViolatesMinTime(err error) bool {
function NewFromConfig (line 64) | func NewFromConfig(cfg *config.Monkey) (MySQL, error) {
function New (line 86) | func New(host string, port int, user string, password string, dbname str...
function utcDate (line 104) | func utcDate(date time.Time) time.Time {
function schedExists (line 216) | func schedExists(tx *sql.Tx, date time.Time) (result bool, err error) {
function dsn (line 243) | func dsn(host string, port int, user string, password string, dbname str...
function respectsMinTimeBetweenKills (line 303) | func respectsMinTimeBetweenKills(tx *sql.Tx, now time.Time, term chaosmo...
function noKillsSince (line 402) | func noKillsSince(days int, now time.Time, endHour int, loc *time.Locati...
function recordTermination (line 430) | func recordTermination(tx *sql.Tx, term chaosmonkey.Termination, loc *ti...
function Migrate (line 449) | func Migrate(mysqlDb MySQL) error {
FILE: mysql/mysql_test.go
function inUse (line 52) | func inUse(port int) bool {
function TestMain (line 62) | func TestMain(m *testing.M) {
function startMySQLContainer (line 109) | func startMySQLContainer() (*exec.Cmd, error) {
function initDB (line 159) | func initDB() error {
function stopMySQLContainer (line 193) | func stopMySQLContainer(name string, t *testing.T) {
FILE: mysql/no_kills_since_test.go
function TestZeroDaysBetweenKills (line 39) | func TestZeroDaysBetweenKills(t *testing.T) {
function parse (line 155) | func parse(s string) time.Time {
function format (line 164) | func format(tm time.Time) string {
FILE: mysql/schedstore_test.go
function TestPublishRetrieve (line 37) | func TestPublishRetrieve(t *testing.T) {
function NewMySQL (line 82) | func NewMySQL() (mysql.MySQL, error) {
function TestPublishRetrieveMultipleEntries (line 86) | func TestPublishRetrieveMultipleEntries(t *testing.T) {
function TestScheduleAlreadyExists (line 138) | func TestScheduleAlreadyExists(t *testing.T) {
function TestScheduleAlreadyExistsConcurrency (line 186) | func TestScheduleAlreadyExistsConcurrency(t *testing.T) {
function TestOnlyReturnsFromDayRequested (line 256) | func TestOnlyReturnsFromDayRequested(t *testing.T) {
function TestNoScheduleRetrievedOnWrongDay (line 320) | func TestNoScheduleRetrievedOnWrongDay(t *testing.T) {
function TestPublishDateDifferentTimes (line 364) | func TestPublishDateDifferentTimes(t *testing.T) {
FILE: outage/outage.go
type NullOutage (line 26) | type NullOutage struct
method Outage (line 29) | func (n NullOutage) Outage() (bool, error) {
function init (line 33) | func init() {
function GetOutage (line 38) | func GetOutage(cfg *config.Monkey) (chaosmonkey.Outage, error) {
FILE: schedstore/schedstore.go
type SchedStore (line 29) | type SchedStore interface
FILE: schedule/constrainer.go
type Constrainer (line 18) | type Constrainer interface
FILE: schedule/schedule.go
function doScheduleApp (line 81) | func doScheduleApp(schedule *Schedule, app *deploy.App, cfg chaosmonkey....
function chooseTerminationTime (line 122) | func chooseTerminationTime(now time.Time, startHour int, endHour int, lo...
type float64Rand (line 144) | type float64Rand interface
function shouldKillInstance (line 155) | func shouldKillInstance(meanTimeBetweenKillsInWorkDays int, r float64Ran...
type Entry (line 173) | type Entry struct
method UnmarshalJSON (line 184) | func (e *Entry) UnmarshalJSON(b []byte) (err error) {
method Equal (line 204) | func (e *Entry) Equal(o *Entry) bool {
method Crontab (line 214) | func (e *Entry) Crontab(termPath, account string) string {
type apiGroup (line 179) | type apiGroup struct
function terminateCommand (line 230) | func terminateCommand(termPath string, group grp.InstanceGroup) string {
function logRedirect (line 250) | func logRedirect(logPath string) string {
type Schedule (line 255) | type Schedule struct
method Populate (line 36) | func (s *Schedule) Populate(d deploy.Deployment, getter chaosmonkey.Ap...
method Add (line 71) | func (s *Schedule) Add(tm time.Time, group grp.InstanceGroup) {
method Entries (line 76) | func (s *Schedule) Entries() []Entry {
method Crontab (line 279) | func (s Schedule) Crontab(exPath string, account string) []byte {
method MarshalJSON (line 300) | func (s Schedule) MarshalJSON() ([]byte, error) {
method UnmarshalJSON (line 305) | func (s *Schedule) UnmarshalJSON(b []byte) (err error) {
function New (line 260) | func New() *Schedule {
type ByTime (line 269) | type ByTime
method Len (line 271) | func (t ByTime) Len() int { return len(t) }
method Swap (line 272) | func (t ByTime) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
method Less (line 273) | func (t ByTime) Less(i, j int) bool { return t[i].Time.Before(t[j].Tim...
FILE: schedule/schedule_test.go
function TestPopulate (line 28) | func TestPopulate(t *testing.T) {
type mockConfigGetter (line 61) | type mockConfigGetter struct
method Get (line 68) | func (g mockConfigGetter) Get(app string) (*chaosmonkey.AppConfig, err...
function countEntries (line 76) | func countEntries(buf []byte) int {
FILE: spinnaker/config.go
method Get (line 27) | func (s Spinnaker) Get(app string) (c *chaosmonkey.AppConfig, err error) {
FILE: spinnaker/fromjson.go
function fromJSON (line 93) | func fromJSON(js []byte) (*chaosmonkey.AppConfig, error) {
type parsedJSON (line 182) | type parsedJSON struct
type parsedAttr (line 187) | type parsedAttr struct
type parsedChaosMonkey (line 191) | type parsedChaosMonkey struct
FILE: spinnaker/fromjson_test.go
function TestFromJSON (line 23) | func TestFromJSON(t *testing.T) {
function TestFromJSONDisabled (line 115) | func TestFromJSONDisabled(t *testing.T) {
function TestBadJSON (line 136) | func TestBadJSON(t *testing.T) {
function TestFromJSONEmptyWhitelist (line 177) | func TestFromJSONEmptyWhitelist(t *testing.T) {
function TestFromJSONPopulatedWhitelist (line 222) | func TestFromJSONPopulatedWhitelist(t *testing.T) {
FILE: spinnaker/spinnaker.go
type Spinnaker (line 41) | type Spinnaker struct
method AccountID (line 171) | func (s Spinnaker) AccountID(name string) (id string, err error) {
method alternateAccountID (line 219) | func (s Spinnaker) alternateAccountID(name string) (string, error) {
method Apps (line 237) | func (s Spinnaker) Apps(c chan<- *D.App, appNames []string) {
method GetInstanceIDs (line 254) | func (s Spinnaker) GetInstanceIDs(app string, account D.AccountName, c...
method GetApp (line 298) | func (s Spinnaker) GetApp(appName string) (*D.App, error) {
method AppNames (line 346) | func (s Spinnaker) AppNames() (appnames []string, err error) {
method clusters (line 384) | func (s Spinnaker) clusters(appName string) spinnakerClusters {
method asgs (line 432) | func (s Spinnaker) asgs(appName, account, clusterName string) (result ...
method CloudProvider (line 483) | func (s Spinnaker) CloudProvider(name string) (provider string, err er...
method account (line 504) | func (s Spinnaker) account(name string) (account, error) {
method GetClusterNames (line 552) | func (s Spinnaker) GetClusterNames(app string, account D.AccountName) ...
method GetRegionNames (line 596) | func (s Spinnaker) GetRegionNames(app string, account D.AccountName, c...
type spinnakerClusters (line 49) | type spinnakerClusters
type spinnakerServerGroup (line 53) | type spinnakerServerGroup struct
type spinnakerInstance (line 61) | type spinnakerInstance struct
function getClient (line 67) | func getClient(pfxData []byte, password string) (*http.Client, error) {
function getClientX509 (line 91) | func getClientX509(x509Cert, x509Key string) (*http.Client, error) {
function NewFromConfig (line 105) | func NewFromConfig(cfg *config.Monkey) (Spinnaker, error) {
function New (line 140) | func New(endpoint string, certPath string, password string, x509Cert str...
type spinnakerApp (line 379) | type spinnakerApp struct
type account (line 497) | type account struct
FILE: spinnaker/terminator.go
constant terminateType (line 30) | terminateType string = "terminateInstances"
type killPayload (line 34) | type killPayload struct
type kpJob (line 41) | type kpJob struct
type fakeTerminator (line 53) | type fakeTerminator struct
method Execute (line 67) | func (t fakeTerminator) Execute(trm chaosmonkey.Termination) error {
function NewFakeTerm (line 57) | func NewFakeTerm() chaosmonkey.Terminator {
method tasksURL (line 62) | func (s Spinnaker) tasksURL(appName string) string {
method Execute (line 72) | func (s Spinnaker) Execute(trm chaosmonkey.Termination) (err error) {
function killJSONPayload (line 108) | func killJSONPayload(ins chaosmonkey.Instance, otherID string, spinnaker...
method OtherID (line 143) | func (s Spinnaker) OtherID(ins chaosmonkey.Instance) (otherID string, er...
FILE: spinnaker/terminator_test.go
function TestKillJSONPayload (line 24) | func TestKillJSONPayload(t *testing.T) {
function TestKillJSONPayloadWithOtherID (line 139) | func TestKillJSONPayloadWithOtherID(t *testing.T) {
FILE: spinnaker/urls.go
method appsURL (line 20) | func (s Spinnaker) appsURL() string {
method appURL (line 25) | func (s Spinnaker) appURL(appName string) string {
method clustersURL (line 30) | func (s Spinnaker) clustersURL(appName string) string {
method clusterURL (line 35) | func (s Spinnaker) clusterURL(appName string, account string, clusterNam...
method serverGroupsURL (line 40) | func (s Spinnaker) serverGroupsURL(appName, account, clusterName string)...
method accountURL (line 45) | func (s Spinnaker) accountURL(account string) string {
method accountsURL (line 50) | func (s Spinnaker) accountsURL(expanded bool) string {
method instanceURL (line 59) | func (s Spinnaker) instanceURL(account string, region string, id string)...
method activeASGURL (line 64) | func (s Spinnaker) activeASGURL(appName, account, clusterName, cloudProv...
FILE: term/term.go
type leashedKiller (line 32) | type leashedKiller struct
method Execute (line 35) | func (l leashedKiller) Execute(trm chaosmonkey.Termination) error {
type UnleashedInTestEnv (line 42) | type UnleashedInTestEnv struct
method Error (line 44) | func (err UnleashedInTestEnv) Error() string {
function Terminate (line 52) | func Terminate(d deps.Deps, app string, account string, region string, s...
function doTerminate (line 95) | func doTerminate(d deps.Deps, group grp.InstanceGroup) error {
function PickRandomInstance (line 184) | func PickRandomInstance(group grp.InstanceGroup, cfg chaosmonkey.AppConf...
FILE: term/term_ext_test.go
function TestEnabledAccounts (line 27) | func TestEnabledAccounts(t *testing.T) {
FILE: term/terminate_test.go
function mockDeps (line 30) | func mockDeps() deps.Deps {
function TestTerminateKills (line 46) | func TestTerminateKills(t *testing.T) {
function TestTerminateOnlyKillsInProd (line 81) | func TestTerminateOnlyKillsInProd(t *testing.T) {
function TestTerminateDoesntKillIfRecorderFails (line 97) | func TestTerminateDoesntKillIfRecorderFails(t *testing.T) {
function TestTerminateDoesntKillInLeashedMode (line 114) | func TestTerminateDoesntKillInLeashedMode(t *testing.T) {
function TestNeverTerminateUnleashedInTestEnv (line 139) | func TestNeverTerminateUnleashedInTestEnv(t *testing.T) {
function TestDoesNotTerminateIfTrackerFails (line 157) | func TestDoesNotTerminateIfTrackerFails(t *testing.T) {
function TestDoesNotTerminateIfAppIsDisabled (line 177) | func TestDoesNotTerminateIfAppIsDisabled(t *testing.T) {
FILE: term/terminator.go
type fake (line 24) | type fake struct
method Execute (line 32) | func (t fake) Execute(trm chaosmonkey.Termination) error {
function Fake (line 27) | func Fake() chaosmonkey.Terminator {
FILE: tracker/tracker.go
function init (line 25) | func init() {
function getTrackers (line 30) | func getTrackers(cfg *config.Monkey) ([]chaosmonkey.Tracker, error) {
function getTracker (line 50) | func getTracker(kind string, cfg *config.Monkey) (chaosmonkey.Tracker, e...
Condensed preview — 102 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (449K chars).
[
{
"path": ".gitignore",
"chars": 20,
"preview": "coverage.out\n.idea/\n"
},
{
"path": ".travis.yml",
"chars": 1163,
"preview": "# sudo is required for docker\nsudo: required\n\nlanguage: go\n\ngo:\n - \"1.20.x\"\n\nenv:\n - DEPLOY_DOCS=\"$(if [[ $TRAVIS_BRA"
},
{
"path": "LICENSE",
"chars": 11344,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 816,
"preview": ".PHONY: check fmt lint errcheck test build\n\nSHELL:=/bin/bash\n\nbuild: check\n\tgo build github.com/Netflix/chaosmonkey/cmd/"
},
{
"path": "NOTICE",
"chars": 68901,
"preview": "Chaos Monkey randomly terminates instances.\nCopyright (C) 2016 Netflix, Inc.\n\nChaos Monkey makes use of several third-pa"
},
{
"path": "OSSMETADATA",
"chars": 20,
"preview": "osslifecycle=active\n"
},
{
"path": "README.md",
"chars": 2053,
"preview": "\n\n[](OS"
},
{
"path": "cal/cal.go",
"chars": 1130,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "cal/cal_test.go",
"chars": 1489,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "chaosmonkey.go",
"chars": 6954,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "chaosmonkey_test.go",
"chars": 908,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "clock/clock.go",
"chars": 1127,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "cmd/chaosmonkey/main.go",
"chars": 1143,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/chaosmonkey.go",
"chars": 11103,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/command.go",
"chars": 738,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/dumpconfig.go",
"chars": 920,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/dumpmonkeyconfig.go",
"chars": 1826,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/eligible.go",
"chars": 1390,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/fetchschedule.go",
"chars": 1680,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/install.go",
"chars": 3454,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/install_test.go",
"chars": 3474,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/migrate.go",
"chars": 912,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/osutil.go",
"chars": 1248,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/outage.go",
"chars": 890,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/regions.go",
"chars": 1223,
"preview": "// Copyright 2017 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/schedule.go",
"chars": 3260,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/schedule_int_test.go",
"chars": 2750,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/schedule_test.go",
"chars": 4008,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "command/terminate.go",
"chars": 1262,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "config/config.go",
"chars": 658,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "config/config_test.go",
"chars": 1128,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "config/monkey.go",
"chars": 15014,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "config/monkey_test.go",
"chars": 2270,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "config/param/param.go",
"chars": 2220,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "constrainer/constrainer.go",
"chars": 1166,
"preview": "// Copyright 2017 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "decryptor/decryptor.go",
"chars": 1284,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/app.go",
"chars": 2820,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/asg.go",
"chars": 3077,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/deploy_test.go",
"chars": 4656,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/deployment.go",
"chars": 6403,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/eligible_instance_groups.go",
"chars": 4552,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deploy/eligible_instance_groups_test.go",
"chars": 6607,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "deps/deps.go",
"chars": 2076,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "docs/Configuration-file-format.md",
"chars": 3414,
"preview": "The config file is in [TOML] format.\n\nChaos Monkey will look for a file named `chaosmonkey.toml` in the following\nlocati"
},
{
"path": "docs/Configuring-behavior-via-Spinnaker.md",
"chars": 2682,
"preview": "Through the Spinnaker web UI, you can configure how often Chaos Monkey\nterminates instances for each application.\n\nClick"
},
{
"path": "docs/How-to-deploy.md",
"chars": 9052,
"preview": "We currently don't have a streamlined process for deploying Chaos Monkey. This\npage describes the manual steps required "
},
{
"path": "docs/Running-locally.md",
"chars": 333,
"preview": "*Note: this doc is in progress*\n\nTo run locally, you need a local MySQL and a local Spinnaker. This page\ndescribes how t"
},
{
"path": "docs/Termination-behavior.md",
"chars": 2078,
"preview": "## Enabled group\n\nChaos Monkey will only consider server groups eligible for termination if they\nare marked as enabled b"
},
{
"path": "docs/dev/Running-tests.md",
"chars": 1552,
"preview": "To run unit tests:\n\n```bash\ngo test ./...\n```\n\n## Tests that interact with MySQL\n\nThere are some tests that interact wit"
},
{
"path": "docs/dev/Vendoring-dependencies.md",
"chars": 320,
"preview": "If you wish to add a new dependency to Chaos Monkey, use [govendor][1] to add it.\n\nPlease ensure that the license of the"
},
{
"path": "docs/index.md",
"chars": 464,
"preview": "\n\nChaos Monkey is responsible for randomly terminating instances in production to ensure that engineers"
},
{
"path": "docs/plugins/Constrainer.md",
"chars": 1124,
"preview": "# Constrainer\n\nThere may be some cases where you want to prevent some combination of Chaos\nMonkey terminations, but the "
},
{
"path": "docs/plugins/Decryptor.md",
"chars": 899,
"preview": "A decryptor allows you to use encrypted versions of the passwords for the MySQL\ndatabase and Spinnaker p12 certificate ("
},
{
"path": "docs/plugins/Error-counter.md",
"chars": 1022,
"preview": "An error counter is used to record the rate of errors generated by Chaos Monkey\nto an external system such as a metrics "
},
{
"path": "docs/plugins/Outage-checker.md",
"chars": 591,
"preview": "An outage checker is used to automatially disable Chaos Monkey during ongoing outages.\n\nIf you wish to have Chaos Monkey"
},
{
"path": "docs/plugins/Tracker.md",
"chars": 1046,
"preview": "A tracker is used to record termination events in some sort of external system.\nInside Netflix, we use trackers to recor"
},
{
"path": "docs/plugins/index.md",
"chars": 1882,
"preview": "# Plugins\n\nWhen Chaos Monkey runs inside of Netflix, it integrates with a number of\nproprietary systems and contains som"
},
{
"path": "eligible/eligible.go",
"chars": 6308,
"preview": "// Copyright 2017 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "eligible/eligible_test.go",
"chars": 6221,
"preview": "package eligible\n\nimport (\n\t\"github.com/Netflix/chaosmonkey/v2\"\n\tD \"github.com/Netflix/chaosmonkey/v2/deploy\"\n\t\"github.c"
},
{
"path": "eligible/instances_canary_test.go",
"chars": 2987,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "eligible/instances_test.go",
"chars": 6314,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "env/env.go",
"chars": 1174,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "errorcounter/errorcounter.go",
"chars": 1295,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "go.mod",
"chars": 1877,
"preview": "module github.com/Netflix/chaosmonkey/v2\n\ngo 1.19\n\nrequire (\n\tgithub.com/SmartThingsOSS/frigga-go v0.0.0-20180827230714-"
},
{
"path": "go.sum",
"chars": 7574,
"preview": "github.com/SmartThingsOSS/frigga-go v0.0.0-20180827230714-55b2c36db3e7 h1:e3ZaLXEVpiXqp5D/ozG2C6ahR8IctL6TsPrVQN8gbws=\ng"
},
{
"path": "grp/grp.go",
"chars": 5525,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "grp/grp_test.go",
"chars": 7072,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "migration/migrations.go",
"chars": 8060,
"preview": "// Code generated by go-bindata.\n// sources:\n// migration/mysql/1.0.0_initial_schema.sql\n// DO NOT EDIT!\n\npackage migrat"
},
{
"path": "migration/mysql/1.0.0_initial_schema.sql",
"chars": 1427,
"preview": "-- +migrate Up\n-- SQL in section 'Up' is executed when this migration is applied\nCREATE TABLE IF NOT EXISTS schedules (\n"
},
{
"path": "mkdocs.yml",
"chars": 1086,
"preview": "site_name: Chaos Monkey\nsite_url: https://netflix.github.io/chaosmonkey\nrepo_url: https://github.com/Netflix/chaosmonkey"
},
{
"path": "mock/configgetter.go",
"chars": 1538,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/deployment.go",
"chars": 5284,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/deps.go",
"chars": 2728,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/install.go",
"chars": 920,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/instance.go",
"chars": 1537,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/mock.go",
"chars": 1575,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/outage.go",
"chars": 783,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mock/terminator.go",
"chars": 1061,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mysql/checker_test.go",
"chars": 10154,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mysql/mysql.go",
"chars": 13700,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mysql/mysql_test.go",
"chars": 4769,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mysql/no_kills_since_test.go",
"chars": 7193,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "mysql/schedstore_test.go",
"chars": 10858,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "outage/outage.go",
"chars": 1306,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "schedstore/schedstore.go",
"chars": 1220,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "schedule/constrainer.go",
"chars": 829,
"preview": "// Copyright 2017 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "schedule/schedule.go",
"chars": 9225,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "schedule/schedule_test.go",
"chars": 2165,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/config.go",
"chars": 1490,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/fromjson.go",
"chars": 5509,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/fromjson_test.go",
"chars": 7577,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/spinnaker.go",
"chars": 17557,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/terminator.go",
"chars": 6148,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/terminator_test.go",
"chars": 4087,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "spinnaker/urls.go",
"chars": 2744,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "term/term.go",
"chars": 5505,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "term/term_ext_test.go",
"chars": 2913,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "term/terminate_test.go",
"chars": 5892,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "term/terminator.go",
"chars": 1215,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "tracker/tracker.go",
"chars": 1645,
"preview": "// Copyright 2016 Netflix, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "update-docs.sh",
"chars": 578,
"preview": "#!/bin/bash\nset -e\necho \"DEPLOY_DOCS=$DEPLOY_DOCS\"\nif [[ $DEPLOY_DOCS != true ]]; then\n echo \"Not building docs\"\n "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the Netflix/chaosmonkey GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 102 files (404.5 KB), approximately 113.0k tokens, and a symbol index with 539 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.