Full Code of Netflix/chaosmonkey for AI

master eaa28fb761c0 cached
102 files
404.5 KB
113.0k tokens
539 symbols
1 requests
Download .txt
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
================================================
![logo](docs/logo.png "logo")

[![NetflixOSS Lifecycle](https://img.shields.io/osslifecycle/Netflix/chaosmonkey.svg)](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,
Download .txt
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
Download .txt
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": "![logo](docs/logo.png \"logo\")\n\n[![NetflixOSS Lifecycle](https://img.shields.io/osslifecycle/Netflix/chaosmonkey.svg)](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": "![Logo](logo.png)\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.

Copied to clipboard!