├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .go-version ├── .project ├── LICENSE ├── README.md ├── RELEASE_VERSION ├── Vagrantfile ├── build.sh ├── bump_release_version_and_tag ├── conf ├── orchestrator-sample.conf.json └── orchestrator-simple.conf.json ├── doc ├── FAQ.md ├── Home.md ├── Orchestrator-Manual.md ├── Orchestrator-deployment.md ├── Orchestrator-first-steps.md ├── Orchestrator-for-developers.md ├── Orchestrator-high-availability.md └── images │ ├── orchestator-cm-co-masters.png │ ├── orchestator-cm-simple-drag-master.png │ ├── orchestrator-about.png │ ├── orchestrator-audit-small.png │ ├── orchestrator-deployment.png │ ├── orchestrator-discover.png │ ├── orchestrator-instance-modal.png │ ├── orchestrator-introduction-screenshot.png │ ├── orchestrator-problems.png │ ├── orchestrator-pseudo-gtid-dead-relay-master-begin-drag.png │ ├── orchestrator-pseudo-gtid-dead-relay-master-drop.png │ ├── orchestrator-pseudo-gtid-dead-relay-master-refactored-1.png │ ├── orchestrator-pseudo-gtid-dead-relay-master.png │ ├── orchestrator-simple-drag-hover.png │ ├── orchestrator-simple-drag.png │ ├── orchestrator-simple-dropped.png │ ├── orchestrator-simple-with-problems.png │ └── orchestrator-simple.png ├── etc └── init.d │ └── orchestrator.bash ├── go ├── agent │ ├── agent.go │ └── agent_dao.go ├── app │ ├── cli.go │ └── http.go ├── attributes │ ├── attributes.go │ └── attributes_dao.go ├── cmd │ └── orchestrator │ │ └── main.go ├── config │ ├── cli_flags.go │ └── config.go ├── db │ └── db.go ├── discovery │ └── discovery.go ├── http │ ├── agents_api.go │ ├── api.go │ ├── httpbase.go │ └── web.go ├── inst │ ├── analysis.go │ ├── analysis_dao.go │ ├── audit.go │ ├── audit_dao.go │ ├── binlog.go │ ├── candidate_database_instance.go │ ├── cluster.go │ ├── cluster_alias.go │ ├── cluster_alias_dao.go │ ├── cluster_domain_dao.go │ ├── downtime_dao.go │ ├── instance.go │ ├── instance_binlog.go │ ├── instance_binlog_dao.go │ ├── instance_dao.go │ ├── instance_dao_test.go │ ├── instance_key.go │ ├── instance_key_map.go │ ├── instance_test.go │ ├── instance_topology.go │ ├── instance_topology_dao.go │ ├── instance_topology_test.go │ ├── instance_utils.go │ ├── maintenance.go │ ├── maintenance_dao.go │ ├── master_equivalence.go │ ├── master_equivalence_dao.go │ ├── oracle_gtid_set.go │ ├── oracle_gtid_set_entry.go │ ├── pool.go │ ├── pool_dao.go │ ├── postponed_functions.go │ ├── process.go │ ├── process_dao.go │ ├── resolve.go │ └── resolve_dao.go ├── logic │ ├── async_request.go │ ├── async_request_dao.go │ ├── disable_recovery.go │ ├── orchestrator.go │ ├── topology_recovery.go │ └── topology_recovery_dao.go ├── metrics │ └── graphite.go ├── os │ └── process.go ├── process │ ├── access_token_dao.go │ ├── election_dao.go │ ├── health_dao.go │ ├── host.go │ └── token.go └── ssl │ ├── ssl.go │ └── ssl_test.go ├── resources ├── metrics │ └── orchestrator-grafana.json ├── public │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ └── bootstrap.min.css │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ └── js │ │ │ └── bootstrap.min.js │ ├── css │ │ └── orchestrator.css │ ├── images │ │ ├── ajax-loader.gif │ │ ├── booking-logo-32.png │ │ ├── booking-logo-s.png │ │ ├── keep-calm-and-let-orchestrator-handle-it-transp-m.png │ │ ├── octocat-logo-32.png │ │ ├── outbrain-logo-32.png │ │ ├── outbrain-logo-s.png │ │ └── tile.png │ └── js │ │ ├── agent.js │ │ ├── agents.js │ │ ├── audit-failure-detection.js │ │ ├── audit-recovery.js │ │ ├── audit.js │ │ ├── bootbox.min.js │ │ ├── cluster-analysis-shared.js │ │ ├── cluster-pools.js │ │ ├── cluster-tree.js │ │ ├── cluster.js │ │ ├── clusters-analysis.js │ │ ├── clusters.js │ │ ├── common.js │ │ ├── corex-jquery.js │ │ ├── corex.js │ │ ├── d3.v3.min.js │ │ ├── discover.js │ │ ├── instance-problems.js │ │ ├── jquery-ui.min.js │ │ ├── jquery.cookie-1.4.1.min.js │ │ ├── jquery.min.js │ │ ├── long-queries.js │ │ ├── md5.js │ │ ├── orchestrator.js │ │ ├── search.js │ │ ├── seed-shared.js │ │ ├── seed.js │ │ ├── seeds.js │ │ └── status.js ├── sql │ └── pseudo-gtid.sql └── templates │ ├── about.tmpl │ ├── agent.tmpl │ ├── agent_seed_details.tmpl │ ├── agents.tmpl │ ├── audit.tmpl │ ├── audit_failure_detection.tmpl │ ├── audit_recovery.tmpl │ ├── cluster.tmpl │ ├── cluster_pools.tmpl │ ├── clusters.tmpl │ ├── clusters_analysis.tmpl │ ├── discover.tmpl │ ├── faq.tmpl │ ├── home.tmpl │ ├── keep-calm.tmpl │ ├── layout.tmpl │ ├── long_queries.tmpl │ ├── search.tmpl │ ├── seeds.tmpl │ └── status.tmpl ├── tests └── integration │ └── instance_dao_test.go ├── vagrant ├── README.md ├── admin-build.sh ├── base-build.sh ├── db1-build.sh ├── db1-my.cnf ├── db2-build.sh ├── db2-my.cnf ├── db3-build.sh ├── db3-my.cnf ├── db4-build.sh └── db4-my.cnf └── vendor ├── github.com ├── codegangsta │ └── inject │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── inject.go │ │ ├── translations │ │ └── README_zh_cn.md │ │ └── update_readme.sh ├── cyberdelia │ └── go-metrics-graphite │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ └── graphite.go ├── go-martini │ └── martini │ │ ├── .gitignore │ │ ├── Godeps │ │ └── Godeps.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── env.go │ │ ├── go_version.go │ │ ├── logger.go │ │ ├── martini.go │ │ ├── recovery.go │ │ ├── response_writer.go │ │ ├── return_handler.go │ │ ├── router.go │ │ ├── static.go │ │ ├── translations │ │ ├── README_de_DE.md │ │ ├── README_es_ES.md │ │ ├── README_fr_FR.md │ │ ├── README_ja_JP.md │ │ ├── README_ko_kr.md │ │ ├── README_pl_PL.md │ │ ├── README_pt_br.md │ │ ├── README_ru_RU.md │ │ ├── README_tr_TR.md │ │ ├── README_zh_cn.md │ │ └── README_zh_tw.md │ │ └── wercker.yml ├── go-sql-driver │ └── mysql │ │ ├── AUTHORS │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── ISSUE_TEMPLATE.md │ │ ├── LICENSE │ │ ├── PULL_REQUEST_TEMPLATE.md │ │ ├── README.md │ │ ├── appengine.go │ │ ├── benchmark_test.go │ │ ├── buffer.go │ │ ├── collations.go │ │ ├── connection.go │ │ ├── const.go │ │ ├── driver.go │ │ ├── driver_test.go │ │ ├── dsn.go │ │ ├── dsn_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── infile.go │ │ ├── packets.go │ │ ├── result.go │ │ ├── rows.go │ │ ├── statement.go │ │ ├── transaction.go │ │ ├── utils.go │ │ └── utils_test.go ├── howeyc │ └── gopass │ │ ├── .travis.yml │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── pass.go │ │ └── pass_test.go ├── martini-contrib │ ├── auth │ │ ├── LICENSE │ │ ├── README.md │ │ ├── basic.go │ │ ├── util.go │ │ └── wercker.yml │ ├── gzip │ │ ├── LICENSE │ │ ├── README.md │ │ ├── gzip.go │ │ └── wercker.yml │ ├── oauth2 │ │ └── oauth2.go │ └── render │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fixtures │ │ ├── basic │ │ │ ├── admin │ │ │ │ └── index.tmpl │ │ │ ├── another_layout.tmpl │ │ │ ├── content.tmpl │ │ │ ├── current_layout.tmpl │ │ │ ├── delims.tmpl │ │ │ ├── hello.tmpl │ │ │ ├── hypertext.html │ │ │ └── layout.tmpl │ │ └── custom_funcs │ │ │ └── index.tmpl │ │ ├── render.go │ │ └── wercker.yml ├── outbrain │ └── golib │ │ ├── log │ │ └── log.go │ │ ├── math │ │ └── math.go │ │ ├── sqlutils │ │ └── sqlutils.go │ │ ├── tests │ │ └── spec.go │ │ └── util │ │ └── text.go ├── oxtoacart │ └── bpool │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bpool.go │ │ ├── bufferpool.go │ │ ├── bytepool.go │ │ └── sizedbufferpool.go ├── patrickmn │ └── go-cache │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── cache.go │ │ ├── cache_test.go │ │ ├── sharded.go │ │ └── sharded_test.go └── rcrowley │ └── go-metrics │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── cmd │ ├── metrics-bench │ │ └── metrics-bench.go │ ├── metrics-example │ │ └── metrics-example.go │ └── never-read │ │ └── never-read.go │ ├── counter.go │ ├── counter_test.go │ ├── debug.go │ ├── debug_test.go │ ├── ewma.go │ ├── ewma_test.go │ ├── exp │ └── exp.go │ ├── gauge.go │ ├── gauge_float64.go │ ├── gauge_float64_test.go │ ├── gauge_test.go │ ├── graphite.go │ ├── graphite_test.go │ ├── healthcheck.go │ ├── histogram.go │ ├── histogram_test.go │ ├── json.go │ ├── json_test.go │ ├── librato │ ├── client.go │ └── librato.go │ ├── log.go │ ├── memory.md │ ├── meter.go │ ├── meter_test.go │ ├── metrics.go │ ├── metrics_test.go │ ├── opentsdb.go │ ├── opentsdb_test.go │ ├── registry.go │ ├── registry_test.go │ ├── runtime.go │ ├── runtime_cgo.go │ ├── runtime_gccpufraction.go │ ├── runtime_no_cgo.go │ ├── runtime_no_gccpufraction.go │ ├── runtime_test.go │ ├── sample.go │ ├── sample_test.go │ ├── stathat │ └── stathat.go │ ├── syslog.go │ ├── timer.go │ ├── timer_test.go │ ├── validate.sh │ ├── writer.go │ └── writer_test.go ├── golang.org └── x │ └── crypto │ └── ssh │ └── terminal │ ├── terminal.go │ ├── terminal_test.go │ ├── util.go │ ├── util_bsd.go │ ├── util_linux.go │ ├── util_plan9.go │ └── util_windows.go ├── gopkg.in └── gcfg.v1 │ ├── LICENSE │ ├── README │ ├── doc.go │ ├── example_test.go │ ├── go1_0.go │ ├── go1_2.go │ ├── issues_test.go │ ├── read.go │ ├── read_test.go │ ├── scanner │ ├── errors.go │ ├── example_test.go │ ├── scanner.go │ └── scanner_test.go │ ├── set.go │ ├── testdata │ ├── gcfg_test.gcfg │ └── gcfg_unicode_test.gcfg │ ├── token │ ├── position.go │ ├── position_test.go │ ├── serialize.go │ ├── serialize_test.go │ └── token.go │ └── types │ ├── bool.go │ ├── doc.go │ ├── enum.go │ ├── enum_test.go │ ├── int.go │ ├── int_test.go │ ├── scan.go │ └── scan_test.go └── vendor.json /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # orchestrator upstream has moved 2 | 3 | **NOTE**: `orchestrator` development is now active on https://github.com/github/orchestrator, where Issues and Pull Requests are accepted. 4 | 5 | This repository is no longer the upstream and latest version of `orchestrator`. 6 | 7 | Please submit your Issue at https://github.com/github/orchestrator/issues/new 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # orchestrator upstream has moved 2 | 3 | **NOTE**: `orchestrator` development is now active on https://github.com/github/orchestrator, where Issues and Pull Requests are accepted. 4 | 5 | This repository is no longer the upstream and latest version of `orchestrator`. 6 | 7 | Please submit your Pull Request with the https://github.com/github/orchestrator repository. 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | conf/orchestrator.conf.json 2 | .DS_Store 3 | .vagrant/ 4 | vagrant/admin-post-install.sh 5 | vagrant/db-post-install.sh 6 | vagrant/db1-post-install.sh 7 | vagrant/db2-post-install.sh 8 | vagrant/db3-post-install.sh 9 | vagrant/db4-post-install.sh 10 | vagrant/vagrant-ssh-key 11 | vagrant/vagrant-ssh-key.pub 12 | Godeps/_workspace 13 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | orchestrator 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RELEASE_VERSION: -------------------------------------------------------------------------------- 1 | 1.5.7 2 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_DIR = File.join(ENV['HOME'], 'Development') 2 | 3 | ENV['VAGRANT_SERVER_URL'] = "https://atlas.hashicorp.com" if ENV['VAGRANT_SERVER_URL'].nil? || ENV['VAGRANT_SERVER_URL'].empty? 4 | ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox' if ENV['VAGRANT_DEFAULT_PROVIDER'].nil? || ENV['VAGRANT_DEFAULT_PROVIDER'].empty? 5 | BOX = ENV['VAGRANT_BOX'].nil? || ENV['VAGRANT_BOX'].empty? ? 'nrel/CentOS-6.6-x86_64' : ENV['VAGRANT_BOX'] 6 | 7 | VAGRANTFILE_API_VERSION = "2" 8 | 9 | system(" 10 | if [[ #{ARGV[0]} = 'up' ]] && [[ ! -e 'vagrant/vagrant-ssh-key' ]]; then 11 | ssh-keygen -t rsa -b 768 -N '' -q -f vagrant/vagrant-ssh-key 12 | fi 13 | ") 14 | 15 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 16 | config.vm.box = BOX 17 | config.vm.box_download_insecure = true 18 | config.vm.box_check_update = false 19 | config.vm.provider 'virtualbox' do |vb| 20 | vb.customize ['modifyvm', :id, '--nictype1', 'virtio'] 21 | vb.customize ['modifyvm', :id, '--nictype2', 'virtio'] 22 | end 23 | 24 | config.vm.synced_folder '.', '/orchestrator', type: 'rsync', 25 | rsync__auto: true, 26 | rsync__exclude: ".git/" 27 | 28 | (0..4).each do |n| 29 | name = (n > 0 ? ("db" + n.to_s) : "admin") 30 | config.vm.define name do |db| 31 | db.vm.hostname = name 32 | db.vm.network "private_network", ip: "192.168.57.20" + n.to_s 33 | db.vm.provision "shell", path: "vagrant/base-build.sh" 34 | if name == "admin" 35 | db.vm.network "forwarded_port", guest:3000, host:3000 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /bump_release_version_and_tag: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Simple script to update the release version and tag according to that value. 4 | # - release version now stored in RELEASE_VERSION e.g. 1.4.571 5 | # - the tag usually is of the form v1.4.571. 6 | # 7 | # If there's a difference between current code and there are no pending changes 8 | # then: 9 | # - the current version will be increased by one 10 | # - the RELEASE_VERSION updated accordingly 11 | # - a new tag will be created 12 | # - the tag will be pushed to origin (may need fixing) 13 | 14 | diff=$(git diff) 15 | rc=$? 16 | if [ $rc != 0 ]; then 17 | echo "git diff failed. Doing nothing" 18 | exit 0 19 | fi 20 | if [ -n "$diff" ]; then 21 | echo "Some pending changes need committing. Doing nothing" 22 | exit 0 23 | fi 24 | old_version=$(cat RELEASE_VERSION) 25 | old_tag=v$old_version 26 | diff=$(git diff $old_tag) 27 | rc=$? 28 | if [ $rc = 128 ]; then 29 | echo "git diff $old_tag failed. Revision unknown. Doing nothing" 30 | exit 0 31 | fi 32 | if [ $rc != 0 ]; then 33 | echo "git diff $old_tag failed. Doing nothing" 34 | exit 0 35 | fi 36 | if [ -z "$diff" ]; then 37 | echo "No differences against $old_version. Doing nothing" 38 | exit 0 39 | fi 40 | 41 | echo "We need to bump the version" 42 | echo "OLD: version: $old_version, tag: $old_tag" 43 | # now generate a new version. 44 | last_digit=$(echo "$old_version" | sed -e 's/.*\.//') 45 | new_digit=$(($last_digit + 1)) 46 | new_version=$(echo "$old_version" | sed -e "s/\.[0-9]*$/.$new_digit/") 47 | new_tag=v$new_version 48 | echo "NEW: version: $new_version, tag: $new_tag" 49 | echo "Modifying RELEASE_VERSION..." 50 | echo "$new_version" > RELEASE_VERSION 51 | echo "Committing change to RELEASE_VERSION..." 52 | git add RELEASE_VERSION 53 | git commit -m"bump RELEASE_VERSION from $old_version to $new_version" 54 | echo "Adding new tag: $new_tag" 55 | git tag $new_tag 56 | echo "Pushing changes and new tag..." 57 | git push 58 | git push origin $new_tag 59 | -------------------------------------------------------------------------------- /doc/Home.md: -------------------------------------------------------------------------------- 1 | # orchestrator upstream has moved 2 | 3 | **NOTE**: `orchestrator` development is now active on https://github.com/github/orchestrator, where Issues and Pull Requests are accepted. 4 | 5 | This repository is no longer the upstream and latest version of `orchestrator`. 6 | 7 | The documentation in this repository is not up-to-date. 8 | 9 | --- 10 | Welcome to the _orchestrator_ wiki! 11 | 12 | * [Manual](Orchestrator-Manual) 13 | * [Manual, Table of Contents](Orchestrator-Manual#toc) 14 | * [Orchestrator deployment](Orchestrator-deployment) 15 | * [Orchestrator first steps](Orchestrator-first-steps) 16 | * [Orchestrator for developers](Orchestrator-for-developers) 17 | * [FAQ](FAQ) 18 | -------------------------------------------------------------------------------- /doc/Orchestrator-high-availability.md: -------------------------------------------------------------------------------- 1 | # orchestrator upstream has moved 2 | 3 | **NOTE**: `orchestrator` development is now active on https://github.com/github/orchestrator, where Issues and Pull Requests are accepted. 4 | 5 | This repository is no longer the upstream and latest version of `orchestrator`. 6 | 7 | The documentation in this repository is not up-to-date. 8 | 9 | --- 10 | ## Orchestrator High Availability 11 | 12 | _Orchestrator_ makes your MySQL topologies available, but what makes _orchestrator_ highly available? 13 | 14 | Before drilling down into the details, we should first observe that orchestrator is a service that runs with a MySQL backend. Thus, we need to substantiate the HA of both these components, as well as the continued correctness in the failover process of either of the two or of both. 15 | 16 | ### High Availability of the Orchestrator service 17 | 18 | ### High Availability of the Orchestrator backend database 19 | 20 | At this time _Orchestrator_ relies on a MySQL backend. The state of the clusters is persisted to tables and is queried via SQL. It is worth considering the following: 21 | 22 | - The backend database is very small, and is linear with your number of servers. For most setups it's a matter of a few MB and well below 1GB, depending on history configuration, rate of polling etc. This easily allows for a fully in-memory database even on simplest machines. 23 | 24 | - Write rate is low 25 | 26 | To that extent you may use one of the following solutions in order to make the backend database highly available: 27 | 28 | - 2-node MySQL Cluster 29 | This is a synchronous solution; anything you write on one node is guaranteed to exist on the second. Data is available and up to date even in the face of a death of one server. 30 | Suggestion: abstract via HAProxy with `first` load-balancing algorithm. 31 | > NOTE: right now table creation explicitly creates tables using InnoDB engine; you may `ALTER TABLE ... ENGINE=NDB` 32 | 33 | - 3-node Galera/XtraDB cluster 34 | This is a synchronous solution; anything you write on one node is guaranteed to exist on both other servers. 35 | Galera is eventually consistent. 36 | Data is available and up to date even in the face of a death of one server. 37 | Suggestion: abstract via HAProxy with `first` load-balancing algorithm. 38 | 39 | - 2-node active-passive master-master configuration 40 | 41 | > NOTE: there has been an initial discussion on supporting Consul/etcd as backend datastore; there is no pending work on that at this time. 42 | -------------------------------------------------------------------------------- /doc/images/orchestator-cm-co-masters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestator-cm-co-masters.png -------------------------------------------------------------------------------- /doc/images/orchestator-cm-simple-drag-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestator-cm-simple-drag-master.png -------------------------------------------------------------------------------- /doc/images/orchestrator-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-about.png -------------------------------------------------------------------------------- /doc/images/orchestrator-audit-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-audit-small.png -------------------------------------------------------------------------------- /doc/images/orchestrator-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-deployment.png -------------------------------------------------------------------------------- /doc/images/orchestrator-discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-discover.png -------------------------------------------------------------------------------- /doc/images/orchestrator-instance-modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-instance-modal.png -------------------------------------------------------------------------------- /doc/images/orchestrator-introduction-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-introduction-screenshot.png -------------------------------------------------------------------------------- /doc/images/orchestrator-problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-problems.png -------------------------------------------------------------------------------- /doc/images/orchestrator-pseudo-gtid-dead-relay-master-begin-drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-pseudo-gtid-dead-relay-master-begin-drag.png -------------------------------------------------------------------------------- /doc/images/orchestrator-pseudo-gtid-dead-relay-master-drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-pseudo-gtid-dead-relay-master-drop.png -------------------------------------------------------------------------------- /doc/images/orchestrator-pseudo-gtid-dead-relay-master-refactored-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-pseudo-gtid-dead-relay-master-refactored-1.png -------------------------------------------------------------------------------- /doc/images/orchestrator-pseudo-gtid-dead-relay-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-pseudo-gtid-dead-relay-master.png -------------------------------------------------------------------------------- /doc/images/orchestrator-simple-drag-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-simple-drag-hover.png -------------------------------------------------------------------------------- /doc/images/orchestrator-simple-drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-simple-drag.png -------------------------------------------------------------------------------- /doc/images/orchestrator-simple-dropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-simple-dropped.png -------------------------------------------------------------------------------- /doc/images/orchestrator-simple-with-problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-simple-with-problems.png -------------------------------------------------------------------------------- /doc/images/orchestrator-simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/doc/images/orchestrator-simple.png -------------------------------------------------------------------------------- /etc/init.d/orchestrator.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # orchestrator daemon 3 | # chkconfig: 345 20 80 4 | # description: orchestrator daemon 5 | # processname: orchestrator 6 | 7 | 8 | # Script credit: http://werxltd.com/wp/2012/01/05/simple-init-d-script-template/ 9 | 10 | DAEMON_PATH="/usr/local/orchestrator" 11 | 12 | DAEMON=orchestrator 13 | DAEMONOPTS="--verbose http" 14 | 15 | NAME=orchestrator 16 | DESC="orchestrator: MySQL replication management and visualization" 17 | PIDFILE=/var/run/$NAME.pid 18 | SCRIPTNAME=/etc/init.d/$NAME 19 | 20 | ulimit -n 16384 21 | 22 | # The file /etc/orchestrator_profile can be used to inject pre-service execution 23 | # scripts, such as exporting variables or whatever. It's yours! 24 | [ -f /etc/orchestrator_profile ] && . /etc/orchestrator_profile 25 | 26 | case "$1" in 27 | start) 28 | printf "%-50s" "Starting $NAME..." 29 | cd $DAEMON_PATH 30 | PID=$(./$DAEMON $DAEMONOPTS >> /var/log/${NAME}.log 2>&1 & echo $!) 31 | #echo "Saving PID" $PID " to " $PIDFILE 32 | if [ -z $PID ]; then 33 | printf "%s\n" "Fail" 34 | exit 1 35 | elif [ -z "$(ps axf | awk '{print $1}' | grep ${PID})" ]; then 36 | printf "%s\n" "Fail" 37 | exit 1 38 | else 39 | echo $PID > $PIDFILE 40 | printf "%s\n" "Ok" 41 | fi 42 | ;; 43 | status) 44 | printf "%-50s" "Checking $NAME..." 45 | if [ -f $PIDFILE ]; then 46 | PID=$(cat $PIDFILE) 47 | if [ -z "$(ps axf | awk '{print $1}' | grep ${PID})" ]; then 48 | printf "%s\n" "Process dead but pidfile exists" 49 | exit 1 50 | else 51 | echo "Running" 52 | fi 53 | else 54 | printf "%s\n" "Service not running" 55 | exit 1 56 | fi 57 | ;; 58 | stop) 59 | printf "%-50s" "Stopping $NAME" 60 | PID=$(cat $PIDFILE) 61 | cd $DAEMON_PATH 62 | if [ -f $PIDFILE ]; then 63 | kill -TERM $PID 64 | rm -f $PIDFILE 65 | # Wait for orchestrator to stop otherwise restart may fail. 66 | # (The newly restarted process may be unable to bind to the 67 | # currently bound socket.) 68 | while ps -p $PID >/dev/null 2>&1; do 69 | printf "." 70 | sleep 1 71 | done 72 | printf "\n" 73 | printf "Ok\n" 74 | else 75 | printf "%s\n" "pidfile not found" 76 | exit 1 77 | fi 78 | ;; 79 | restart) 80 | $0 stop 81 | $0 start 82 | ;; 83 | reload) 84 | PID=$(cat $PIDFILE) 85 | cd $DAEMON_PATH 86 | if [ -f $PIDFILE ]; then 87 | kill -HUP $PID 88 | printf "%s\n" "Ok" 89 | else 90 | printf "%s\n" "pidfile not found" 91 | exit 1 92 | fi 93 | ;; 94 | *) 95 | echo "Usage: $0 {status|start|stop|restart|reload}" 96 | exit 1 97 | esac 98 | -------------------------------------------------------------------------------- /go/agent/agent.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package agent 18 | 19 | import "github.com/outbrain/orchestrator/go/inst" 20 | 21 | // LogicalVolume describes an LVM volume 22 | type LogicalVolume struct { 23 | Name string 24 | GroupName string 25 | Path string 26 | IsSnapshot bool 27 | SnapshotPercent float64 28 | } 29 | 30 | // Mount describes a file system mount point 31 | type Mount struct { 32 | Path string 33 | Device string 34 | LVPath string 35 | FileSystem string 36 | IsMounted bool 37 | DiskUsage int64 38 | MySQLDataPath string 39 | MySQLDiskUsage int64 40 | } 41 | 42 | // Agent presents the data of an agent 43 | type Agent struct { 44 | Hostname string 45 | Port int 46 | Token string 47 | LastSubmitted string 48 | AvailableLocalSnapshots []string 49 | AvailableSnapshots []string 50 | LogicalVolumes []LogicalVolume 51 | MountPoint Mount 52 | MySQLRunning bool 53 | MySQLDiskUsage int64 54 | MySQLPort int64 55 | MySQLDatadirDiskFree int64 56 | MySQLErrorLogTail []string 57 | } 58 | 59 | // SeedOperation makes for the high level data & state of a seed operation 60 | type SeedOperation struct { 61 | SeedId int64 62 | TargetHostname string 63 | SourceHostname string 64 | StartTimestamp string 65 | EndTimestamp string 66 | IsComplete bool 67 | IsSuccessful bool 68 | } 69 | 70 | // SeedOperationState represents a single state (step) in a seed operation 71 | type SeedOperationState struct { 72 | SeedStateId int64 73 | SeedId int64 74 | StateTimestamp string 75 | Action string 76 | ErrorMessage string 77 | } 78 | 79 | // Build an instance key for a given agent 80 | func (this *Agent) GetInstance() *inst.InstanceKey { 81 | return &inst.InstanceKey{Hostname: this.Hostname, Port: int(this.MySQLPort)} 82 | } 83 | -------------------------------------------------------------------------------- /go/attributes/attributes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attributes 18 | 19 | // HostAttributes presnts attributes submitted by a host 20 | type HostAttributes struct { 21 | Hostname string 22 | AttributeName string 23 | AttributeValue string 24 | SubmitTimestamp string 25 | ExpireTimestamp string 26 | } 27 | -------------------------------------------------------------------------------- /go/config/cli_flags.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | // CLIFlags stores some command line flags that are globally available in the process' lifetime 20 | type CLIFlags struct { 21 | Noop *bool 22 | SkipUnresolve *bool 23 | SkipUnresolveCheck *bool 24 | BinlogFile *string 25 | Databaseless *bool 26 | GrabElection *bool 27 | Version *bool 28 | Statement *string 29 | PromotionRule *string 30 | ConfiguredVersion string 31 | } 32 | 33 | var RuntimeCLIFlags CLIFlags 34 | -------------------------------------------------------------------------------- /go/inst/audit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | // Audit presents a single audit entry (namely in the database) 20 | type Audit struct { 21 | AuditId int64 22 | AuditTimestamp string 23 | AuditType string 24 | AuditInstanceKey InstanceKey 25 | Message string 26 | } 27 | -------------------------------------------------------------------------------- /go/inst/cluster_alias.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | // SetClusterAlias will write (and override) a single cluster name mapping 20 | func SetClusterAlias(clusterName string, alias string) error { 21 | return WriteClusterAlias(clusterName, alias) 22 | } 23 | 24 | // GetClusterByAlias returns the cluster name associated with given alias. 25 | // The function returns with error when: 26 | // - No cluster is associated with the alias 27 | // - More than one cluster is associated with the alias 28 | func GetClusterByAlias(alias string) (string, error) { 29 | return ReadClusterNameByAlias(alias) 30 | } 31 | -------------------------------------------------------------------------------- /go/inst/cluster_domain_dao.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "github.com/outbrain/golib/log" 21 | "github.com/outbrain/orchestrator/go/config" 22 | "github.com/outbrain/orchestrator/go/db" 23 | ) 24 | 25 | // WriteClusterDomainName will write (and override) the domain name of a cluster 26 | func WriteClusterDomainName(clusterName string, domainName string) error { 27 | writeFunc := func() error { 28 | _, err := db.ExecOrchestrator(` 29 | insert into 30 | cluster_domain_name (cluster_name, domain_name, last_registered) 31 | values 32 | (?, ?, NOW()) 33 | on duplicate key update 34 | domain_name=values(domain_name), 35 | last_registered=values(last_registered) 36 | `, 37 | clusterName, domainName) 38 | return log.Errore(err) 39 | } 40 | return ExecDBWriteFunc(writeFunc) 41 | } 42 | 43 | // ExpireClusterDomainName expires cluster_domain_name entries that haven't been updated recently. 44 | func ExpireClusterDomainName() error { 45 | writeFunc := func() error { 46 | _, err := db.ExecOrchestrator(` 47 | delete from cluster_domain_name 48 | where last_registered < NOW() - INTERVAL ? MINUTE 49 | `, config.Config.ExpiryHostnameResolvesMinutes, 50 | ) 51 | return log.Errore(err) 52 | } 53 | return ExecDBWriteFunc(writeFunc) 54 | } 55 | -------------------------------------------------------------------------------- /go/inst/maintenance.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "github.com/outbrain/orchestrator/go/config" 21 | ) 22 | 23 | // Maintenance indicates a maintenance entry (also in the database) 24 | type Maintenance struct { 25 | MaintenanceId uint 26 | Key InstanceKey 27 | BeginTimestamp string 28 | SecondsElapsed uint 29 | IsActive bool 30 | Owner string 31 | Reason string 32 | } 33 | 34 | var maintenanceOwner string = "" 35 | 36 | func GetMaintenanceOwner() string { 37 | if maintenanceOwner != "" { 38 | return maintenanceOwner 39 | } 40 | return config.Config.MaintenanceOwner 41 | } 42 | 43 | func SetMaintenanceOwner(owner string) { 44 | maintenanceOwner = owner 45 | } 46 | -------------------------------------------------------------------------------- /go/inst/master_equivalence.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | // InstanceBinlogCoordinates is a convenice wrapper for instance key + binlog coordinates 20 | type InstanceBinlogCoordinates struct { 21 | Key InstanceKey 22 | Coordinates BinlogCoordinates 23 | } 24 | -------------------------------------------------------------------------------- /go/inst/oracle_gtid_set.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "strings" 21 | ) 22 | 23 | // OracleGtidSet represents a set of GTID ranges as depicted by Retrieved_Gtid_Set, Executed_Gtid_Set or @@gtid_purged. 24 | type OracleGtidSet struct { 25 | GtidEntries [](*OracleGtidSetEntry) 26 | } 27 | 28 | // Example input: `230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539, 29 | // 316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-8935:8984-6124596, 30 | // 321f5c0d-70e5-11e5-adb2-ecf4bb2262ff:1-56` 31 | func ParseGtidSet(gtidSet string) (res *OracleGtidSet, err error) { 32 | res = &OracleGtidSet{} 33 | 34 | gtidSet = strings.TrimSpace(gtidSet) 35 | if gtidSet == "" { 36 | return res, nil 37 | } 38 | entries := strings.Split(gtidSet, ",") 39 | for _, entry := range entries { 40 | if gtidRange, err := NewOracleGtidSetEntry(entry); err == nil { 41 | res.GtidEntries = append(res.GtidEntries, gtidRange) 42 | } else { 43 | return res, err 44 | } 45 | } 46 | return res, nil 47 | } 48 | 49 | // RemoveUUID removes entries that belong to given UUID. 50 | // By way of how this works there can only be one entry matching our UUID, but we generalize. 51 | // We keep order of entries. 52 | func (this *OracleGtidSet) RemoveUUID(uuid string) (removed bool) { 53 | filteredEntries := [](*OracleGtidSetEntry){} 54 | for _, entry := range this.GtidEntries { 55 | if entry.UUID == uuid { 56 | removed = true 57 | } else { 58 | filteredEntries = append(filteredEntries, entry) 59 | } 60 | } 61 | if removed { 62 | this.GtidEntries = filteredEntries 63 | } 64 | return removed 65 | } 66 | 67 | func (this OracleGtidSet) String() string { 68 | tokens := []string{} 69 | for _, entry := range this.GtidEntries { 70 | tokens = append(tokens, entry.String()) 71 | } 72 | return strings.Join(tokens, ",\n") 73 | } 74 | -------------------------------------------------------------------------------- /go/inst/oracle_gtid_set_entry.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "fmt" 21 | "strings" 22 | ) 23 | 24 | // OracleGtidSetEntry represents an entry in a set of GTID ranges, 25 | // for example, the entry: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-8935:8984-6124596" (may include gaps) 26 | type OracleGtidSetEntry struct { 27 | UUID string 28 | Ranges string 29 | } 30 | 31 | // NewOracleGtidSetEntry parses a single entry text 32 | func NewOracleGtidSetEntry(gtidRangeString string) (*OracleGtidSetEntry, error) { 33 | gtidRangeString = strings.TrimSpace(gtidRangeString) 34 | tokens := strings.SplitN(gtidRangeString, ":", 2) 35 | if len(tokens) != 2 { 36 | return nil, fmt.Errorf("Cannot parse OracleGtidSetEntry from %s", gtidRangeString) 37 | } 38 | if tokens[0] == "" { 39 | return nil, fmt.Errorf("Unexpected UUID: %s", tokens[0]) 40 | } 41 | if tokens[1] == "" { 42 | return nil, fmt.Errorf("Unexpected GTID range: %s", tokens[1]) 43 | } 44 | gtidRange := &OracleGtidSetEntry{UUID: tokens[0], Ranges: tokens[1]} 45 | return gtidRange, nil 46 | } 47 | 48 | // String returns a user-friendly string representation of this entry 49 | func (this OracleGtidSetEntry) String() string { 50 | return fmt.Sprintf("%s:%s", this.UUID, this.Ranges) 51 | } 52 | -------------------------------------------------------------------------------- /go/inst/pool.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/outbrain/orchestrator/go/config" 23 | 24 | "github.com/outbrain/golib/log" 25 | ) 26 | 27 | // PoolInstancesMap lists instance keys per pool name 28 | type PoolInstancesMap map[string]([]*InstanceKey) 29 | 30 | // ClusterPoolInstance is an instance mapping a cluster, pool & instance 31 | type ClusterPoolInstance struct { 32 | ClusterName string 33 | ClusterAlias string 34 | Pool string 35 | Hostname string 36 | Port int 37 | } 38 | 39 | func ApplyPoolInstances(pool string, instancesList string) error { 40 | var instanceKeys [](*InstanceKey) 41 | if instancesList != "" { 42 | instancesStrings := strings.Split(instancesList, ",") 43 | for _, instanceString := range instancesStrings { 44 | 45 | instanceKey, err := ParseInstanceKeyLoose(instanceString) 46 | if config.Config.SupportFuzzyPoolHostnames { 47 | instanceKey = ReadFuzzyInstanceKeyIfPossible(instanceKey) 48 | } 49 | log.Debugf("%+v", instanceKey) 50 | if err != nil { 51 | return log.Errore(err) 52 | } 53 | 54 | instanceKeys = append(instanceKeys, instanceKey) 55 | } 56 | } 57 | writePoolInstances(pool, instanceKeys) 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /go/inst/postponed_functions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | import ( 20 | "github.com/outbrain/golib/log" 21 | ) 22 | 23 | type PostponedFunctionsContainer struct { 24 | PostponedFunctions [](func() error) 25 | } 26 | 27 | func NewPostponedFunctionsContainer() *PostponedFunctionsContainer { 28 | postponedFunctionsContainer := &PostponedFunctionsContainer{} 29 | postponedFunctionsContainer.PostponedFunctions = [](func() error){} 30 | return postponedFunctionsContainer 31 | } 32 | 33 | func (this *PostponedFunctionsContainer) AddPostponedFunction(f func() error) { 34 | this.PostponedFunctions = append(this.PostponedFunctions, f) 35 | } 36 | 37 | func (this *PostponedFunctionsContainer) InvokePostponed() (err error) { 38 | if len(this.PostponedFunctions) == 0 { 39 | return 40 | } 41 | log.Debugf("PostponedFunctionsContainer: invoking %+v postponed functions", len(this.PostponedFunctions)) 42 | for _, postponedFunction := range this.PostponedFunctions { 43 | ferr := postponedFunction() 44 | if err == nil { 45 | err = ferr 46 | } 47 | } 48 | return err 49 | } 50 | -------------------------------------------------------------------------------- /go/inst/process.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package inst 18 | 19 | // Process presents a MySQL executing thread (as observed by PROCESSLIST) 20 | type Process struct { 21 | InstanceHostname string 22 | InstancePort int 23 | Id int64 24 | User string 25 | Host string 26 | Db string 27 | Command string 28 | Time int64 29 | State string 30 | Info string 31 | StartedAt string 32 | } 33 | -------------------------------------------------------------------------------- /go/logic/async_request.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logic 18 | 19 | import ( 20 | "github.com/outbrain/orchestrator/go/inst" 21 | ) 22 | 23 | // AsyncRequest represents an entry in the async_request table 24 | type AsyncRequest struct { 25 | Id int64 26 | Story string 27 | Command string 28 | OperatedInstanceKey *inst.InstanceKey 29 | DestinationKey *inst.InstanceKey 30 | Pattern string 31 | GTIDHint inst.OperationGTIDHint 32 | } 33 | 34 | func NewEmptyAsyncRequest() *AsyncRequest { 35 | asyncRequest := &AsyncRequest{} 36 | asyncRequest.GTIDHint = inst.GTIDHintNeutral 37 | return asyncRequest 38 | } 39 | 40 | func NewAsyncRequest(story string, command string, instanceKey *inst.InstanceKey, destinationKey *inst.InstanceKey, pattern string, gtidHint inst.OperationGTIDHint) *AsyncRequest { 41 | asyncRequest := NewEmptyAsyncRequest() 42 | asyncRequest.Story = story 43 | asyncRequest.Command = command 44 | asyncRequest.OperatedInstanceKey = instanceKey 45 | asyncRequest.DestinationKey = destinationKey 46 | asyncRequest.Pattern = pattern 47 | asyncRequest.GTIDHint = gtidHint 48 | return asyncRequest 49 | } 50 | 51 | func NewSimpleAsyncRequest(story string, command string, instanceKey *inst.InstanceKey) *AsyncRequest { 52 | asyncRequest := NewEmptyAsyncRequest() 53 | asyncRequest.Story = story 54 | asyncRequest.Command = command 55 | asyncRequest.OperatedInstanceKey = instanceKey 56 | asyncRequest.DestinationKey = nil 57 | asyncRequest.Pattern = "" 58 | return asyncRequest 59 | } 60 | -------------------------------------------------------------------------------- /go/logic/disable_recovery.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logic 18 | 19 | // This file holds wrappers around routines to check if global 20 | // recovery is disabled or not. 21 | // 22 | // This is determined by looking in the table 23 | // orchestrator.global_recovery_disable for a value 1. Note: for 24 | // recoveries to actually happen this must be configured explicitly 25 | // in orchestrator.conf.json. This setting is an emergency brake 26 | // to quickly be able to prevent recoveries happening in some large 27 | // outage type situation. Maybe this value should be cached etc 28 | // but we won't be doing that many recoveries at once so the load 29 | // on this table is expected to be very low. It should be fine to 30 | // go to the database each time. 31 | 32 | import ( 33 | "github.com/outbrain/golib/log" 34 | "github.com/outbrain/golib/sqlutils" 35 | "github.com/outbrain/orchestrator/go/db" 36 | ) 37 | 38 | // IsRecoveryDisabled returns true if Recoveries are disabled globally 39 | func IsRecoveryDisabled() (bool, error) { 40 | var ( 41 | disabled bool // default is false! 42 | err error 43 | ) 44 | query := ` 45 | SELECT 46 | COUNT(*) as mycount 47 | FROM 48 | global_recovery_disable 49 | WHERE 50 | disable_recovery=? 51 | ` 52 | err = db.QueryOrchestrator(query, sqlutils.Args(1), func(m sqlutils.RowMap) error { 53 | mycount := m.GetInt("mycount") 54 | disabled = (mycount > 0) 55 | return nil 56 | }) 57 | if err != nil { 58 | err = log.Errorf("recovery.IsRecoveryDisabled(): %v", err) 59 | } 60 | return disabled, err 61 | } 62 | 63 | // DisableRecovery ensures recoveries are disabled globally 64 | func DisableRecovery() error { 65 | _, err := db.ExecOrchestrator(` 66 | INSERT IGNORE INTO global_recovery_disable 67 | (disable_recovery) 68 | VALUES (1) 69 | `, 70 | ) 71 | return err 72 | } 73 | 74 | // EnableRecovery ensures recoveries are enabled globally 75 | func EnableRecovery() error { 76 | _, err := db.ExecOrchestrator(` 77 | DELETE FROM global_recovery_disable 78 | `, 79 | ) 80 | return err 81 | } 82 | -------------------------------------------------------------------------------- /go/metrics/graphite.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package metrics 18 | 19 | import ( 20 | "github.com/cyberdelia/go-metrics-graphite" 21 | "github.com/outbrain/golib/log" 22 | "github.com/outbrain/orchestrator/go/config" 23 | "github.com/outbrain/orchestrator/go/process" 24 | "github.com/rcrowley/go-metrics" 25 | "net" 26 | "strings" 27 | "time" 28 | ) 29 | 30 | var graphiteTickCallbacks [](func()) 31 | 32 | // InitGraphiteMetrics is called once in the lifetime of the app, after config has been loaded 33 | func InitGraphiteMetrics() error { 34 | if config.Config.GraphiteAddr == "" { 35 | return nil 36 | } 37 | if config.Config.GraphitePollSeconds <= 0 { 38 | return nil 39 | } 40 | if config.Config.GraphitePath == "" { 41 | return log.Errorf("No graphite path provided (see GraphitePath config variable). Will not log to graphite") 42 | } 43 | addr, err := net.ResolveTCPAddr("tcp", config.Config.GraphiteAddr) 44 | if err != nil { 45 | return log.Errore(err) 46 | } 47 | graphitePathHostname := process.ThisHostname 48 | if config.Config.GraphiteConvertHostnameDotsToUnderscores { 49 | graphitePathHostname = strings.Replace(graphitePathHostname, ".", "_", -1) 50 | } 51 | graphitePath := config.Config.GraphitePath 52 | graphitePath = strings.Replace(graphitePath, "{hostname}", graphitePathHostname, -1) 53 | 54 | log.Debugf("Will log to graphite on %+v, %+v", config.Config.GraphiteAddr, graphitePath) 55 | 56 | graphiteCallbackTick := time.Tick(time.Duration(config.Config.GraphitePollSeconds) * time.Second) 57 | go func() { 58 | go graphite.Graphite(metrics.DefaultRegistry, 1*time.Minute, graphitePath, addr) 59 | for range graphiteCallbackTick { 60 | for _, f := range graphiteTickCallbacks { 61 | go f() 62 | } 63 | } 64 | }() 65 | 66 | return nil 67 | } 68 | 69 | func OnGraphiteTick(f func()) { 70 | graphiteTickCallbacks = append(graphiteTickCallbacks, f) 71 | } 72 | -------------------------------------------------------------------------------- /go/process/host.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach, courtesy Booking.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package process 18 | 19 | import ( 20 | "github.com/outbrain/golib/log" 21 | "os" 22 | ) 23 | 24 | var ThisHostname string 25 | 26 | func init() { 27 | var err error 28 | ThisHostname, err = os.Hostname() 29 | if err != nil { 30 | log.Fatalf("Cannot resolve self hostname; required. Aborting. %+v", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /go/process/token.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Outbrain Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package process 18 | 19 | import ( 20 | "crypto/rand" 21 | "crypto/sha256" 22 | "encoding/hex" 23 | ) 24 | 25 | func GetHash(input []byte) string { 26 | hasher := sha256.New() 27 | hasher.Write(input) 28 | return hex.EncodeToString(hasher.Sum(nil)) 29 | } 30 | 31 | func GetRandomData() []byte { 32 | size := 64 33 | rb := make([]byte, size) 34 | _, _ = rand.Read(rb) 35 | return rb 36 | } 37 | 38 | // Token is used to identify and validate requests to this service 39 | type Token struct { 40 | Hash string 41 | } 42 | 43 | var ProcessToken *Token = NewToken() 44 | 45 | func NewToken() *Token { 46 | return &Token{ 47 | Hash: GetHash(GetRandomData()), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /resources/public/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /resources/public/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /resources/public/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /resources/public/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/ajax-loader.gif -------------------------------------------------------------------------------- /resources/public/images/booking-logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/booking-logo-32.png -------------------------------------------------------------------------------- /resources/public/images/booking-logo-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/booking-logo-s.png -------------------------------------------------------------------------------- /resources/public/images/keep-calm-and-let-orchestrator-handle-it-transp-m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/keep-calm-and-let-orchestrator-handle-it-transp-m.png -------------------------------------------------------------------------------- /resources/public/images/octocat-logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/octocat-logo-32.png -------------------------------------------------------------------------------- /resources/public/images/outbrain-logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/outbrain-logo-32.png -------------------------------------------------------------------------------- /resources/public/images/outbrain-logo-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/outbrain-logo-s.png -------------------------------------------------------------------------------- /resources/public/images/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outbrain-inc/orchestrator/2a6e4b7a96934b11c3dacb0b2fa4be814f168eef/resources/public/images/tile.png -------------------------------------------------------------------------------- /resources/public/js/agents.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | showLoader(); 4 | activateRefreshTimer(); 5 | 6 | $.get(appUrl("/api/agents"), function (agents) { 7 | displayAgents(agents); 8 | }, "json"); 9 | function displayAgents(agents) { 10 | hideLoader(); 11 | 12 | agents.forEach(function (agent) { 13 | $("#agents").append('
'); 14 | var popoverElement = $("#agents [data-agent-name='" + agent.Hostname + "'].popover"); 15 | //var title = agent.Hostname; 16 | //popoverElement.find("h3 a").html(title); 17 | var contentHtml = '' 18 | + '' 19 | + agent.Hostname 20 | + '' 21 | ; 22 | popoverElement.find(".popover-content").html(contentHtml); 23 | }); 24 | 25 | $("div.popover").popover(); 26 | $("div.popover").show(); 27 | 28 | if (agents.length == 0) { 29 | addAlert("No agents found"); 30 | } 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /resources/public/js/audit.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | showLoader(); 4 | var apiUri = "/api/audit/"+currentPage(); 5 | if (auditHostname()) { 6 | apiUri = "/api/audit/instance/"+auditHostname()+"/"+auditPort()+"/"+currentPage(); 7 | } 8 | $.get(appUrl(apiUri), function (auditEntries) { 9 | displayAudit(auditEntries); 10 | }, "json"); 11 | function displayAudit(auditEntries) { 12 | var baseWebUri = appUrl("/web/audit/"); 13 | if (auditHostname()) { 14 | baseWebUri += "instance/"+auditHostname()+"/"+auditPort()+"/"; 15 | } 16 | hideLoader(); 17 | auditEntries.forEach(function (audit) { 18 | var row = jQuery(''); 19 | jQuery('', { text: audit.AuditTimestamp }).appendTo(row); 20 | jQuery('', { text: audit.AuditType }).appendTo(row); 21 | if (audit.AuditInstanceKey.Hostname) { 22 | var uri = appUrl("/web/audit/instance/"+audit.AuditInstanceKey.Hostname+"/"+audit.AuditInstanceKey.Port); 23 | $('', { text: audit.AuditInstanceKey.Hostname+":"+audit.AuditInstanceKey.Port , href: uri}).wrap($("")).parent().appendTo(row); 24 | } else { 25 | jQuery('', { text: audit.AuditInstanceKey.Hostname+":"+audit.AuditInstanceKey.Port }).appendTo(row); 26 | } 27 | jQuery('', { text: audit.Message }).appendTo(row); 28 | row.appendTo('#audit tbody'); 29 | }); 30 | if (currentPage() <= 0) { 31 | $("#audit .pager .previous").addClass("disabled"); 32 | } 33 | if (auditEntries.length == 0) { 34 | $("#audit .pager .next").addClass("disabled"); 35 | } 36 | $("#audit .pager .previous").not(".disabled").find("a").click(function() { 37 | window.location.href = baseWebUri+(currentPage() - 1); 38 | }); 39 | $("#audit .pager .next").not(".disabled").find("a").click(function() { 40 | window.location.href = baseWebUri+(currentPage() + 1); 41 | }); 42 | $("#audit .pager .disabled a").click(function() { 43 | return false; 44 | }); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /resources/public/js/cluster-analysis-shared.js: -------------------------------------------------------------------------------- 1 | var interestingAnalysis = { 2 | "DeadMaster" : true, 3 | "DeadMasterAndSlaves" : true, 4 | "DeadMasterAndSomeSlaves" : true, 5 | "DeadMasterWithoutSlaves" : true, 6 | "UnreachableMasterWithStaleSlaves": true, 7 | "UnreachableMaster" : true, 8 | "AllMasterSlavesNotReplicating" : true, 9 | "AllMasterSlavesNotReplicatingOrDead" : true, 10 | "AllMasterSlavesStale" : true, 11 | "DeadCoMaster" : true, 12 | "DeadCoMasterAndSomeSlaves" : true, 13 | "DeadIntermediateMaster" : true, 14 | "DeadIntermediateMasterWithSingleSlaveFailingToConnect" : true, 15 | "DeadIntermediateMasterWithSingleSlave" : true, 16 | "DeadIntermediateMasterAndSomeSlaves" : true, 17 | "AllIntermediateMasterSlavesFailingToConnectOrDead" : true, 18 | "AllIntermediateMasterSlavesNotReplicating" : true, 19 | "UnreachableIntermediateMaster" : true, 20 | "BinlogServerFailingToConnectToMaster" : true, 21 | }; 22 | -------------------------------------------------------------------------------- /resources/public/js/common.js: -------------------------------------------------------------------------------- 1 | function Instance(){ 2 | 3 | } 4 | Instance.createElement = function (instance){ 5 | return $('

'); 6 | } -------------------------------------------------------------------------------- /resources/public/js/discover.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | $('button[data-btn=discover-instance]').unbind("click"); 4 | $('button[data-btn=discover-instance]').click(function() { 5 | 6 | if (!$("#discoverHostName").val()) { 7 | return addAlert("You must enter a host name"); 8 | } 9 | if (!$("#discoverPort").val()) { 10 | return addAlert("You must enter a port"); 11 | } 12 | discover($("#discoverHostName").val(), $("#discoverPort").val()) 13 | return false; 14 | }); 15 | $("#discoverHostName").focus(); 16 | }); 17 | 18 | function discover(hostname, port) { 19 | showLoader(); 20 | var uri = "/api/discover/"+hostname+"/"+port; 21 | $.get(appUrl(uri), function (operationResult) { 22 | hideLoader(); 23 | if (operationResult.Code == "ERROR" || operationResult.Details == null) { 24 | addAlert(operationResult.Message) 25 | } else { 26 | var instance = operationResult.Details; 27 | addInfo('Discovered
' 28 | +instance.Key.Hostname+":"+instance.Key.Port+'' 29 | ); 30 | } 31 | }, "json"); 32 | 33 | } -------------------------------------------------------------------------------- /resources/public/js/instance-problems.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | showLoader(); 3 | 4 | var problemsURI = "/api/problems"; 5 | if (typeof currentClusterName != "undefined") { 6 | problemsURI += "/" + currentClusterName(); 7 | } 8 | $.get(appUrl(problemsURI), function(instances) { 9 | if (instances == null) { 10 | instances = []; 11 | } 12 | $.get(appUrl("/api/maintenance"), function(maintenanceList) { 13 | normalizeInstances(instances, maintenanceList); 14 | displayProblemInstances(instances); 15 | }, "json"); 16 | }, "json"); 17 | 18 | function displayProblemInstances(instances) { 19 | hideLoader(); 20 | 21 | function SortByProblemOrder(instance0, instance1) { 22 | var orderDiff = instance0.problemOrder - instance1.problemOrder; 23 | if (orderDiff != 0) return orderDiff; 24 | var orderDiff = instance1.SlaveLagSeconds.Int64 - instance0.SlaveLagSeconds.Int64; 25 | if (orderDiff != 0) return orderDiff; 26 | orderDiff = instance0.title.localeCompare(instance1.title); 27 | if (orderDiff != 0) return orderDiff; 28 | return 0; 29 | } 30 | instances.sort(SortByProblemOrder); 31 | 32 | var countProblemInstances = 0; 33 | instances.forEach(function(instance) { 34 | var considerInstance = instance.hasProblem 35 | if (countProblemInstances >= 1000) { 36 | considerInstance = false; 37 | } 38 | if (considerInstance) { 39 | var li = $("
  • "); 40 | var instanceEl = Instance.createElement(instance).addClass("instance-problem").appendTo(li); 41 | $("#instance_problems ul").append(li); 42 | 43 | //var popoverElement = $("#instance_problems [data-nodeid='" + instance.id + "'].popover"); 44 | renderInstanceElement(instanceEl, instance, "problems"); //popoverElement 45 | instanceEl.click(function() { 46 | openNodeModal(instance); 47 | return false; 48 | }); 49 | if (countProblemInstances == 0) { 50 | // First problem instance 51 | $("#instance_problems_button").addClass("btn-" + instance.renderHint) 52 | } 53 | countProblemInstances += 1; 54 | } 55 | }); 56 | if (countProblemInstances > 0 && (autoshowProblems() == "true") && ($.cookie("anonymize") != "true")) { 57 | $("#instance_problems .dropdown-toggle").dropdown('toggle'); 58 | } 59 | if (countProblemInstances == 0) { 60 | $("#instance_problems").hide(); 61 | } 62 | 63 | $("div.popover").popover(); 64 | $("div.popover").show(); 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /resources/public/js/jquery.cookie-1.4.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jquery.cookie v1.4.1 | MIT */ 2 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}}); -------------------------------------------------------------------------------- /resources/public/js/search.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | $("#searchInput").val(currentSearchString()); 4 | 5 | 6 | showLoader(); 7 | $.get(appUrl("/api/search/"+currentSearchString()), function (instances) { 8 | $.get(appUrl("/api/maintenance"), 9 | function (maintenanceList) { 10 | normalizeInstances(instances, maintenanceList); 11 | displaySearchInstances(instances); 12 | }, "json"); 13 | }, "json"); 14 | function displaySearchInstances(instances) { 15 | hideLoader(); 16 | instances.forEach(function (instance) { 17 | var instanceEl = Instance.createElement(instance).addClass("instance-search").appendTo("#searchResults"); 18 | renderInstanceElement(instanceEl, instance, "search"); 19 | instanceEl.find("h3").click(function () { 20 | openNodeModal(instance); 21 | return false; 22 | }); 23 | }); 24 | 25 | if (instances.length == 0) { 26 | addAlert("No search results found for "+currentSearchString()); 27 | } 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /resources/public/js/seed-shared.js: -------------------------------------------------------------------------------- 1 | 2 | function appendSeedDetails(seed, selector) { 3 | var row = ''; 4 | var statusMessage; 5 | if (seed.IsComplete) { 6 | statusMessage = (seed.IsSuccessful ? 'Success' : 'Fail'); 7 | } else { 8 | statusMessage = 'Active'; 9 | } 10 | row += '' + statusMessage + ''; 11 | row += '' + seed.SeedId + ''; 12 | row += ''+seed.TargetHostname+''; 13 | row += ''+seed.SourceHostname+''; 14 | row += '' + seed.StartTimestamp + ''; 15 | row += '' + (seed.IsComplete ? seed.EndTimestamp : '') + ''; 16 | row += ''; 17 | $(selector).append(row); 18 | hideLoader(); 19 | } 20 | 21 | function appendSeedState(seedState) { 22 | var action = seedState.Action; 23 | action = action.replace(/Copied ([0-9]+).([0-9]+) bytes (.*$)/, function(match, match1, match2, match3) { 24 | return "Copied " + toHumanFormat(match1) + " / " + toHumanFormat(match2) + " " + match3; 25 | }); 26 | var row = ''; 27 | row += '' + seedState.StateTimestamp + ''; 28 | row += '' + action + ''; 29 | row += '' + seedState.ErrorMessage + ''; 30 | row += ''; 31 | $("[data-agent=seed_states]").append(row); 32 | hideLoader(); 33 | } 34 | 35 | $("body").on("click", "button[data-command=abort-seed]", function(event) { 36 | var seedId = $(event.target).attr("data-seed-id"); 37 | var sourceHost = $(event.target).attr("data-seed-source-host"); 38 | var targetHost = $(event.target).attr("data-seed-target-host"); 39 | 40 | var message = "Are you sure you wish to abort seed " + seedId + " from " + 41 | sourceHost + " to " + 42 | targetHost + " ?"; 43 | bootbox.confirm(message, function(confirm) { 44 | if (confirm) { 45 | showLoader(); 46 | $.get(appUrl("/api/agent-abort-seed/"+seedId), function (operationResult) { 47 | hideLoader(); 48 | if (operationResult.Code == "ERROR") { 49 | addAlert(operationResult.Message) 50 | } else { 51 | location.reload(); 52 | } 53 | }, "json"); 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /resources/public/js/seed.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | showLoader(); 4 | 5 | $.get(appUrl("/api/agent-seed-details/"+currentSeedId()), function (seedArray) { 6 | showLoader(); 7 | seedArray.forEach(function (seed) { 8 | appendSeedDetails(seed, "[data-agent=seed_details]"); 9 | if (!seed.IsComplete) { 10 | activateRefreshTimer(); 11 | } 12 | }); 13 | }, "json"); 14 | 15 | $.get(appUrl("/api/agent-seed-states/"+currentSeedId()), function (seedStates) { 16 | showLoader(); 17 | seedStates.forEach(function (seedState) { 18 | appendSeedState(seedState); 19 | }); 20 | }, "json"); 21 | }); 22 | -------------------------------------------------------------------------------- /resources/public/js/seeds.js: -------------------------------------------------------------------------------- 1 | 2 | $(document).ready(function () { 3 | showLoader(); 4 | 5 | $.get(appUrl("/api/seeds"), function (seeds) { 6 | showLoader(); 7 | var hasActive = false; 8 | seeds.forEach(function (seed) { 9 | appendSeedDetails(seed, "[data-agent=seed_details]"); 10 | if (!seed.IsComplete) { 11 | hasActive = true; 12 | } 13 | }); 14 | if (hasActive) { 15 | activateRefreshTimer(); 16 | } 17 | }, "json"); 18 | }); 19 | -------------------------------------------------------------------------------- /resources/public/js/status.js: -------------------------------------------------------------------------------- 1 | 2 | function addStatusTableData(name, column1, column2) { 3 | $("#orchestratorStatusTable").append( 4 | '' + name + '' + 5 | '' + column1 + '' + 6 | '' + column2 + '' 7 | ); 8 | } 9 | function addStatusActionButton(name, uri) { 10 | $("#orchestratorStatus .panel-footer").append( 11 | ' ' 12 | ); 13 | var button = $('#orchestratorStatus .panel-footer button:last'); 14 | button.click(function(){ 15 | apiCommand("/api/"+uri); 16 | }); 17 | 18 | console.log(button) 19 | } 20 | 21 | $(document).ready(function () { 22 | var statusObject = $("#orchestratorStatus .panel-body"); 23 | $.get(appUrl("/api/health/"), function (health) { 24 | statusObject.prepend('

    '+health.Message+'

    ') 25 | health.Details.AvailableNodes.forEach(function(node) { 26 | var values = node.split(";"); 27 | var hostname = values[0]; 28 | var token = values[1]; 29 | var app_version = values[2]; 30 | var message = hostname; 31 | if (hostname + ";" + token == health.Details.ActiveNode) { 32 | message += ' [Active]'; 33 | } 34 | if (hostname == health.Details.Hostname) { 35 | message += ' [This node]'; 36 | } 37 | addStatusTableData("Available node", message, app_version); 38 | }) 39 | 40 | var userId = getUserId(); 41 | if (userId == "") { 42 | userId = "[unknown]" 43 | } 44 | var userStatus = (isAuthorizedForAction() ? "admin" : "read only"); 45 | addStatusTableData("You", userId + ", " + userStatus, ""); 46 | 47 | if (isAuthorizedForAction()) { 48 | addStatusActionButton("Reload configuration", "reload-configuration"); 49 | addStatusActionButton("Reset hostname resolve cache", "reset-hostname-resolve-cache"); 50 | addStatusActionButton("Reelect", "reelect"); 51 | } 52 | 53 | }, "json"); 54 | }); 55 | -------------------------------------------------------------------------------- /resources/templates/about.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | About 5 |
    6 |
    7 |

    8 | Orchestrator is a MySQL replication topology management and visualization tool, allowing for: 9 |

      10 |
    • Detection and investigation of replication clusters
    • 11 |
    • Safe topology refactoring: moving slaves around the topology
    • 12 |
    • Sleek topology visualization
    • 13 |
    • Replication problems visualization
    • 14 |
    • Topology changes via intuitive drag & drop
    • 15 |
    • Maintenance mode declaration and enforcement
    • 16 |
    • Auditing of operations
    • 17 |
    • More...
    • 18 |
    19 |

    20 |

    21 | Refactoring the topology is a matter of a simple drag & drop. Orchestrator will keep you safe by disallowing invalid replication topologies (e.g. replicating from ROW based to STATEMENT based, from 5.5 to 5.1 etc.) 22 |

    23 |

    24 | Orchestrator can serve in different modes: 25 |

      26 |
    • 27 | Command line interface 28 |
    • 29 |
    • 30 | Web API 31 |
    • 32 |
    • 33 | Web interface 34 |
    • 35 |
    36 | 37 |

    38 |

    39 | Orchestrator is developed at GitHub to answer for the difficulty in managing and recovering replication topologies. 40 |

    41 |

    42 | Orchestrator is released as open source under the 43 | Apache 2.0 license and is available at: 44 | https://github.com/github/orchestrator 45 |

    46 |

    47 | Developed by Shlomi Noach. 48 |

    49 |
    50 |
    51 |
    52 | -------------------------------------------------------------------------------- /resources/templates/agent_seed_details.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 |
    7 |
    8 | Seed 9 |
    10 |
    11 |
    12 |
    13 | Seed details 14 |
    15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    StatusIdTarget hostSource hostSeed start timeSeed end time
    28 |
    29 |
    30 |
    31 |
    32 |
    33 | Seed states 34 |
    35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
    State start timeState actionError message
    45 |
    46 |
    47 |
    48 |
    49 | 50 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /resources/templates/agents.tmpl: -------------------------------------------------------------------------------- 1 | 16 | 17 |
    18 |
    19 | 20 | 21 | 23 | 24 | -------------------------------------------------------------------------------- /resources/templates/audit.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
      5 | 6 | 7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    Audit timeTypeInstancemessage
    20 |
      21 | 22 | 23 |
    24 |
    25 |
    26 |
    27 | 28 | 29 | 42 | 43 | -------------------------------------------------------------------------------- /resources/templates/audit_failure_detection.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
      5 | 6 | 7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    AnalysisFailed instanceAffectedCluster nameCluster aliasDetect time
    22 |
      23 | 24 | 25 |
    26 |
    27 |
    28 |
    29 | 30 | 31 | 40 | 41 | -------------------------------------------------------------------------------- /resources/templates/audit_recovery.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
      5 | 6 | 7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
    AnalysisFailed instanceAffectedCluster nameCluster aliasStart timeEnd timeSuccessor instance
    24 |
      25 | 26 | 27 |
    28 |
    29 |
    30 |
    31 | 32 | 33 | 46 | 47 | -------------------------------------------------------------------------------- /resources/templates/cluster.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 32 |
    33 |
    34 |
    35 |
    36 | 37 |
  • 51 | 52 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /resources/templates/cluster_pools.tmpl: -------------------------------------------------------------------------------- 1 | 16 | 17 |
    18 |
    19 | 20 | 21 | 30 | 31 | -------------------------------------------------------------------------------- /resources/templates/clusters.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/templates/clusters_analysis.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/templates/discover.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | Discover a new instance 5 |
    6 |
    7 |
    8 |

    9 | Enter a MySQL hostname and a port. Orchestrator will try and resolve the CNAME. 10 |

    11 |

    12 | If running in discovery mode, orchestrator will also attempt to discover the entire topology the instance belongs to. 13 |

    14 |
    15 | 16 |
    17 | 18 | 19 | 20 |
    21 |
    22 |
    23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /resources/templates/home.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |

    Orchestrator

    3 |

    4 | Welcome! Orchestrator is your MySQL replication visualization and management tool. 5 |

    6 |
    7 |

    8 | Orchestrator is familiar with replication cluster(s) at this moment. Click "Clusters" on top navigation bar too see them. If you have more replication clusters you wish to visualize and manage, please let orchestrator know about them by going to the discovery page. 9 |

    10 |

    11 | Orchestrator is a multi-tiered application. It executes as a command line tool - you can move slaves around the topology and do most other stuff. It also serves as Web API. In fact, there is little to the web interface you're using right 12 | now - it's mostly client cosmetics; all operations in this web interface are based on web API calls. 13 |

    14 |
    15 |
    16 |

    17 | It seems like this is your initial installation of orchestrator: there are no known clusters at this stage. 18 |

    19 |

    20 | Your next task is to let orchestrator know about your replication topologies. Pick one server from each topology (this could be either master or slave). Orchestrator will attempt to connect to such a server, and auto-discover the entire 21 | replication tree by recursively crawling the server's master and slaves. 22 |

    23 |

    24 | Once discovered (and allow for a few minutes for complete replication graph detection), you will be able to easily change your topology via drag & drop, start & stop your slaves, start/end maintenance modes and get quick insight into replication problems. 25 |

    26 |

    27 | Make sure your configuration file has the proper credentials for accessing all those remote MySQL servers. You will need the SUPER and PROCESS privileges on any node you wish to be able to discover. Make sure to run the 28 | following on masters of your topologies; fill in your own host name (limit '%' to a specific host), user and password: 29 |

    30 | GRANT SUPER, PROCESS ON *.* TO 'orchestrator'@'%' IDENTIFIED BY 'secret';
    31 | 
    32 |

    33 |

    34 | Start discovery now 35 |

    36 |
    37 |
    38 | 39 | 40 | 41 | 55 | -------------------------------------------------------------------------------- /resources/templates/keep-calm.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 |
    5 |
    6 | -------------------------------------------------------------------------------- /resources/templates/long_queries.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 |
    7 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /resources/templates/search.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /resources/templates/seeds.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 |
    7 |
    8 | Seeds 9 |
    10 |
    11 |
    12 |
    13 | Seed details 14 |
    15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    StatusIdTarget hostSource hostSeed start timeSeed end time
    28 |
    29 |
    30 |
    31 |
    32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /resources/templates/status.tmpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | Status 5 |
    6 |
    7 | 8 |
    9 |
    10 | 12 |
    13 |
    14 | 15 | 16 | -------------------------------------------------------------------------------- /vagrant/README.md: -------------------------------------------------------------------------------- 1 | Orchestrator Vagrant Instructions 2 | ================================= 3 | 4 | Orchestrator's Vagrant defaults to installing five (5) CentOS 6.6 boxes in the following replication topology, with a separate "admin" node: 5 | 6 | ``` 7 | db1<->db2 8 | | | 9 | v v 10 | db3 db4 11 | ``` 12 | 13 | It is possible to override what gets installed by the use of environmental variables: 14 | 15 | ``` 16 | VAGRANT_SERVER_URL defaults to 'https://atlas.hashicorp.com' 17 | VAGRANT_DEFAULT_PROVIDER defaults to 'virtualbox' 18 | VAGRANT_BOX defaults to 'nrel/CentOS-6.6-x86_64 19 | ``` 20 | 21 | The MySQL configuration is such that it is the minimum required to set up replication. For testing such features as delayed replication, RBR/SBR, GTID, it is simply a matter of editing the `vagrant/dbX-my.cnf` before running the `vagrant up` command. 22 | 23 | FAQ 24 | === 25 | 26 | Q: By default, there are still a lot of steps that I have to do within each virtual machine to get going 27 | 28 | A: That is by design. Vagrant will execute `db-post-install.sh`, `dbX-post-install.sh`, and `admin-post-install.sh` in the `vagrant/` directory (they are `.gitignore`'d) for any custom work that you want to have done (i.e. build Orchestrator, etc etc) 29 | 30 | Q: I run some other distribution of Linux. Why don't you support that? 31 | 32 | A: Pull Requests are welcome! If you update any of the `vagrant/*.sh` scripts, they must work with at least CentOS 6 and Ubuntu 12 33 | 34 | Tips & Tricks 35 | ============= 36 | 37 | Specify GTID Usage 38 | ------------------ 39 | 40 | If you want to use GTID Replication, you must update all of the `vagrant/dbX-my.cnf` files with the following options in `[mysqld]`: 41 | 42 | ``` 43 | enforce-gtid-consistency 44 | gtid-mode=ON 45 | ``` 46 | 47 | Specify RBR/SBR 48 | --------------- 49 | 50 | `vagrant/dbX-my.cnf` files are copied directly to the virtual machines. If you'd like to specify SBR/RBR/MIXED, add one of the following lines to the `[mysqld]` section of the `my.cnf` template: 51 | 52 | ``` 53 | binlog_format=MIXED 54 | binlog_format=STATEMENT 55 | binlog_format=ROW 56 | ``` 57 | 58 | This is not global because we want to be able to test out non-standard replication configurations. 59 | 60 | Use VMWare vs. VirtualBox 61 | ------------------------- 62 | 63 | ``` 64 | %> export VAGRANT_DEFAULT_PROVIDER='vmware_fusion' 65 | %> vagrant up 66 | ``` 67 | 68 | CentOS 69 | ------ 70 | 71 | This is the default. Nothing special is required: 72 | 73 | ``` 74 | %> vagrant up 75 | ``` 76 | 77 | Ubuntu 78 | ------ 79 | 80 | ``` 81 | %> export VAGRANT_SERVER_URL="https://atlas.hashicorp.com" 82 | %> export VAGRANT_BOX='chef/ubuntu-12.04' 83 | %> vagrant up 84 | ``` 85 | 86 | TO DO 87 | ===== 88 | 89 | - Support other MySQL's (5.5, 5.7, MariaDB) 90 | - Support customizable replication configurations 91 | - Better `my.cnf` templates 92 | 93 | -------------------------------------------------------------------------------- /vagrant/admin-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install orchestrator 4 | rpm -i /tmp/orchestrator-release/orchestrator*.rpm 5 | /sbin/chkconfig orchestrator on 6 | cp /usr/local/orchestrator/orchestrator-sample.conf.json /etc/orchestrator.conf.json 7 | /sbin/service orchestrator start 8 | 9 | echo '* * * * * root /usr/bin/orchestrator -c discover -i db1' > /etc/cron.d/orchestrator-discovery 10 | 11 | # Discover instances 12 | /usr/bin/orchestrator -c discover -i localhost -------------------------------------------------------------------------------- /vagrant/db1-build.sh: -------------------------------------------------------------------------------- 1 | if [[ -e /etc/debian_version ]]; then 2 | sudo cp /orchestrator/vagrant/db1-my.cnf /etc/mysql/my.cnf 3 | sudo /etc/init.d/mysql restart 4 | fi 5 | 6 | /usr/bin/mysql -uroot -ss -e 'GRANT REPLICATION SLAVE ON *.* TO "repl"@"192.168.57.%" IDENTIFIED BY "vagrant_repl"' 7 | /usr/bin/mysql -uroot -ss -e 'CHANGE MASTER TO MASTER_HOST="192.168.57.202", MASTER_USER="repl", MASTER_PASSWORD="vagrant_repl", MASTER_CONNECT_RETRY=10, MASTER_RETRY_COUNT=36' 8 | /usr/bin/mysql -uroot -ss -e 'START SLAVE' 9 | -------------------------------------------------------------------------------- /vagrant/db1-my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | datadir=/var/lib/mysql 3 | user=mysql 4 | # Disabling symbolic-links is recommended to prevent assorted security risks 5 | symbolic-links=0 6 | server_id=1 7 | log_bin 8 | log-slave-updates 9 | report-host=db1 10 | 11 | [mysqld_safe] 12 | log-error=/var/log/mysqld.log 13 | pid-file=/var/run/mysqld/mysqld.pid 14 | -------------------------------------------------------------------------------- /vagrant/db2-build.sh: -------------------------------------------------------------------------------- 1 | if [[ -e /etc/debian_version ]]; then 2 | sudo cp /orchestrator/vagrant/db1-my.cnf /etc/mysql/my.cnf 3 | sudo /etc/init.d/mysql restart 4 | fi 5 | 6 | /usr/bin/mysql -uroot -ss -e 'GRANT REPLICATION SLAVE ON *.* TO "repl"@"192.168.57.%" IDENTIFIED BY "vagrant_repl"' 7 | /usr/bin/mysql -uroot -ss -e 'CHANGE MASTER TO MASTER_HOST="192.168.57.201", MASTER_USER="repl", MASTER_PASSWORD="vagrant_repl", MASTER_CONNECT_RETRY=10, MASTER_RETRY_COUNT=36' 8 | /usr/bin/mysql -uroot -ss -e 'START SLAVE' 9 | -------------------------------------------------------------------------------- /vagrant/db2-my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | datadir=/var/lib/mysql 3 | user=mysql 4 | # Disabling symbolic-links is recommended to prevent assorted security risks 5 | symbolic-links=0 6 | server_id=2 7 | log_bin 8 | log-slave-updates 9 | report-host=db2 10 | 11 | [mysqld_safe] 12 | log-error=/var/log/mysqld.log 13 | pid-file=/var/run/mysqld/mysqld.pid 14 | -------------------------------------------------------------------------------- /vagrant/db3-build.sh: -------------------------------------------------------------------------------- 1 | if [[ -e /etc/debian_version ]]; then 2 | sudo cp /orchestrator/vagrant/db1-my.cnf /etc/mysql/my.cnf 3 | sudo /etc/init.d/mysql restart 4 | fi 5 | 6 | /usr/bin/mysql -uroot -ss -e 'GRANT REPLICATION SLAVE ON *.* TO "repl"@"192.168.57.%" IDENTIFIED BY "vagrant_repl"' 7 | /usr/bin/mysql -uroot -ss -e 'CHANGE MASTER TO MASTER_HOST="192.168.57.201", MASTER_USER="repl", MASTER_PASSWORD="vagrant_repl", MASTER_CONNECT_RETRY=10, MASTER_RETRY_COUNT=36' 8 | /usr/bin/mysql -uroot -ss -e 'START SLAVE' 9 | -------------------------------------------------------------------------------- /vagrant/db3-my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | datadir=/var/lib/mysql 3 | user=mysql 4 | # Disabling symbolic-links is recommended to prevent assorted security risks 5 | symbolic-links=0 6 | server_id=3 7 | log_bin 8 | log-slave-updates 9 | report-host=db3 10 | 11 | [mysqld_safe] 12 | log-error=/var/log/mysqld.log 13 | pid-file=/var/run/mysqld/mysqld.pid 14 | -------------------------------------------------------------------------------- /vagrant/db4-build.sh: -------------------------------------------------------------------------------- 1 | if [[ -e /etc/debian_version ]]; then 2 | sudo cp /orchestrator/vagrant/db1-my.cnf /etc/mysql/my.cnf 3 | sudo /etc/init.d/mysql restart 4 | fi 5 | 6 | /usr/bin/mysql -uroot -ss -e 'GRANT REPLICATION SLAVE ON *.* TO "repl"@"192.168.57.%" IDENTIFIED BY "vagrant_repl"' 7 | /usr/bin/mysql -uroot -ss -e 'CHANGE MASTER TO MASTER_HOST="192.168.57.202", MASTER_USER="repl", MASTER_PASSWORD="vagrant_repl", MASTER_CONNECT_RETRY=10, MASTER_RETRY_COUNT=36' 8 | /usr/bin/mysql -uroot -ss -e 'START SLAVE' 9 | -------------------------------------------------------------------------------- /vagrant/db4-my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | datadir=/var/lib/mysql 3 | user=mysql 4 | # Disabling symbolic-links is recommended to prevent assorted security risks 5 | symbolic-links=0 6 | server_id=4 7 | log_bin 8 | log-slave-updates 9 | report-host=db4 10 | 11 | [mysqld_safe] 12 | log-error=/var/log/mysqld.log 13 | pid-file=/var/run/mysqld/mysqld.pid 14 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/.gitignore: -------------------------------------------------------------------------------- 1 | inject 2 | inject.test 3 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/translations/README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # inject 2 | -- 3 | import "github.com/codegangsta/inject" 4 | 5 | inject包提供了多种对实体的映射和依赖注入方式。 6 | 7 | ## 用法 8 | 9 | #### func InterfaceOf 10 | 11 | ```go 12 | func InterfaceOf(value interface{}) reflect.Type 13 | ``` 14 | 函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。 15 | 16 | #### type Applicator 17 | 18 | ```go 19 | type Applicator interface { 20 | // 在Type map中维持对结构体中每个域的引用并用'inject'来标记 21 | // 如果注入失败将会返回一个error. 22 | Apply(interface{}) error 23 | } 24 | ``` 25 | 26 | Applicator接口表示到结构体的依赖映射关系。 27 | 28 | #### type Injector 29 | 30 | ```go 31 | type Injector interface { 32 | Applicator 33 | Invoker 34 | TypeMapper 35 | // SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖, 36 | // 将会继续从它的父injector中找,直到返回error. 37 | SetParent(Injector) 38 | } 39 | ``` 40 | 41 | Injector接口表示对结构体、函数参数的映射和依赖注入。 42 | 43 | #### func New 44 | 45 | ```go 46 | func New() Injector 47 | ``` 48 | New创建并返回一个Injector. 49 | 50 | #### type Invoker 51 | 52 | ```go 53 | type Invoker interface { 54 | // Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。 55 | // 它将返回reflect.Value的切片,其中存放原函数的返回值。 56 | // 如果注入失败则返回error. 57 | Invoke(interface{}) ([]reflect.Value, error) 58 | } 59 | ``` 60 | 61 | Invoker接口表示通过反射进行函数调用。 62 | 63 | #### type TypeMapper 64 | 65 | ```go 66 | type TypeMapper interface { 67 | // 基于调用reflect.TypeOf得到的类型映射interface{}的值。 68 | Map(interface{}) TypeMapper 69 | // 基于提供的接口的指针映射interface{}的值。 70 | // 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。 71 | MapTo(interface{}, interface{}) TypeMapper 72 | // 为直接插入基于类型和值的map提供一种可能性。 73 | // 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。 74 | Set(reflect.Type, reflect.Value) TypeMapper 75 | // 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。 76 | Get(reflect.Type) reflect.Value 77 | } 78 | ``` 79 | 80 | TypeMapper接口用来表示基于类型到接口值的映射。 81 | 82 | 83 | ## 译者 84 | 85 | 张强 (qqbunny@yeah.net) -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/update_readme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go get github.com/robertkrimen/godocdown/godocdown 3 | godocdown > README.md 4 | -------------------------------------------------------------------------------- /vendor/github.com/cyberdelia/go-metrics-graphite/AUTHORS: -------------------------------------------------------------------------------- 1 | These people have provided bug fixes, new features or improved the documentation. 2 | 3 | * Daniel Garcia 4 | * Peter Teichman 5 | * Phillip Kovalev 6 | * Richard Crowley 7 | * Timothée Peignier 8 | * Tomás Senart 9 | -------------------------------------------------------------------------------- /vendor/github.com/cyberdelia/go-metrics-graphite/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Timothée Peignier. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/cyberdelia/go-metrics-graphite/README.md: -------------------------------------------------------------------------------- 1 | This is a reporter for the [go-metrics](https://github.com/rcrowley/go-metrics) 2 | library which will post the metrics to Graphite. It was originally part of the 3 | `go-metrics` library itself, but has been split off to make maintenance of 4 | both the core library and the client easier. 5 | 6 | ### Usage 7 | 8 | ```go 9 | import "github.com/cyberdelia/go-metrics-graphite" 10 | 11 | 12 | go graphite.Graphite(metrics.DefaultRegistry, 13 | 1*time.Second, "some.prefix", addr) 14 | ``` 15 | 16 | ### Migrating from `rcrowley/go-metrics` implementation 17 | 18 | Simply modify the import from `"github.com/rcrowley/go-metrics/librato"` to 19 | `"github.com/cyberdelia/go-metrics-graphite"` and it should Just Work. 20 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | 25 | /.godeps 26 | /.envrc 27 | 28 | # Godeps 29 | Godeps/_workspace 30 | Godeps/Readme 31 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/go-martini/martini", 3 | "GoVersion": "go1.4.2", 4 | "Deps": [ 5 | { 6 | "ImportPath": "github.com/codegangsta/inject", 7 | "Comment": "v1.0-rc1-10-g33e0aa1", 8 | "Rev": "33e0aa1cb7c019ccc3fbe049a8262a6403d30504" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/env.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // Envs 8 | const ( 9 | Dev string = "development" 10 | Prod string = "production" 11 | Test string = "test" 12 | ) 13 | 14 | // Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable. 15 | var Env = Dev 16 | var Root string 17 | 18 | func setENV(e string) { 19 | if len(e) > 0 { 20 | Env = e 21 | } 22 | } 23 | 24 | func init() { 25 | setENV(os.Getenv("MARTINI_ENV")) 26 | var err error 27 | Root, err = os.Getwd() 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/go_version.go: -------------------------------------------------------------------------------- 1 | // +build !go1.1 2 | 3 | package martini 4 | 5 | func MartiniDoesNotSupportGo1Point0() { 6 | "Martini requires Go 1.1 or greater." 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/logger.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. 10 | func Logger() Handler { 11 | return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) { 12 | start := time.Now() 13 | 14 | addr := req.Header.Get("X-Real-IP") 15 | if addr == "" { 16 | addr = req.Header.Get("X-Forwarded-For") 17 | if addr == "" { 18 | addr = req.RemoteAddr 19 | } 20 | } 21 | 22 | log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr) 23 | 24 | rw := res.(ResponseWriter) 25 | c.Next() 26 | 27 | log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/return_handler.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "github.com/codegangsta/inject" 5 | "net/http" 6 | "reflect" 7 | ) 8 | 9 | // ReturnHandler is a service that Martini provides that is called 10 | // when a route handler returns something. The ReturnHandler is 11 | // responsible for writing to the ResponseWriter based on the values 12 | // that are passed into this function. 13 | type ReturnHandler func(Context, []reflect.Value) 14 | 15 | func defaultReturnHandler() ReturnHandler { 16 | return func(ctx Context, vals []reflect.Value) { 17 | rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) 18 | res := rv.Interface().(http.ResponseWriter) 19 | var responseVal reflect.Value 20 | if len(vals) > 1 && vals[0].Kind() == reflect.Int { 21 | res.WriteHeader(int(vals[0].Int())) 22 | responseVal = vals[1] 23 | } else if len(vals) > 0 { 24 | responseVal = vals[0] 25 | } 26 | if canDeref(responseVal) { 27 | responseVal = responseVal.Elem() 28 | } 29 | if isByteSlice(responseVal) { 30 | res.Write(responseVal.Bytes()) 31 | } else { 32 | res.Write([]byte(responseVal.String())) 33 | } 34 | } 35 | } 36 | 37 | func isByteSlice(val reflect.Value) bool { 38 | return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 39 | } 40 | 41 | func canDeref(val reflect.Value) bool { 42 | return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go-MySQL-Driver authors for copyright purposes. 2 | 3 | # If you are submitting a patch, please add your name or the name of the 4 | # organization which holds the copyright to this list in alphabetical order. 5 | 6 | # Names should be added to this file as 7 | # Name 8 | # The email address is not required for organizations. 9 | # Please keep the list sorted. 10 | 11 | 12 | # Individual Persons 13 | 14 | Aaron Hopkins 15 | Arne Hormann 16 | Carlos Nieto 17 | Chris Moos 18 | Daniel Nichter 19 | Daniël van Eeden 20 | DisposaBoy 21 | Frederick Mayle 22 | Gustavo Kristic 23 | Hanno Braun 24 | Henri Yandell 25 | Hirotaka Yamamoto 26 | INADA Naoki 27 | James Harr 28 | Jian Zhen 29 | Joshua Prunier 30 | Julien Lefevre 31 | Julien Schmidt 32 | Kamil Dziedzic 33 | Kevin Malachowski 34 | Leonardo YongUk Kim 35 | Luca Looz 36 | Lucas Liu 37 | Luke Scott 38 | Michael Woolnough 39 | Nicola Peduzzi 40 | Paul Bonser 41 | Runrioter Wung 42 | Soroush Pour 43 | Stan Putrya 44 | Stanley Gunawan 45 | Xiaobing Jiang 46 | Xiuming Chen 47 | 48 | # Organizations 49 | 50 | Barracuda Networks, Inc. 51 | Google Inc. 52 | Stripe Inc. 53 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | ## Reporting Issues 4 | 5 | Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed). 6 | 7 | ## Contributing Code 8 | 9 | By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file. 10 | Don't forget to add yourself to the AUTHORS file. 11 | 12 | ### Code Review 13 | 14 | Everyone is invited to review and comment on pull requests. 15 | If it looks fine to you, comment with "LGTM" (Looks good to me). 16 | 17 | If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes. 18 | 19 | Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM". 20 | 21 | ## Development Ideas 22 | 23 | If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page. 24 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Issue description 2 | Tell us what should happen and what happens instead 3 | 4 | ### Example code 5 | ```go 6 | If possible, please enter some example code here to reproduce the issue. 7 | ``` 8 | 9 | ### Error log 10 | ``` 11 | If you have an error log, please paste it here. 12 | ``` 13 | 14 | ### Configuration 15 | *Driver version (or git SHA):* 16 | 17 | *Go version:* run `go version` in your console 18 | 19 | *Server version:* E.g. MySQL 5.6, MariaDB 10.0.20 20 | 21 | *Server OS:* E.g. Debian 8.1 (Jessie), Windows 10 22 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | Please explain the changes you made here. 3 | 4 | ### Checklist 5 | - [ ] Code compiles correctly 6 | - [ ] Created tests which fail without the change (if possible) 7 | - [ ] All tests passing 8 | - [ ] Extended the README / documentation, if necessary 9 | - [ ] Added myself / the copyright holder to the AUTHORS file 10 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/appengine.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | // +build appengine 10 | 11 | package mysql 12 | 13 | import ( 14 | "appengine/cloudsql" 15 | ) 16 | 17 | func init() { 18 | RegisterDial("cloudsql", cloudsql.Dial) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/errors_test.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "bytes" 13 | "log" 14 | "testing" 15 | ) 16 | 17 | func TestErrorsSetLogger(t *testing.T) { 18 | previous := errLog 19 | defer func() { 20 | errLog = previous 21 | }() 22 | 23 | // set up logger 24 | const expected = "prefix: test\n" 25 | buffer := bytes.NewBuffer(make([]byte, 0, 64)) 26 | logger := log.New(buffer, "prefix: ", 0) 27 | 28 | // print 29 | SetLogger(logger) 30 | errLog.Print("test") 31 | 32 | // check result 33 | if actual := buffer.String(); actual != expected { 34 | t.Errorf("expected %q, got %q", expected, actual) 35 | } 36 | } 37 | 38 | func TestErrorsStrictIgnoreNotes(t *testing.T) { 39 | runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) { 40 | dbt.mustExec("DROP TABLE IF EXISTS does_not_exist") 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/result.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlResult struct { 12 | affectedRows int64 13 | insertId int64 14 | } 15 | 16 | func (res *mysqlResult) LastInsertId() (int64, error) { 17 | return res.insertId, nil 18 | } 19 | 20 | func (res *mysqlResult) RowsAffected() (int64, error) { 21 | return res.affectedRows, nil 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/rows.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "io" 14 | ) 15 | 16 | type mysqlField struct { 17 | tableName string 18 | name string 19 | flags fieldFlag 20 | fieldType byte 21 | decimals byte 22 | } 23 | 24 | type mysqlRows struct { 25 | mc *mysqlConn 26 | columns []mysqlField 27 | } 28 | 29 | type binaryRows struct { 30 | mysqlRows 31 | } 32 | 33 | type textRows struct { 34 | mysqlRows 35 | } 36 | 37 | type emptyRows struct{} 38 | 39 | func (rows *mysqlRows) Columns() []string { 40 | columns := make([]string, len(rows.columns)) 41 | if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias { 42 | for i := range columns { 43 | if tableName := rows.columns[i].tableName; len(tableName) > 0 { 44 | columns[i] = tableName + "." + rows.columns[i].name 45 | } else { 46 | columns[i] = rows.columns[i].name 47 | } 48 | } 49 | } else { 50 | for i := range columns { 51 | columns[i] = rows.columns[i].name 52 | } 53 | } 54 | return columns 55 | } 56 | 57 | func (rows *mysqlRows) Close() error { 58 | mc := rows.mc 59 | if mc == nil { 60 | return nil 61 | } 62 | if mc.netConn == nil { 63 | return ErrInvalidConn 64 | } 65 | 66 | // Remove unread packets from stream 67 | err := mc.readUntilEOF() 68 | if err == nil { 69 | if err = mc.discardResults(); err != nil { 70 | return err 71 | } 72 | } 73 | 74 | rows.mc = nil 75 | return err 76 | } 77 | 78 | func (rows *binaryRows) Next(dest []driver.Value) error { 79 | if mc := rows.mc; mc != nil { 80 | if mc.netConn == nil { 81 | return ErrInvalidConn 82 | } 83 | 84 | // Fetch next row from stream 85 | return rows.readRow(dest) 86 | } 87 | return io.EOF 88 | } 89 | 90 | func (rows *textRows) Next(dest []driver.Value) error { 91 | if mc := rows.mc; mc != nil { 92 | if mc.netConn == nil { 93 | return ErrInvalidConn 94 | } 95 | 96 | // Fetch next row from stream 97 | return rows.readRow(dest) 98 | } 99 | return io.EOF 100 | } 101 | 102 | func (rows emptyRows) Columns() []string { 103 | return nil 104 | } 105 | 106 | func (rows emptyRows) Close() error { 107 | return nil 108 | } 109 | 110 | func (rows emptyRows) Next(dest []driver.Value) error { 111 | return io.EOF 112 | } 113 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/transaction.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlTx struct { 12 | mc *mysqlConn 13 | } 14 | 15 | func (tx *mysqlTx) Commit() (err error) { 16 | if tx.mc == nil || tx.mc.netConn == nil { 17 | return ErrInvalidConn 18 | } 19 | err = tx.mc.exec("COMMIT") 20 | tx.mc = nil 21 | return 22 | } 23 | 24 | func (tx *mysqlTx) Rollback() (err error) { 25 | if tx.mc == nil || tx.mc.netConn == nil { 26 | return ErrInvalidConn 27 | } 28 | err = tx.mc.exec("ROLLBACK") 29 | tx.mc = nil 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/howeyc/gopass/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | go: 8 | - 1.3 9 | - 1.4 10 | - 1.5 11 | - tip 12 | -------------------------------------------------------------------------------- /vendor/github.com/howeyc/gopass/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Chris Howey 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /vendor/github.com/howeyc/gopass/README.md: -------------------------------------------------------------------------------- 1 | # getpasswd in Go [![GoDoc](https://godoc.org/github.com/howeyc/gopass?status.svg)](https://godoc.org/github.com/howeyc/gopass) [![Build Status](https://secure.travis-ci.org/howeyc/gopass.png?branch=master)](http://travis-ci.org/howeyc/gopass) 2 | 3 | Retrieve password from user terminal or piped input without echo. 4 | 5 | Verified on BSD, Linux, and Windows. 6 | 7 | Example: 8 | ```go 9 | package main 10 | 11 | import "fmt" 12 | import "github.com/howeyc/gopass" 13 | 14 | func main() { 15 | fmt.Printf("Password: ") 16 | 17 | // Silent. For printing *'s use gopass.GetPasswdMasked() 18 | pass, err := gopass.GetPasswd() 19 | if err != nil { 20 | // Handle gopass.ErrInterrupted or getch() read error 21 | } 22 | 23 | // Do something with pass 24 | } 25 | ``` 26 | 27 | Caution: Multi-byte characters not supported! 28 | -------------------------------------------------------------------------------- /vendor/github.com/howeyc/gopass/pass.go: -------------------------------------------------------------------------------- 1 | package gopass 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "os" 8 | 9 | "golang.org/x/crypto/ssh/terminal" 10 | ) 11 | 12 | var defaultGetCh = func() (byte, error) { 13 | buf := make([]byte, 1) 14 | if n, err := os.Stdin.Read(buf); n == 0 || err != nil { 15 | if err != nil { 16 | return 0, err 17 | } 18 | return 0, io.EOF 19 | } 20 | return buf[0], nil 21 | } 22 | 23 | var ( 24 | ErrInterrupted = errors.New("Interrupted") 25 | 26 | // Provide variable so that tests can provide a mock implementation. 27 | getch = defaultGetCh 28 | ) 29 | 30 | // getPasswd returns the input read from terminal. 31 | // If masked is true, typing will be matched by asterisks on the screen. 32 | // Otherwise, typing will echo nothing. 33 | func getPasswd(masked bool) ([]byte, error) { 34 | var err error 35 | var pass, bs, mask []byte 36 | if masked { 37 | bs = []byte("\b \b") 38 | mask = []byte("*") 39 | } 40 | 41 | if terminal.IsTerminal(int(os.Stdin.Fd())) { 42 | if oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())); err != nil { 43 | return pass, err 44 | } else { 45 | defer terminal.Restore(int(os.Stdin.Fd()), oldState) 46 | } 47 | } 48 | 49 | for { 50 | if v, e := getch(); e != nil { 51 | err = e 52 | break 53 | } else if v == 127 || v == 8 { 54 | if l := len(pass); l > 0 { 55 | pass = pass[:l-1] 56 | fmt.Print(string(bs)) 57 | } 58 | } else if v == 13 || v == 10 { 59 | break 60 | } else if v == 3 { 61 | err = ErrInterrupted 62 | break 63 | } else if v != 0 { 64 | pass = append(pass, v) 65 | fmt.Print(string(mask)) 66 | } 67 | } 68 | fmt.Println() 69 | return pass, err 70 | } 71 | 72 | // GetPasswd returns the password read from the terminal without echoing input. 73 | // The returned byte array does not include end-of-line characters. 74 | func GetPasswd() ([]byte, error) { 75 | return getPasswd(false) 76 | } 77 | 78 | // GetPasswdMasked returns the password read from the terminal, echoing asterisks. 79 | // The returned byte array does not include end-of-line characters. 80 | func GetPasswdMasked() ([]byte, error) { 81 | return getPasswd(true) 82 | } 83 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/README.md: -------------------------------------------------------------------------------- 1 | # auth [![wercker status](https://app.wercker.com/status/8e5237b01b52f169a1274fad9a89617b "wercker status")](https://app.wercker.com/project/bykey/8e5237b01b52f169a1274fad9a89617b) 2 | Martini middleware/handler for http basic authentication. 3 | 4 | [API Reference](http://godoc.org/github.com/martini-contrib/auth) 5 | 6 | ## Simple Usage 7 | 8 | Use `auth.Basic` to authenticate against a pre-defined username and password: 9 | 10 | ~~~ go 11 | import ( 12 | "github.com/go-martini/martini" 13 | "github.com/martini-contrib/auth" 14 | ) 15 | 16 | func main() { 17 | m := martini.Classic() 18 | // authenticate every request 19 | m.Use(auth.Basic("username", "secretpassword")) 20 | m.Run() 21 | } 22 | ~~~ 23 | 24 | ## Advanced Usage 25 | 26 | Using `auth.BasicFunc` lets you authenticate on a per-user level, by checking 27 | the username and password in the callback function: 28 | 29 | ~~~ go 30 | import ( 31 | "github.com/go-martini/martini" 32 | "github.com/martini-contrib/auth" 33 | ) 34 | 35 | func main() { 36 | m := martini.Classic() 37 | // authenticate every request 38 | m.Use(auth.BasicFunc(func(username, password string) bool { 39 | return username == "admin" && password == "guessme" 40 | })) 41 | m.Run() 42 | } 43 | ~~~ 44 | 45 | Note that checking usernames and passwords with string comparison might be 46 | susceptible to timing attacks. To avoid that, use `auth.SecureCompare` instead: 47 | 48 | ~~~ go 49 | m.Use(auth.BasicFunc(func(username, password string) bool { 50 | return auth.SecureCompare(username, "admin") && auth.SecureCompare(password, "guessme") 51 | })) 52 | } 53 | ~~~ 54 | 55 | Upon successful authentication, the username is available to all subsequent 56 | handlers via the `auth.User` type: 57 | 58 | ~~~ go 59 | m.Get("/", func(user auth.User) string { 60 | return "Welcome, " + string(user) 61 | }) 62 | } 63 | ~~~ 64 | 65 | ## Authors 66 | * [Jeremy Saenz](http://github.com/codegangsta) 67 | * [Brendon Murphy](http://github.com/bemurphy) 68 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/basic.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "encoding/base64" 5 | "github.com/go-martini/martini" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | // User is the authenticated username that was extracted from the request. 11 | type User string 12 | 13 | // BasicRealm is used when setting the WWW-Authenticate response header. 14 | var BasicRealm = "Authorization Required" 15 | 16 | // Basic returns a Handler that authenticates via Basic Auth. Writes a http.StatusUnauthorized 17 | // if authentication fails. 18 | func Basic(username string, password string) martini.Handler { 19 | var siteAuth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 20 | return func(res http.ResponseWriter, req *http.Request, c martini.Context) { 21 | auth := req.Header.Get("Authorization") 22 | if !SecureCompare(auth, "Basic "+siteAuth) { 23 | unauthorized(res) 24 | return 25 | } 26 | c.Map(User(username)) 27 | } 28 | } 29 | 30 | // BasicFunc returns a Handler that authenticates via Basic Auth using the provided function. 31 | // The function should return true for a valid username/password combination. 32 | func BasicFunc(authfn func(string, string) bool) martini.Handler { 33 | return func(res http.ResponseWriter, req *http.Request, c martini.Context) { 34 | auth := req.Header.Get("Authorization") 35 | if len(auth) < 6 || auth[:6] != "Basic " { 36 | unauthorized(res) 37 | return 38 | } 39 | b, err := base64.StdEncoding.DecodeString(auth[6:]) 40 | if err != nil { 41 | unauthorized(res) 42 | return 43 | } 44 | tokens := strings.SplitN(string(b), ":", 2) 45 | if len(tokens) != 2 || !authfn(tokens[0], tokens[1]) { 46 | unauthorized(res) 47 | return 48 | } 49 | c.Map(User(tokens[0])) 50 | } 51 | } 52 | 53 | func unauthorized(res http.ResponseWriter) { 54 | res.Header().Set("WWW-Authenticate", "Basic realm=\""+BasicRealm+"\"") 55 | http.Error(res, "Not Authorized", http.StatusUnauthorized) 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/util.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/subtle" 6 | ) 7 | 8 | // SecureCompare performs a constant time compare of two strings to limit timing attacks. 9 | func SecureCompare(given string, actual string) bool { 10 | givenSha := sha256.Sum256([]byte(given)) 11 | actualSha := sha256.Sum256([]byte(actual)) 12 | 13 | return subtle.ConstantTimeCompare(givenSha[:], actualSha[:]) == 1 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/gzip/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/gzip/README.md: -------------------------------------------------------------------------------- 1 | # gzip [![wercker status](https://app.wercker.com/status/186d65e4d8160cf274ffc5835e6d9795 "wercker status")](https://app.wercker.com/project/bykey/186d65e4d8160cf274ffc5835e6d9795) 2 | Gzip middleware for Martini. 3 | 4 | [API Reference](http://godoc.org/github.com/martini-contrib/gzip) 5 | 6 | ## Usage 7 | 8 | ~~~ go 9 | import ( 10 | "github.com/go-martini/martini" 11 | "github.com/martini-contrib/gzip" 12 | ) 13 | 14 | func main() { 15 | m := martini.Classic() 16 | // gzip every request 17 | m.Use(gzip.All()) 18 | m.Run() 19 | } 20 | 21 | ~~~ 22 | 23 | Make sure to include the Gzip middleware above other middleware that alter the response body (like the render middleware). 24 | 25 | ## Changing compression level 26 | 27 | You can set compression level using gzip.Options: 28 | 29 | ~~~ go 30 | import ( 31 | "github.com/go-martini/martini" 32 | "github.com/martini-contrib/gzip" 33 | ) 34 | 35 | func main() { 36 | m := martini.Classic() 37 | // gzip every request with maximum compression level 38 | m.Use(gzip.All(gzip.Options{ 39 | CompressionLevel: gzip.BestCompression, 40 | })) 41 | m.Run() 42 | } 43 | ~~~ 44 | 45 | The compression level can be DefaultCompression or any integer value between BestSpeed and BestCompression inclusive. 46 | 47 | ## Authors 48 | * [Jeremy Saenz](http://github.com/codegangsta) 49 | * [Shane Logsdon](http://github.com/slogsdon) 50 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/gzip/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/admin/index.tmpl: -------------------------------------------------------------------------------- 1 |

    Admin {{.}}

    2 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/another_layout.tmpl: -------------------------------------------------------------------------------- 1 | another head 2 | {{ yield }} 3 | another foot 4 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/content.tmpl: -------------------------------------------------------------------------------- 1 |

    {{ . }}

    2 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/current_layout.tmpl: -------------------------------------------------------------------------------- 1 | {{ current }} head 2 | {{ yield }} 3 | {{ current }} foot 4 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/delims.tmpl: -------------------------------------------------------------------------------- 1 |

    Hello {[{.}]}

    -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/hello.tmpl: -------------------------------------------------------------------------------- 1 |

    Hello {{.}}

    2 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/hypertext.html: -------------------------------------------------------------------------------- 1 | Hypertext! 2 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/basic/layout.tmpl: -------------------------------------------------------------------------------- 1 | head 2 | {{ yield }} 3 | foot 4 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/fixtures/custom_funcs/index.tmpl: -------------------------------------------------------------------------------- 1 | {{ myCustomFunc }} 2 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/outbrain/golib/math/math.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Shlomi Noach. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package math 18 | 19 | func MinInt(i1, i2 int) int { 20 | if i1 < i2 { 21 | return i1 22 | } 23 | return i2 24 | } 25 | 26 | func MaxInt(i1, i2 int) int { 27 | if i1 > i2 { 28 | return i1 29 | } 30 | return i2 31 | } 32 | 33 | func MinInt64(i1, i2 int64) int64 { 34 | if i1 < i2 { 35 | return i1 36 | } 37 | return i2 38 | } 39 | 40 | func MaxInt64(i1, i2 int64) int64 { 41 | if i1 > i2 { 42 | return i1 43 | } 44 | return i2 45 | } 46 | 47 | func MinUInt(i1, i2 uint) uint { 48 | if i1 < i2 { 49 | return i1 50 | } 51 | return i2 52 | } 53 | 54 | func MaxUInt(i1, i2 uint) uint { 55 | if i1 > i2 { 56 | return i1 57 | } 58 | return i2 59 | } 60 | 61 | func MinUInt64(i1, i2 uint64) uint64 { 62 | if i1 < i2 { 63 | return i1 64 | } 65 | return i2 66 | } 67 | 68 | func MaxUInt64(i1, i2 uint64) uint64 { 69 | if i1 > i2 { 70 | return i1 71 | } 72 | return i2 73 | } 74 | 75 | func MinString(i1, i2 string) string { 76 | if i1 < i2 { 77 | return i1 78 | } 79 | return i2 80 | } 81 | 82 | func MaxString(i1, i2 string) string { 83 | if i1 > i2 { 84 | return i1 85 | } 86 | return i2 87 | } 88 | 89 | // TernaryString acts like a "? :" C-style ternary operator for strings 90 | func TernaryString(condition bool, resTrue string, resFalse string) string { 91 | if condition { 92 | return resTrue 93 | } 94 | return resFalse 95 | } 96 | 97 | // TernaryString acts like a "? :" C-style ternary operator for ints 98 | func TernaryInt(condition bool, resTrue int, resFalse int) int { 99 | if condition { 100 | return resTrue 101 | } 102 | return resFalse 103 | } 104 | 105 | // AbsInt is an ABS function for int type 106 | func AbsInt(i int) int { 107 | if i >= 0 { 108 | return i 109 | } 110 | return -i 111 | } 112 | 113 | // AbsInt64 is an ABS function for int64 type 114 | func AbsInt64(i int64) int64 { 115 | if i >= 0 { 116 | return i 117 | } 118 | return -i 119 | } 120 | -------------------------------------------------------------------------------- /vendor/github.com/outbrain/golib/tests/spec.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // Spec is an access point to test Expections 8 | type Spec struct { 9 | t *testing.T 10 | } 11 | 12 | // S generates a spec. You will want to use it once in a test file, once in a test or once per each check 13 | func S(t *testing.T) *Spec { 14 | return &Spec{t: t} 15 | } 16 | 17 | // ExpectNil expects given value to be nil, or errors 18 | func (spec *Spec) ExpectNil(actual interface{}) { 19 | if actual == nil { 20 | return 21 | } 22 | spec.t.Errorf("Expected %+v to be nil", actual) 23 | } 24 | 25 | // ExpectNotNil expects given value to be not nil, or errors 26 | func (spec *Spec) ExpectNotNil(actual interface{}) { 27 | if actual != nil { 28 | return 29 | } 30 | spec.t.Errorf("Expected %+v to be not nil", actual) 31 | } 32 | 33 | // ExpectEquals expects given values to be equal (comparison via `==`), or errors 34 | func (spec *Spec) ExpectEquals(actual, value interface{}) { 35 | if actual == value { 36 | return 37 | } 38 | spec.t.Errorf("Expected %+v, got %+v", value, actual) 39 | } 40 | 41 | // ExpectNotEquals expects given values to be nonequal (comparison via `==`), or errors 42 | func (spec *Spec) ExpectNotEquals(actual, value interface{}) { 43 | if !(actual == value) { 44 | return 45 | } 46 | spec.t.Errorf("Expected not %+v", value) 47 | } 48 | 49 | // ExpectEqualsAny expects given actual to equal (comparison via `==`) at least one of given values, or errors 50 | func (spec *Spec) ExpectEqualsAny(actual interface{}, values ...interface{}) { 51 | for _, value := range values { 52 | if actual == value { 53 | return 54 | } 55 | } 56 | spec.t.Errorf("Expected %+v to equal any of given values", actual) 57 | } 58 | 59 | // ExpectNotEqualsAny expects given actual to be nonequal (comparison via `==`)tp any of given values, or errors 60 | func (spec *Spec) ExpectNotEqualsAny(actual interface{}, values ...interface{}) { 61 | for _, value := range values { 62 | if actual == value { 63 | spec.t.Errorf("Expected not %+v", value) 64 | } 65 | } 66 | } 67 | 68 | // ExpectFalse expects given values to be false, or errors 69 | func (spec *Spec) ExpectFalse(actual interface{}) { 70 | spec.ExpectEquals(actual, false) 71 | } 72 | 73 | // ExpectTrue expects given values to be true, or errors 74 | func (spec *Spec) ExpectTrue(actual interface{}) { 75 | spec.ExpectEquals(actual, true) 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/outbrain/golib/util/text.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Shlomi Noach. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package util 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | "regexp" 23 | "strconv" 24 | ) 25 | 26 | // ParseSimpleTime parses input in the format 7s, 55m, 3h, 31d, 4w (second, minute, hour, day, week) 27 | // The time.ParseDuration() function should have done this, but it does not support "d" and "w" extensions. 28 | func SimpleTimeToSeconds(simpleTime string) (int, error) { 29 | if matched, _ := regexp.MatchString("^[0-9]+s$", simpleTime); matched { 30 | i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1]) 31 | return i, nil 32 | } 33 | if matched, _ := regexp.MatchString("^[0-9]+m$", simpleTime); matched { 34 | i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1]) 35 | return i * 60, nil 36 | } 37 | if matched, _ := regexp.MatchString("^[0-9]+h$", simpleTime); matched { 38 | i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1]) 39 | return i * 60 * 60, nil 40 | } 41 | if matched, _ := regexp.MatchString("^[0-9]+d$", simpleTime); matched { 42 | i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1]) 43 | return i * 60 * 60 * 24, nil 44 | } 45 | if matched, _ := regexp.MatchString("^[0-9]+w$", simpleTime); matched { 46 | i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1]) 47 | return i * 60 * 60 * 24 * 7, nil 48 | } 49 | return 0, errors.New(fmt.Sprintf("Cannot parse simple time: %s", simpleTime)) 50 | } 51 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/README.md: -------------------------------------------------------------------------------- 1 | # bpool [![GoDoc](https://godoc.org/github.com/oxtoacart/bpool?status.png)](https://godoc.org/github.com/oxtoacart/bpool) 2 | 3 | Package bpool implements leaky pools of byte arrays and Buffers as bounded channels. 4 | It is based on the leaky buffer example from the Effective Go documentation: http://golang.org/doc/effective_go.html#leaky_buffer 5 | 6 | bpool provides the following pool types: 7 | 8 | * [bpool.BufferPool](https://godoc.org/github.com/oxtoacart/bpool#BufferPool) 9 | which provides a fixed-size pool of 10 | [bytes.Buffers](http://golang.org/pkg/bytes/#Buffer). 11 | * [bpool.BytePool](https://godoc.org/github.com/oxtoacart/bpool#BytePool) which 12 | provides a fixed-size pool of `[]byte` slices with a pre-set width (length). 13 | * [bpool.SizedBufferPool](https://godoc.org/github.com/oxtoacart/bpool#SizedBufferPool), 14 | which is an alternative to `bpool.BufferPool` that pre-sizes the capacity of 15 | buffers issued from the pool and discards buffers that have grown too large 16 | upon return. 17 | 18 | A common use case for this package is to use buffers to execute HTML templates 19 | against (via ExecuteTemplate) or encode JSON into (via json.NewEncoder). This 20 | allows you to catch any rendering or marshalling errors prior to writing to a 21 | `http.ResponseWriter`, which helps to avoid writing incomplete or malformed data 22 | to the response. 23 | 24 | ## Install 25 | 26 | `go get github.com/oxtoacart/bpool` 27 | 28 | ## Documentation 29 | 30 | See [godoc.org](http://godoc.org/github.com/oxtoacart/bpool) or use `godoc github.com/oxtoacart/bpool` 31 | 32 | ## Example 33 | 34 | Here's a quick example for using `bpool.BufferPool`. We create a pool of the 35 | desired size, call the `Get()` method to obtain a buffer for use, and call 36 | `Put(buf)` to return the buffer to the pool. 37 | 38 | ```go 39 | 40 | var bufpool *bpool.BufferPool 41 | 42 | func main() { 43 | 44 | bufpool = bpool.NewBufferPool(48) 45 | 46 | } 47 | 48 | func someFunction() error { 49 | 50 | // Get a buffer from the pool 51 | buf := bufpool.Get() 52 | ... 53 | ... 54 | ... 55 | // Return the buffer to the pool 56 | bufpool.Put(buf) 57 | 58 | return nil 59 | } 60 | ``` 61 | 62 | ## License 63 | 64 | Apache 2.0 Licensed. See the LICENSE file for details. 65 | 66 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bpool.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package bpool implements leaky pools of byte arrays and Buffers as bounded 3 | channels. It is based on the leaky buffer example from the Effective Go 4 | documentation: http://golang.org/doc/effective_go.html#leaky_buffer 5 | */ 6 | package bpool 7 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bufferpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // BufferPool implements a pool of bytes.Buffers in the form of a bounded 8 | // channel. 9 | type BufferPool struct { 10 | c chan *bytes.Buffer 11 | } 12 | 13 | // NewBufferPool creates a new BufferPool bounded to the given size. 14 | func NewBufferPool(size int) (bp *BufferPool) { 15 | return &BufferPool{ 16 | c: make(chan *bytes.Buffer, size), 17 | } 18 | } 19 | 20 | // Get gets a Buffer from the BufferPool, or creates a new one if none are 21 | // available in the pool. 22 | func (bp *BufferPool) Get() (b *bytes.Buffer) { 23 | select { 24 | case b = <-bp.c: 25 | // reuse existing buffer 26 | default: 27 | // create new buffer 28 | b = bytes.NewBuffer([]byte{}) 29 | } 30 | return 31 | } 32 | 33 | // Put returns the given Buffer to the BufferPool. 34 | func (bp *BufferPool) Put(b *bytes.Buffer) { 35 | b.Reset() 36 | select { 37 | case bp.c <- b: 38 | default: // Discard the buffer if the pool is full. 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bytepool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | // BytePool implements a leaky pool of []byte in the form of a bounded 4 | // channel. 5 | type BytePool struct { 6 | c chan []byte 7 | w int 8 | } 9 | 10 | // NewBytePool creates a new BytePool bounded to the given maxSize, with new 11 | // byte arrays sized based on width. 12 | func NewBytePool(maxSize int, width int) (bp *BytePool) { 13 | return &BytePool{ 14 | c: make(chan []byte, maxSize), 15 | w: width, 16 | } 17 | } 18 | 19 | // Get gets a []byte from the BytePool, or creates a new one if none are 20 | // available in the pool. 21 | func (bp *BytePool) Get() (b []byte) { 22 | select { 23 | case b = <-bp.c: 24 | // reuse existing buffer 25 | default: 26 | // create new buffer 27 | b = make([]byte, bp.w) 28 | } 29 | return 30 | } 31 | 32 | // Put returns the given Buffer to the BytePool. 33 | func (bp *BytePool) Put(b []byte) { 34 | select { 35 | case bp.c <- b: 36 | // buffer went back into pool 37 | default: 38 | // buffer didn't go back into pool, just discard 39 | } 40 | } 41 | 42 | // Width returns the width of the byte arrays in this pool. 43 | func (bp *BytePool) Width() (n int) { 44 | return bp.w 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/sizedbufferpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // SizedBufferPool implements a pool of bytes.Buffers in the form of a bounded 8 | // channel. Buffers are pre-allocated to the requested size. 9 | type SizedBufferPool struct { 10 | c chan *bytes.Buffer 11 | a int 12 | } 13 | 14 | // SizedBufferPool creates a new BufferPool bounded to the given size. 15 | // size defines the number of buffers to be retained in the pool and alloc sets 16 | // the initial capacity of new buffers to minimize calls to make(). 17 | // 18 | // The value of alloc should seek to provide a buffer that is representative of 19 | // most data written to the the buffer (i.e. 95th percentile) without being 20 | // overly large (which will increase static memory consumption). You may wish to 21 | // track the capacity of your last N buffers (i.e. using an []int) prior to 22 | // returning them to the pool as input into calculating a suitable alloc value. 23 | func NewSizedBufferPool(size int, alloc int) (bp *SizedBufferPool) { 24 | return &SizedBufferPool{ 25 | c: make(chan *bytes.Buffer, size), 26 | a: alloc, 27 | } 28 | } 29 | 30 | // Get gets a Buffer from the SizedBufferPool, or creates a new one if none are 31 | // available in the pool. Buffers have a pre-allocated capacity. 32 | func (bp *SizedBufferPool) Get() (b *bytes.Buffer) { 33 | select { 34 | case b = <-bp.c: 35 | // reuse existing buffer 36 | default: 37 | // create new buffer 38 | b = bytes.NewBuffer(make([]byte, 0, bp.a)) 39 | } 40 | return 41 | } 42 | 43 | // Put returns the given Buffer to the SizedBufferPool. 44 | func (bp *SizedBufferPool) Put(b *bytes.Buffer) { 45 | b.Reset() 46 | 47 | // Release buffers over our maximum capacity and re-create a pre-sized 48 | // buffer to replace it. 49 | // Note that the cap(b.Bytes()) provides the capacity from the read off-set 50 | // only, but as we've called b.Reset() the full capacity of the underlying 51 | // byte slice is returned. 52 | if cap(b.Bytes()) > bp.a { 53 | b = bytes.NewBuffer(make([]byte, 0, bp.a)) 54 | } 55 | 56 | select { 57 | case bp.c <- b: 58 | default: // Discard the buffer if the pool is full. 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/patrickmn/go-cache/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | This is a list of people who have contributed code to go-cache. They, or their 2 | employers, are the copyright holders of the contributed code. Contributed code 3 | is subject to the license restrictions listed in LICENSE (as they were when the 4 | code was contributed.) 5 | 6 | Dustin Sallings 7 | Jason Mooberry 8 | Sergey Shepelev 9 | -------------------------------------------------------------------------------- /vendor/github.com/patrickmn/go-cache/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015 Patrick Mylund Nielsen and the go-cache contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/patrickmn/go-cache/sharded_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "strconv" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // func TestDjb33(t *testing.T) { 11 | // } 12 | 13 | var shardedKeys = []string{ 14 | "f", 15 | "fo", 16 | "foo", 17 | "barf", 18 | "barfo", 19 | "foobar", 20 | "bazbarf", 21 | "bazbarfo", 22 | "bazbarfoo", 23 | "foobarbazq", 24 | "foobarbazqu", 25 | "foobarbazquu", 26 | "foobarbazquux", 27 | } 28 | 29 | func TestShardedCache(t *testing.T) { 30 | tc := unexportedNewSharded(DefaultExpiration, 0, 13) 31 | for _, v := range shardedKeys { 32 | tc.Set(v, "value", DefaultExpiration) 33 | } 34 | } 35 | 36 | func BenchmarkShardedCacheGetExpiring(b *testing.B) { 37 | benchmarkShardedCacheGet(b, 5*time.Minute) 38 | } 39 | 40 | func BenchmarkShardedCacheGetNotExpiring(b *testing.B) { 41 | benchmarkShardedCacheGet(b, NoExpiration) 42 | } 43 | 44 | func benchmarkShardedCacheGet(b *testing.B, exp time.Duration) { 45 | b.StopTimer() 46 | tc := unexportedNewSharded(exp, 0, 10) 47 | tc.Set("foobarba", "zquux", DefaultExpiration) 48 | b.StartTimer() 49 | for i := 0; i < b.N; i++ { 50 | tc.Get("foobarba") 51 | } 52 | } 53 | 54 | func BenchmarkShardedCacheGetManyConcurrentExpiring(b *testing.B) { 55 | benchmarkShardedCacheGetManyConcurrent(b, 5*time.Minute) 56 | } 57 | 58 | func BenchmarkShardedCacheGetManyConcurrentNotExpiring(b *testing.B) { 59 | benchmarkShardedCacheGetManyConcurrent(b, NoExpiration) 60 | } 61 | 62 | func benchmarkShardedCacheGetManyConcurrent(b *testing.B, exp time.Duration) { 63 | b.StopTimer() 64 | n := 10000 65 | tsc := unexportedNewSharded(exp, 0, 20) 66 | keys := make([]string, n) 67 | for i := 0; i < n; i++ { 68 | k := "foo" + strconv.Itoa(n) 69 | keys[i] = k 70 | tsc.Set(k, "bar", DefaultExpiration) 71 | } 72 | each := b.N / n 73 | wg := new(sync.WaitGroup) 74 | wg.Add(n) 75 | for _, v := range keys { 76 | go func() { 77 | for j := 0; j < each; j++ { 78 | tsc.Get(v) 79 | } 80 | wg.Done() 81 | }() 82 | } 83 | b.StartTimer() 84 | wg.Wait() 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/.gitignore: -------------------------------------------------------------------------------- 1 | *.[68] 2 | *.a 3 | *.out 4 | *.swp 5 | _obj 6 | _testmain.go 7 | cmd/metrics-bench/metrics-bench 8 | cmd/metrics-example/metrics-example 9 | cmd/never-read/never-read 10 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | 9 | script: 10 | - ./validate.sh 11 | 12 | # this should give us faster builds according to 13 | # http://docs.travis-ci.com/user/migrating-from-legacy/ 14 | sudo: false 15 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Richard Crowley. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS 16 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | The views and conclusions contained in the software and documentation 28 | are those of the authors and should not be interpreted as representing 29 | official policies, either expressed or implied, of Richard Crowley. 30 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/cmd/metrics-bench/metrics-bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/rcrowley/go-metrics" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | r := metrics.NewRegistry() 11 | for i := 0; i < 10000; i++ { 12 | r.Register(fmt.Sprintf("counter-%d", i), metrics.NewCounter()) 13 | r.Register(fmt.Sprintf("gauge-%d", i), metrics.NewGauge()) 14 | r.Register(fmt.Sprintf("gaugefloat64-%d", i), metrics.NewGaugeFloat64()) 15 | r.Register(fmt.Sprintf("histogram-uniform-%d", i), metrics.NewHistogram(metrics.NewUniformSample(1028))) 16 | r.Register(fmt.Sprintf("histogram-exp-%d", i), metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015))) 17 | r.Register(fmt.Sprintf("meter-%d", i), metrics.NewMeter()) 18 | } 19 | time.Sleep(600e9) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/cmd/never-read/never-read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | ) 7 | 8 | func main() { 9 | addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003") 10 | l, err := net.ListenTCP("tcp", addr) 11 | if nil != err { 12 | log.Fatalln(err) 13 | } 14 | log.Println("listening", l.Addr()) 15 | for { 16 | c, err := l.AcceptTCP() 17 | if nil != err { 18 | log.Fatalln(err) 19 | } 20 | log.Println("accepted", c.RemoteAddr()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/counter_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "testing" 4 | 5 | func BenchmarkCounter(b *testing.B) { 6 | c := NewCounter() 7 | b.ResetTimer() 8 | for i := 0; i < b.N; i++ { 9 | c.Inc(1) 10 | } 11 | } 12 | 13 | func TestCounterClear(t *testing.T) { 14 | c := NewCounter() 15 | c.Inc(1) 16 | c.Clear() 17 | if count := c.Count(); 0 != count { 18 | t.Errorf("c.Count(): 0 != %v\n", count) 19 | } 20 | } 21 | 22 | func TestCounterDec1(t *testing.T) { 23 | c := NewCounter() 24 | c.Dec(1) 25 | if count := c.Count(); -1 != count { 26 | t.Errorf("c.Count(): -1 != %v\n", count) 27 | } 28 | } 29 | 30 | func TestCounterDec2(t *testing.T) { 31 | c := NewCounter() 32 | c.Dec(2) 33 | if count := c.Count(); -2 != count { 34 | t.Errorf("c.Count(): -2 != %v\n", count) 35 | } 36 | } 37 | 38 | func TestCounterInc1(t *testing.T) { 39 | c := NewCounter() 40 | c.Inc(1) 41 | if count := c.Count(); 1 != count { 42 | t.Errorf("c.Count(): 1 != %v\n", count) 43 | } 44 | } 45 | 46 | func TestCounterInc2(t *testing.T) { 47 | c := NewCounter() 48 | c.Inc(2) 49 | if count := c.Count(); 2 != count { 50 | t.Errorf("c.Count(): 2 != %v\n", count) 51 | } 52 | } 53 | 54 | func TestCounterSnapshot(t *testing.T) { 55 | c := NewCounter() 56 | c.Inc(1) 57 | snapshot := c.Snapshot() 58 | c.Inc(1) 59 | if count := snapshot.Count(); 1 != count { 60 | t.Errorf("c.Count(): 1 != %v\n", count) 61 | } 62 | } 63 | 64 | func TestCounterZero(t *testing.T) { 65 | c := NewCounter() 66 | if count := c.Count(); 0 != count { 67 | t.Errorf("c.Count(): 0 != %v\n", count) 68 | } 69 | } 70 | 71 | func TestGetOrRegisterCounter(t *testing.T) { 72 | r := NewRegistry() 73 | NewRegisteredCounter("foo", r).Inc(47) 74 | if c := GetOrRegisterCounter("foo", r); 47 != c.Count() { 75 | t.Fatal(c) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/debug_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "runtime" 5 | "runtime/debug" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func BenchmarkDebugGCStats(b *testing.B) { 11 | r := NewRegistry() 12 | RegisterDebugGCStats(r) 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | CaptureDebugGCStatsOnce(r) 16 | } 17 | } 18 | 19 | func TestDebugGCStatsBlocking(t *testing.T) { 20 | if g := runtime.GOMAXPROCS(0); g < 2 { 21 | t.Skipf("skipping TestDebugGCMemStatsBlocking with GOMAXPROCS=%d\n", g) 22 | return 23 | } 24 | ch := make(chan int) 25 | go testDebugGCStatsBlocking(ch) 26 | var gcStats debug.GCStats 27 | t0 := time.Now() 28 | debug.ReadGCStats(&gcStats) 29 | t1 := time.Now() 30 | t.Log("i++ during debug.ReadGCStats:", <-ch) 31 | go testDebugGCStatsBlocking(ch) 32 | d := t1.Sub(t0) 33 | t.Log(d) 34 | time.Sleep(d) 35 | t.Log("i++ during time.Sleep:", <-ch) 36 | } 37 | 38 | func testDebugGCStatsBlocking(ch chan int) { 39 | i := 0 40 | for { 41 | select { 42 | case ch <- i: 43 | return 44 | default: 45 | i++ 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/gauge.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "sync/atomic" 4 | 5 | // Gauges hold an int64 value that can be set arbitrarily. 6 | type Gauge interface { 7 | Snapshot() Gauge 8 | Update(int64) 9 | Value() int64 10 | } 11 | 12 | // GetOrRegisterGauge returns an existing Gauge or constructs and registers a 13 | // new StandardGauge. 14 | func GetOrRegisterGauge(name string, r Registry) Gauge { 15 | if nil == r { 16 | r = DefaultRegistry 17 | } 18 | return r.GetOrRegister(name, NewGauge).(Gauge) 19 | } 20 | 21 | // NewGauge constructs a new StandardGauge. 22 | func NewGauge() Gauge { 23 | if UseNilMetrics { 24 | return NilGauge{} 25 | } 26 | return &StandardGauge{0} 27 | } 28 | 29 | // NewRegisteredGauge constructs and registers a new StandardGauge. 30 | func NewRegisteredGauge(name string, r Registry) Gauge { 31 | c := NewGauge() 32 | if nil == r { 33 | r = DefaultRegistry 34 | } 35 | r.Register(name, c) 36 | return c 37 | } 38 | 39 | // GaugeSnapshot is a read-only copy of another Gauge. 40 | type GaugeSnapshot int64 41 | 42 | // Snapshot returns the snapshot. 43 | func (g GaugeSnapshot) Snapshot() Gauge { return g } 44 | 45 | // Update panics. 46 | func (GaugeSnapshot) Update(int64) { 47 | panic("Update called on a GaugeSnapshot") 48 | } 49 | 50 | // Value returns the value at the time the snapshot was taken. 51 | func (g GaugeSnapshot) Value() int64 { return int64(g) } 52 | 53 | // NilGauge is a no-op Gauge. 54 | type NilGauge struct{} 55 | 56 | // Snapshot is a no-op. 57 | func (NilGauge) Snapshot() Gauge { return NilGauge{} } 58 | 59 | // Update is a no-op. 60 | func (NilGauge) Update(v int64) {} 61 | 62 | // Value is a no-op. 63 | func (NilGauge) Value() int64 { return 0 } 64 | 65 | // StandardGauge is the standard implementation of a Gauge and uses the 66 | // sync/atomic package to manage a single int64 value. 67 | type StandardGauge struct { 68 | value int64 69 | } 70 | 71 | // Snapshot returns a read-only copy of the gauge. 72 | func (g *StandardGauge) Snapshot() Gauge { 73 | return GaugeSnapshot(g.Value()) 74 | } 75 | 76 | // Update updates the gauge's value. 77 | func (g *StandardGauge) Update(v int64) { 78 | atomic.StoreInt64(&g.value, v) 79 | } 80 | 81 | // Value returns the gauge's current value. 82 | func (g *StandardGauge) Value() int64 { 83 | return atomic.LoadInt64(&g.value) 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/gauge_float64.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "sync" 4 | 5 | // GaugeFloat64s hold a float64 value that can be set arbitrarily. 6 | type GaugeFloat64 interface { 7 | Snapshot() GaugeFloat64 8 | Update(float64) 9 | Value() float64 10 | } 11 | 12 | // GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a 13 | // new StandardGaugeFloat64. 14 | func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { 15 | if nil == r { 16 | r = DefaultRegistry 17 | } 18 | return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) 19 | } 20 | 21 | // NewGaugeFloat64 constructs a new StandardGaugeFloat64. 22 | func NewGaugeFloat64() GaugeFloat64 { 23 | if UseNilMetrics { 24 | return NilGaugeFloat64{} 25 | } 26 | return &StandardGaugeFloat64{ 27 | value: 0.0, 28 | } 29 | } 30 | 31 | // NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. 32 | func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { 33 | c := NewGaugeFloat64() 34 | if nil == r { 35 | r = DefaultRegistry 36 | } 37 | r.Register(name, c) 38 | return c 39 | } 40 | 41 | // GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. 42 | type GaugeFloat64Snapshot float64 43 | 44 | // Snapshot returns the snapshot. 45 | func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g } 46 | 47 | // Update panics. 48 | func (GaugeFloat64Snapshot) Update(float64) { 49 | panic("Update called on a GaugeFloat64Snapshot") 50 | } 51 | 52 | // Value returns the value at the time the snapshot was taken. 53 | func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } 54 | 55 | // NilGauge is a no-op Gauge. 56 | type NilGaugeFloat64 struct{} 57 | 58 | // Snapshot is a no-op. 59 | func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } 60 | 61 | // Update is a no-op. 62 | func (NilGaugeFloat64) Update(v float64) {} 63 | 64 | // Value is a no-op. 65 | func (NilGaugeFloat64) Value() float64 { return 0.0 } 66 | 67 | // StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses 68 | // sync.Mutex to manage a single float64 value. 69 | type StandardGaugeFloat64 struct { 70 | mutex sync.Mutex 71 | value float64 72 | } 73 | 74 | // Snapshot returns a read-only copy of the gauge. 75 | func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { 76 | return GaugeFloat64Snapshot(g.Value()) 77 | } 78 | 79 | // Update updates the gauge's value. 80 | func (g *StandardGaugeFloat64) Update(v float64) { 81 | g.mutex.Lock() 82 | defer g.mutex.Unlock() 83 | g.value = v 84 | } 85 | 86 | // Value returns the gauge's current value. 87 | func (g *StandardGaugeFloat64) Value() float64 { 88 | g.mutex.Lock() 89 | defer g.mutex.Unlock() 90 | return g.value 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/gauge_float64_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "testing" 4 | 5 | func BenchmarkGuageFloat64(b *testing.B) { 6 | g := NewGaugeFloat64() 7 | b.ResetTimer() 8 | for i := 0; i < b.N; i++ { 9 | g.Update(float64(i)) 10 | } 11 | } 12 | 13 | func TestGaugeFloat64(t *testing.T) { 14 | g := NewGaugeFloat64() 15 | g.Update(float64(47.0)) 16 | if v := g.Value(); float64(47.0) != v { 17 | t.Errorf("g.Value(): 47.0 != %v\n", v) 18 | } 19 | } 20 | 21 | func TestGaugeFloat64Snapshot(t *testing.T) { 22 | g := NewGaugeFloat64() 23 | g.Update(float64(47.0)) 24 | snapshot := g.Snapshot() 25 | g.Update(float64(0)) 26 | if v := snapshot.Value(); float64(47.0) != v { 27 | t.Errorf("g.Value(): 47.0 != %v\n", v) 28 | } 29 | } 30 | 31 | func TestGetOrRegisterGaugeFloat64(t *testing.T) { 32 | r := NewRegistry() 33 | NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0)) 34 | t.Logf("registry: %v", r) 35 | if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() { 36 | t.Fatal(g) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/gauge_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "testing" 4 | 5 | func BenchmarkGuage(b *testing.B) { 6 | g := NewGauge() 7 | b.ResetTimer() 8 | for i := 0; i < b.N; i++ { 9 | g.Update(int64(i)) 10 | } 11 | } 12 | 13 | func TestGauge(t *testing.T) { 14 | g := NewGauge() 15 | g.Update(int64(47)) 16 | if v := g.Value(); 47 != v { 17 | t.Errorf("g.Value(): 47 != %v\n", v) 18 | } 19 | } 20 | 21 | func TestGaugeSnapshot(t *testing.T) { 22 | g := NewGauge() 23 | g.Update(int64(47)) 24 | snapshot := g.Snapshot() 25 | g.Update(int64(0)) 26 | if v := snapshot.Value(); 47 != v { 27 | t.Errorf("g.Value(): 47 != %v\n", v) 28 | } 29 | } 30 | 31 | func TestGetOrRegisterGauge(t *testing.T) { 32 | r := NewRegistry() 33 | NewRegisteredGauge("foo", r).Update(47) 34 | if g := GetOrRegisterGauge("foo", r); 47 != g.Value() { 35 | t.Fatal(g) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/graphite_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func ExampleGraphite() { 9 | addr, _ := net.ResolveTCPAddr("net", ":2003") 10 | go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr) 11 | } 12 | 13 | func ExampleGraphiteWithConfig() { 14 | addr, _ := net.ResolveTCPAddr("net", ":2003") 15 | go GraphiteWithConfig(GraphiteConfig{ 16 | Addr: addr, 17 | Registry: DefaultRegistry, 18 | FlushInterval: 1 * time.Second, 19 | DurationUnit: time.Millisecond, 20 | Percentiles: []float64{0.5, 0.75, 0.99, 0.999}, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/healthcheck.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | // Healthchecks hold an error value describing an arbitrary up/down status. 4 | type Healthcheck interface { 5 | Check() 6 | Error() error 7 | Healthy() 8 | Unhealthy(error) 9 | } 10 | 11 | // NewHealthcheck constructs a new Healthcheck which will use the given 12 | // function to update its status. 13 | func NewHealthcheck(f func(Healthcheck)) Healthcheck { 14 | if UseNilMetrics { 15 | return NilHealthcheck{} 16 | } 17 | return &StandardHealthcheck{nil, f} 18 | } 19 | 20 | // NilHealthcheck is a no-op. 21 | type NilHealthcheck struct{} 22 | 23 | // Check is a no-op. 24 | func (NilHealthcheck) Check() {} 25 | 26 | // Error is a no-op. 27 | func (NilHealthcheck) Error() error { return nil } 28 | 29 | // Healthy is a no-op. 30 | func (NilHealthcheck) Healthy() {} 31 | 32 | // Unhealthy is a no-op. 33 | func (NilHealthcheck) Unhealthy(error) {} 34 | 35 | // StandardHealthcheck is the standard implementation of a Healthcheck and 36 | // stores the status and a function to call to update the status. 37 | type StandardHealthcheck struct { 38 | err error 39 | f func(Healthcheck) 40 | } 41 | 42 | // Check runs the healthcheck function to update the healthcheck's status. 43 | func (h *StandardHealthcheck) Check() { 44 | h.f(h) 45 | } 46 | 47 | // Error returns the healthcheck's status, which will be nil if it is healthy. 48 | func (h *StandardHealthcheck) Error() error { 49 | return h.err 50 | } 51 | 52 | // Healthy marks the healthcheck as healthy. 53 | func (h *StandardHealthcheck) Healthy() { 54 | h.err = nil 55 | } 56 | 57 | // Unhealthy marks the healthcheck as unhealthy. The error is stored and 58 | // may be retrieved by the Error method. 59 | func (h *StandardHealthcheck) Unhealthy(err error) { 60 | h.err = err 61 | } 62 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/histogram_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "testing" 4 | 5 | func BenchmarkHistogram(b *testing.B) { 6 | h := NewHistogram(NewUniformSample(100)) 7 | b.ResetTimer() 8 | for i := 0; i < b.N; i++ { 9 | h.Update(int64(i)) 10 | } 11 | } 12 | 13 | func TestGetOrRegisterHistogram(t *testing.T) { 14 | r := NewRegistry() 15 | s := NewUniformSample(100) 16 | NewRegisteredHistogram("foo", r, s).Update(47) 17 | if h := GetOrRegisterHistogram("foo", r, s); 1 != h.Count() { 18 | t.Fatal(h) 19 | } 20 | } 21 | 22 | func TestHistogram10000(t *testing.T) { 23 | h := NewHistogram(NewUniformSample(100000)) 24 | for i := 1; i <= 10000; i++ { 25 | h.Update(int64(i)) 26 | } 27 | testHistogram10000(t, h) 28 | } 29 | 30 | func TestHistogramEmpty(t *testing.T) { 31 | h := NewHistogram(NewUniformSample(100)) 32 | if count := h.Count(); 0 != count { 33 | t.Errorf("h.Count(): 0 != %v\n", count) 34 | } 35 | if min := h.Min(); 0 != min { 36 | t.Errorf("h.Min(): 0 != %v\n", min) 37 | } 38 | if max := h.Max(); 0 != max { 39 | t.Errorf("h.Max(): 0 != %v\n", max) 40 | } 41 | if mean := h.Mean(); 0.0 != mean { 42 | t.Errorf("h.Mean(): 0.0 != %v\n", mean) 43 | } 44 | if stdDev := h.StdDev(); 0.0 != stdDev { 45 | t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev) 46 | } 47 | ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) 48 | if 0.0 != ps[0] { 49 | t.Errorf("median: 0.0 != %v\n", ps[0]) 50 | } 51 | if 0.0 != ps[1] { 52 | t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) 53 | } 54 | if 0.0 != ps[2] { 55 | t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) 56 | } 57 | } 58 | 59 | func TestHistogramSnapshot(t *testing.T) { 60 | h := NewHistogram(NewUniformSample(100000)) 61 | for i := 1; i <= 10000; i++ { 62 | h.Update(int64(i)) 63 | } 64 | snapshot := h.Snapshot() 65 | h.Update(0) 66 | testHistogram10000(t, snapshot) 67 | } 68 | 69 | func testHistogram10000(t *testing.T, h Histogram) { 70 | if count := h.Count(); 10000 != count { 71 | t.Errorf("h.Count(): 10000 != %v\n", count) 72 | } 73 | if min := h.Min(); 1 != min { 74 | t.Errorf("h.Min(): 1 != %v\n", min) 75 | } 76 | if max := h.Max(); 10000 != max { 77 | t.Errorf("h.Max(): 10000 != %v\n", max) 78 | } 79 | if mean := h.Mean(); 5000.5 != mean { 80 | t.Errorf("h.Mean(): 5000.5 != %v\n", mean) 81 | } 82 | if stdDev := h.StdDev(); 2886.751331514372 != stdDev { 83 | t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev) 84 | } 85 | ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) 86 | if 5000.5 != ps[0] { 87 | t.Errorf("median: 5000.5 != %v\n", ps[0]) 88 | } 89 | if 7500.75 != ps[1] { 90 | t.Errorf("75th percentile: 7500.75 != %v\n", ps[1]) 91 | } 92 | if 9900.99 != ps[2] { 93 | t.Errorf("99th percentile: 9900.99 != %v\n", ps[2]) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/json.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "time" 7 | ) 8 | 9 | // MarshalJSON returns a byte slice containing a JSON representation of all 10 | // the metrics in the Registry. 11 | func (r *StandardRegistry) MarshalJSON() ([]byte, error) { 12 | data := make(map[string]map[string]interface{}) 13 | r.Each(func(name string, i interface{}) { 14 | values := make(map[string]interface{}) 15 | switch metric := i.(type) { 16 | case Counter: 17 | values["count"] = metric.Count() 18 | case Gauge: 19 | values["value"] = metric.Value() 20 | case GaugeFloat64: 21 | values["value"] = metric.Value() 22 | case Healthcheck: 23 | values["error"] = nil 24 | metric.Check() 25 | if err := metric.Error(); nil != err { 26 | values["error"] = metric.Error().Error() 27 | } 28 | case Histogram: 29 | h := metric.Snapshot() 30 | ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 31 | values["count"] = h.Count() 32 | values["min"] = h.Min() 33 | values["max"] = h.Max() 34 | values["mean"] = h.Mean() 35 | values["stddev"] = h.StdDev() 36 | values["median"] = ps[0] 37 | values["75%"] = ps[1] 38 | values["95%"] = ps[2] 39 | values["99%"] = ps[3] 40 | values["99.9%"] = ps[4] 41 | case Meter: 42 | m := metric.Snapshot() 43 | values["count"] = m.Count() 44 | values["1m.rate"] = m.Rate1() 45 | values["5m.rate"] = m.Rate5() 46 | values["15m.rate"] = m.Rate15() 47 | values["mean.rate"] = m.RateMean() 48 | case Timer: 49 | t := metric.Snapshot() 50 | ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 51 | values["count"] = t.Count() 52 | values["min"] = t.Min() 53 | values["max"] = t.Max() 54 | values["mean"] = t.Mean() 55 | values["stddev"] = t.StdDev() 56 | values["median"] = ps[0] 57 | values["75%"] = ps[1] 58 | values["95%"] = ps[2] 59 | values["99%"] = ps[3] 60 | values["99.9%"] = ps[4] 61 | values["1m.rate"] = t.Rate1() 62 | values["5m.rate"] = t.Rate5() 63 | values["15m.rate"] = t.Rate15() 64 | values["mean.rate"] = t.RateMean() 65 | } 66 | data[name] = values 67 | }) 68 | return json.Marshal(data) 69 | } 70 | 71 | // WriteJSON writes metrics from the given registry periodically to the 72 | // specified io.Writer as JSON. 73 | func WriteJSON(r Registry, d time.Duration, w io.Writer) { 74 | for _ = range time.Tick(d) { 75 | WriteJSONOnce(r, w) 76 | } 77 | } 78 | 79 | // WriteJSONOnce writes metrics from the given registry to the specified 80 | // io.Writer as JSON. 81 | func WriteJSONOnce(r Registry, w io.Writer) { 82 | json.NewEncoder(w).Encode(r) 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/json_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | ) 8 | 9 | func TestRegistryMarshallJSON(t *testing.T) { 10 | b := &bytes.Buffer{} 11 | enc := json.NewEncoder(b) 12 | r := NewRegistry() 13 | r.Register("counter", NewCounter()) 14 | enc.Encode(r) 15 | if s := b.String(); "{\"counter\":{\"count\":0}}\n" != s { 16 | t.Fatalf(s) 17 | } 18 | } 19 | 20 | func TestRegistryWriteJSONOnce(t *testing.T) { 21 | r := NewRegistry() 22 | r.Register("counter", NewCounter()) 23 | b := &bytes.Buffer{} 24 | WriteJSONOnce(r, b) 25 | if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" { 26 | t.Fail() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/librato/client.go: -------------------------------------------------------------------------------- 1 | package librato 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | ) 10 | 11 | const Operations = "operations" 12 | const OperationsShort = "ops" 13 | 14 | type LibratoClient struct { 15 | Email, Token string 16 | } 17 | 18 | // property strings 19 | const ( 20 | // display attributes 21 | Color = "color" 22 | DisplayMax = "display_max" 23 | DisplayMin = "display_min" 24 | DisplayUnitsLong = "display_units_long" 25 | DisplayUnitsShort = "display_units_short" 26 | DisplayStacked = "display_stacked" 27 | DisplayTransform = "display_transform" 28 | // special gauge display attributes 29 | SummarizeFunction = "summarize_function" 30 | Aggregate = "aggregate" 31 | 32 | // metric keys 33 | Name = "name" 34 | Period = "period" 35 | Description = "description" 36 | DisplayName = "display_name" 37 | Attributes = "attributes" 38 | 39 | // measurement keys 40 | MeasureTime = "measure_time" 41 | Source = "source" 42 | Value = "value" 43 | 44 | // special gauge keys 45 | Count = "count" 46 | Sum = "sum" 47 | Max = "max" 48 | Min = "min" 49 | SumSquares = "sum_squares" 50 | 51 | // batch keys 52 | Counters = "counters" 53 | Gauges = "gauges" 54 | 55 | MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics" 56 | ) 57 | 58 | type Measurement map[string]interface{} 59 | type Metric map[string]interface{} 60 | 61 | type Batch struct { 62 | Gauges []Measurement `json:"gauges,omitempty"` 63 | Counters []Measurement `json:"counters,omitempty"` 64 | MeasureTime int64 `json:"measure_time"` 65 | Source string `json:"source"` 66 | } 67 | 68 | func (self *LibratoClient) PostMetrics(batch Batch) (err error) { 69 | var ( 70 | js []byte 71 | req *http.Request 72 | resp *http.Response 73 | ) 74 | 75 | if len(batch.Counters) == 0 && len(batch.Gauges) == 0 { 76 | return nil 77 | } 78 | 79 | if js, err = json.Marshal(batch); err != nil { 80 | return 81 | } 82 | 83 | if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil { 84 | return 85 | } 86 | 87 | req.Header.Set("Content-Type", "application/json") 88 | req.SetBasicAuth(self.Email, self.Token) 89 | 90 | if resp, err = http.DefaultClient.Do(req); err != nil { 91 | return 92 | } 93 | 94 | if resp.StatusCode != http.StatusOK { 95 | var body []byte 96 | if body, err = ioutil.ReadAll(resp.Body); err != nil { 97 | body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err)) 98 | } 99 | err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) 100 | } 101 | return 102 | } 103 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/meter_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func BenchmarkMeter(b *testing.B) { 9 | m := NewMeter() 10 | b.ResetTimer() 11 | for i := 0; i < b.N; i++ { 12 | m.Mark(1) 13 | } 14 | } 15 | 16 | func TestGetOrRegisterMeter(t *testing.T) { 17 | r := NewRegistry() 18 | NewRegisteredMeter("foo", r).Mark(47) 19 | if m := GetOrRegisterMeter("foo", r); 47 != m.Count() { 20 | t.Fatal(m) 21 | } 22 | } 23 | 24 | func TestMeterDecay(t *testing.T) { 25 | ma := meterArbiter{ 26 | ticker: time.NewTicker(time.Millisecond), 27 | } 28 | m := newStandardMeter() 29 | ma.meters = append(ma.meters, m) 30 | go ma.tick() 31 | m.Mark(1) 32 | rateMean := m.RateMean() 33 | time.Sleep(100 * time.Millisecond) 34 | if m.RateMean() >= rateMean { 35 | t.Error("m.RateMean() didn't decrease") 36 | } 37 | } 38 | 39 | func TestMeterNonzero(t *testing.T) { 40 | m := NewMeter() 41 | m.Mark(3) 42 | if count := m.Count(); 3 != count { 43 | t.Errorf("m.Count(): 3 != %v\n", count) 44 | } 45 | } 46 | 47 | func TestMeterSnapshot(t *testing.T) { 48 | m := NewMeter() 49 | m.Mark(1) 50 | if snapshot := m.Snapshot(); m.RateMean() != snapshot.RateMean() { 51 | t.Fatal(snapshot) 52 | } 53 | } 54 | 55 | func TestMeterZero(t *testing.T) { 56 | m := NewMeter() 57 | if count := m.Count(); 0 != count { 58 | t.Errorf("m.Count(): 0 != %v\n", count) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/metrics.go: -------------------------------------------------------------------------------- 1 | // Go port of Coda Hale's Metrics library 2 | // 3 | // 4 | // 5 | // Coda Hale's original work: 6 | package metrics 7 | 8 | // UseNilMetrics is checked by the constructor functions for all of the 9 | // standard metrics. If it is true, the metric returned is a stub. 10 | // 11 | // This global kill-switch helps quantify the observer effect and makes 12 | // for less cluttered pprof profiles. 13 | var UseNilMetrics bool = false 14 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/metrics_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | const FANOUT = 128 11 | 12 | // Stop the compiler from complaining during debugging. 13 | var ( 14 | _ = ioutil.Discard 15 | _ = log.LstdFlags 16 | ) 17 | 18 | func BenchmarkMetrics(b *testing.B) { 19 | r := NewRegistry() 20 | c := NewRegisteredCounter("counter", r) 21 | g := NewRegisteredGauge("gauge", r) 22 | gf := NewRegisteredGaugeFloat64("gaugefloat64", r) 23 | h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) 24 | m := NewRegisteredMeter("meter", r) 25 | t := NewRegisteredTimer("timer", r) 26 | RegisterDebugGCStats(r) 27 | RegisterRuntimeMemStats(r) 28 | b.ResetTimer() 29 | ch := make(chan bool) 30 | 31 | wgD := &sync.WaitGroup{} 32 | /* 33 | wgD.Add(1) 34 | go func() { 35 | defer wgD.Done() 36 | //log.Println("go CaptureDebugGCStats") 37 | for { 38 | select { 39 | case <-ch: 40 | //log.Println("done CaptureDebugGCStats") 41 | return 42 | default: 43 | CaptureDebugGCStatsOnce(r) 44 | } 45 | } 46 | }() 47 | //*/ 48 | 49 | wgR := &sync.WaitGroup{} 50 | //* 51 | wgR.Add(1) 52 | go func() { 53 | defer wgR.Done() 54 | //log.Println("go CaptureRuntimeMemStats") 55 | for { 56 | select { 57 | case <-ch: 58 | //log.Println("done CaptureRuntimeMemStats") 59 | return 60 | default: 61 | CaptureRuntimeMemStatsOnce(r) 62 | } 63 | } 64 | }() 65 | //*/ 66 | 67 | wgW := &sync.WaitGroup{} 68 | /* 69 | wgW.Add(1) 70 | go func() { 71 | defer wgW.Done() 72 | //log.Println("go Write") 73 | for { 74 | select { 75 | case <-ch: 76 | //log.Println("done Write") 77 | return 78 | default: 79 | WriteOnce(r, ioutil.Discard) 80 | } 81 | } 82 | }() 83 | //*/ 84 | 85 | wg := &sync.WaitGroup{} 86 | wg.Add(FANOUT) 87 | for i := 0; i < FANOUT; i++ { 88 | go func(i int) { 89 | defer wg.Done() 90 | //log.Println("go", i) 91 | for i := 0; i < b.N; i++ { 92 | c.Inc(1) 93 | g.Update(int64(i)) 94 | gf.Update(float64(i)) 95 | h.Update(int64(i)) 96 | m.Mark(1) 97 | t.Update(1) 98 | } 99 | //log.Println("done", i) 100 | }(i) 101 | } 102 | wg.Wait() 103 | close(ch) 104 | wgD.Wait() 105 | wgR.Wait() 106 | wgW.Wait() 107 | } 108 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/opentsdb_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func ExampleOpenTSDB() { 9 | addr, _ := net.ResolveTCPAddr("net", ":2003") 10 | go OpenTSDB(DefaultRegistry, 1*time.Second, "some.prefix", addr) 11 | } 12 | 13 | func ExampleOpenTSDBWithConfig() { 14 | addr, _ := net.ResolveTCPAddr("net", ":2003") 15 | go OpenTSDBWithConfig(OpenTSDBConfig{ 16 | Addr: addr, 17 | Registry: DefaultRegistry, 18 | FlushInterval: 1 * time.Second, 19 | DurationUnit: time.Millisecond, 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/runtime_cgo.go: -------------------------------------------------------------------------------- 1 | // +build cgo 2 | // +build !appengine 3 | 4 | package metrics 5 | 6 | import "runtime" 7 | 8 | func numCgoCall() int64 { 9 | return runtime.NumCgoCall() 10 | } 11 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go: -------------------------------------------------------------------------------- 1 | // +build go1.5 2 | 3 | package metrics 4 | 5 | import "runtime" 6 | 7 | func gcCPUFraction(memStats *runtime.MemStats) float64 { 8 | return memStats.GCCPUFraction 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go: -------------------------------------------------------------------------------- 1 | // +build !cgo appengine 2 | 3 | package metrics 4 | 5 | func numCgoCall() int64 { 6 | return 0 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go: -------------------------------------------------------------------------------- 1 | // +build !go1.5 2 | 3 | package metrics 4 | 5 | import "runtime" 6 | 7 | func gcCPUFraction(memStats *runtime.MemStats) float64 { 8 | return 0 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/runtime_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkRuntimeMemStats(b *testing.B) { 10 | r := NewRegistry() 11 | RegisterRuntimeMemStats(r) 12 | b.ResetTimer() 13 | for i := 0; i < b.N; i++ { 14 | CaptureRuntimeMemStatsOnce(r) 15 | } 16 | } 17 | 18 | func TestRuntimeMemStats(t *testing.T) { 19 | r := NewRegistry() 20 | RegisterRuntimeMemStats(r) 21 | CaptureRuntimeMemStatsOnce(r) 22 | zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests. 23 | runtime.GC() 24 | CaptureRuntimeMemStatsOnce(r) 25 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero { 26 | t.Fatal(count - zero) 27 | } 28 | runtime.GC() 29 | runtime.GC() 30 | CaptureRuntimeMemStatsOnce(r) 31 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero { 32 | t.Fatal(count - zero) 33 | } 34 | for i := 0; i < 256; i++ { 35 | runtime.GC() 36 | } 37 | CaptureRuntimeMemStatsOnce(r) 38 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero { 39 | t.Fatal(count - zero) 40 | } 41 | for i := 0; i < 257; i++ { 42 | runtime.GC() 43 | } 44 | CaptureRuntimeMemStatsOnce(r) 45 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures. 46 | t.Fatal(count - zero) 47 | } 48 | } 49 | 50 | func TestRuntimeMemStatsNumThread(t *testing.T) { 51 | r := NewRegistry() 52 | RegisterRuntimeMemStats(r) 53 | CaptureRuntimeMemStatsOnce(r) 54 | 55 | if value := runtimeMetrics.NumThread.Value(); value < 1 { 56 | t.Fatalf("got NumThread: %d, wanted at least 1", value) 57 | } 58 | } 59 | 60 | func TestRuntimeMemStatsBlocking(t *testing.T) { 61 | if g := runtime.GOMAXPROCS(0); g < 2 { 62 | t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g) 63 | } 64 | ch := make(chan int) 65 | go testRuntimeMemStatsBlocking(ch) 66 | var memStats runtime.MemStats 67 | t0 := time.Now() 68 | runtime.ReadMemStats(&memStats) 69 | t1 := time.Now() 70 | t.Log("i++ during runtime.ReadMemStats:", <-ch) 71 | go testRuntimeMemStatsBlocking(ch) 72 | d := t1.Sub(t0) 73 | t.Log(d) 74 | time.Sleep(d) 75 | t.Log("i++ during time.Sleep:", <-ch) 76 | } 77 | 78 | func testRuntimeMemStatsBlocking(ch chan int) { 79 | i := 0 80 | for { 81 | select { 82 | case ch <- i: 83 | return 84 | default: 85 | i++ 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/syslog.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package metrics 4 | 5 | import ( 6 | "fmt" 7 | "log/syslog" 8 | "time" 9 | ) 10 | 11 | // Output each metric in the given registry to syslog periodically using 12 | // the given syslogger. 13 | func Syslog(r Registry, d time.Duration, w *syslog.Writer) { 14 | for _ = range time.Tick(d) { 15 | r.Each(func(name string, i interface{}) { 16 | switch metric := i.(type) { 17 | case Counter: 18 | w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) 19 | case Gauge: 20 | w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) 21 | case GaugeFloat64: 22 | w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) 23 | case Healthcheck: 24 | metric.Check() 25 | w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) 26 | case Histogram: 27 | h := metric.Snapshot() 28 | ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 29 | w.Info(fmt.Sprintf( 30 | "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f", 31 | name, 32 | h.Count(), 33 | h.Min(), 34 | h.Max(), 35 | h.Mean(), 36 | h.StdDev(), 37 | ps[0], 38 | ps[1], 39 | ps[2], 40 | ps[3], 41 | ps[4], 42 | )) 43 | case Meter: 44 | m := metric.Snapshot() 45 | w.Info(fmt.Sprintf( 46 | "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", 47 | name, 48 | m.Count(), 49 | m.Rate1(), 50 | m.Rate5(), 51 | m.Rate15(), 52 | m.RateMean(), 53 | )) 54 | case Timer: 55 | t := metric.Snapshot() 56 | ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 57 | w.Info(fmt.Sprintf( 58 | "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f", 59 | name, 60 | t.Count(), 61 | t.Min(), 62 | t.Max(), 63 | t.Mean(), 64 | t.StdDev(), 65 | ps[0], 66 | ps[1], 67 | ps[2], 68 | ps[3], 69 | ps[4], 70 | t.Rate1(), 71 | t.Rate5(), 72 | t.Rate15(), 73 | t.RateMean(), 74 | )) 75 | } 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/timer_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkTimer(b *testing.B) { 10 | tm := NewTimer() 11 | b.ResetTimer() 12 | for i := 0; i < b.N; i++ { 13 | tm.Update(1) 14 | } 15 | } 16 | 17 | func TestGetOrRegisterTimer(t *testing.T) { 18 | r := NewRegistry() 19 | NewRegisteredTimer("foo", r).Update(47) 20 | if tm := GetOrRegisterTimer("foo", r); 1 != tm.Count() { 21 | t.Fatal(tm) 22 | } 23 | } 24 | 25 | func TestTimerExtremes(t *testing.T) { 26 | tm := NewTimer() 27 | tm.Update(math.MaxInt64) 28 | tm.Update(0) 29 | if stdDev := tm.StdDev(); 4.611686018427388e+18 != stdDev { 30 | t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev) 31 | } 32 | } 33 | 34 | func TestTimerFunc(t *testing.T) { 35 | tm := NewTimer() 36 | tm.Time(func() { time.Sleep(50e6) }) 37 | if max := tm.Max(); 45e6 > max || max > 55e6 { 38 | t.Errorf("tm.Max(): 45e6 > %v || %v > 55e6\n", max, max) 39 | } 40 | } 41 | 42 | func TestTimerZero(t *testing.T) { 43 | tm := NewTimer() 44 | if count := tm.Count(); 0 != count { 45 | t.Errorf("tm.Count(): 0 != %v\n", count) 46 | } 47 | if min := tm.Min(); 0 != min { 48 | t.Errorf("tm.Min(): 0 != %v\n", min) 49 | } 50 | if max := tm.Max(); 0 != max { 51 | t.Errorf("tm.Max(): 0 != %v\n", max) 52 | } 53 | if mean := tm.Mean(); 0.0 != mean { 54 | t.Errorf("tm.Mean(): 0.0 != %v\n", mean) 55 | } 56 | if stdDev := tm.StdDev(); 0.0 != stdDev { 57 | t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev) 58 | } 59 | ps := tm.Percentiles([]float64{0.5, 0.75, 0.99}) 60 | if 0.0 != ps[0] { 61 | t.Errorf("median: 0.0 != %v\n", ps[0]) 62 | } 63 | if 0.0 != ps[1] { 64 | t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) 65 | } 66 | if 0.0 != ps[2] { 67 | t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) 68 | } 69 | if rate1 := tm.Rate1(); 0.0 != rate1 { 70 | t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1) 71 | } 72 | if rate5 := tm.Rate5(); 0.0 != rate5 { 73 | t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5) 74 | } 75 | if rate15 := tm.Rate15(); 0.0 != rate15 { 76 | t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15) 77 | } 78 | if rateMean := tm.RateMean(); 0.0 != rateMean { 79 | t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # check there are no formatting issues 6 | GOFMT_LINES=`gofmt -l . | wc -l | xargs` 7 | test $GOFMT_LINES -eq 0 || echo "gofmt needs to be run, ${GOFMT_LINES} files have issues" 8 | 9 | # run the tests for the root package 10 | go test . 11 | -------------------------------------------------------------------------------- /vendor/github.com/rcrowley/go-metrics/writer_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | ) 7 | 8 | func TestMetricsSorting(t *testing.T) { 9 | var namedMetrics = namedMetricSlice{ 10 | {name: "zzz"}, 11 | {name: "bbb"}, 12 | {name: "fff"}, 13 | {name: "ggg"}, 14 | } 15 | 16 | sort.Sort(namedMetrics) 17 | for i, name := range []string{"bbb", "fff", "ggg", "zzz"} { 18 | if namedMetrics[i].name != name { 19 | t.Fail() 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin dragonfly freebsd netbsd openbsd 6 | 7 | package terminal 8 | 9 | import "syscall" 10 | 11 | const ioctlReadTermios = syscall.TIOCGETA 12 | const ioctlWriteTermios = syscall.TIOCSETA 13 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/terminal/util_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package terminal 6 | 7 | // These constants are declared here, rather than importing 8 | // them from the syscall package as some syscall packages, even 9 | // on linux, for example gccgo, do not declare them. 10 | const ioctlReadTermios = 0x5401 // syscall.TCGETS 11 | const ioctlWriteTermios = 0x5402 // syscall.TCSETS 12 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package terminal provides support functions for dealing with terminals, as 6 | // commonly found on UNIX systems. 7 | // 8 | // Putting a terminal into raw mode is the most common requirement: 9 | // 10 | // oldState, err := terminal.MakeRaw(0) 11 | // if err != nil { 12 | // panic(err) 13 | // } 14 | // defer terminal.Restore(0, oldState) 15 | package terminal 16 | 17 | import ( 18 | "fmt" 19 | "runtime" 20 | ) 21 | 22 | type State struct{} 23 | 24 | // IsTerminal returns true if the given file descriptor is a terminal. 25 | func IsTerminal(fd int) bool { 26 | return false 27 | } 28 | 29 | // MakeRaw put the terminal connected to the given file descriptor into raw 30 | // mode and returns the previous state of the terminal so that it can be 31 | // restored. 32 | func MakeRaw(fd int) (*State, error) { 33 | return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 34 | } 35 | 36 | // GetState returns the current state of a terminal which may be useful to 37 | // restore the terminal after a signal. 38 | func GetState(fd int) (*State, error) { 39 | return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 40 | } 41 | 42 | // Restore restores the terminal connected to the given file descriptor to a 43 | // previous state. 44 | func Restore(fd int, state *State) error { 45 | return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 46 | } 47 | 48 | // GetSize returns the dimensions of the given terminal. 49 | func GetSize(fd int) (width, height int, err error) { 50 | return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 51 | } 52 | 53 | // ReadPassword reads a line of input from a terminal without local echo. This 54 | // is commonly used for inputting passwords and other sensitive data. The slice 55 | // returned does not include the \n. 56 | func ReadPassword(fd int) ([]byte, error) { 57 | return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 58 | } 59 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go 2 | Authors. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/README: -------------------------------------------------------------------------------- 1 | Gcfg reads INI-style configuration files into Go structs; 2 | supports user-defined types and subsections. 3 | 4 | Package docs: https://godoc.org/gopkg.in/gcfg.v1 5 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/go1_0.go: -------------------------------------------------------------------------------- 1 | // +build !go1.2 2 | 3 | package gcfg 4 | 5 | type textUnmarshaler interface { 6 | UnmarshalText(text []byte) error 7 | } 8 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/go1_2.go: -------------------------------------------------------------------------------- 1 | // +build go1.2 2 | 3 | package gcfg 4 | 5 | import ( 6 | "encoding" 7 | ) 8 | 9 | type textUnmarshaler encoding.TextUnmarshaler 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/issues_test.go: -------------------------------------------------------------------------------- 1 | package gcfg 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | type Config1 struct { 11 | Section struct { 12 | Int int 13 | BigInt big.Int 14 | } 15 | } 16 | 17 | var testsIssue1 = []struct { 18 | cfg string 19 | typename string 20 | }{ 21 | {"[section]\nint=X", "int"}, 22 | {"[section]\nint=", "int"}, 23 | {"[section]\nint=1A", "int"}, 24 | {"[section]\nbigint=X", "big.Int"}, 25 | {"[section]\nbigint=", "big.Int"}, 26 | {"[section]\nbigint=1A", "big.Int"}, 27 | } 28 | 29 | // Value parse error should: 30 | // - include plain type name 31 | // - not include reflect internals 32 | func TestIssue1(t *testing.T) { 33 | for i, tt := range testsIssue1 { 34 | var c Config1 35 | err := ReadStringInto(&c, tt.cfg) 36 | switch { 37 | case err == nil: 38 | t.Errorf("%d fail: got ok; wanted error", i) 39 | case !strings.Contains(err.Error(), tt.typename): 40 | t.Errorf("%d fail: error message doesn't contain type name %q: %v", 41 | i, tt.typename, err) 42 | case strings.Contains(err.Error(), "reflect"): 43 | t.Errorf("%d fail: error message includes reflect internals: %v", 44 | i, err) 45 | default: 46 | t.Logf("%d pass: %v", i, err) 47 | } 48 | } 49 | } 50 | 51 | type confIssue2 struct{ Main struct{ Foo string } } 52 | 53 | var testsIssue2 = []readtest{ 54 | {"[main]\n;\nfoo = bar\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, 55 | {"[main]\r\n;\r\nfoo = bar\r\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, 56 | } 57 | 58 | func TestIssue2(t *testing.T) { 59 | for i, tt := range testsIssue2 { 60 | id := fmt.Sprintf("issue2:%d", i) 61 | testRead(t, id, tt) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/scanner/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package scanner_test 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | import ( 12 | "gopkg.in/gcfg.v1/scanner" 13 | "gopkg.in/gcfg.v1/token" 14 | ) 15 | 16 | func ExampleScanner_Scan() { 17 | // src is the input that we want to tokenize. 18 | src := []byte(`[profile "A"] 19 | color = blue ; Comment`) 20 | 21 | // Initialize the scanner. 22 | var s scanner.Scanner 23 | fset := token.NewFileSet() // positions are relative to fset 24 | file := fset.AddFile("", fset.Base(), len(src)) // register input "file" 25 | s.Init(file, src, nil /* no error handler */, scanner.ScanComments) 26 | 27 | // Repeated calls to Scan yield the token sequence found in the input. 28 | for { 29 | pos, tok, lit := s.Scan() 30 | if tok == token.EOF { 31 | break 32 | } 33 | fmt.Printf("%s\t%q\t%q\n", fset.Position(pos), tok, lit) 34 | } 35 | 36 | // output: 37 | // 1:1 "[" "" 38 | // 1:2 "IDENT" "profile" 39 | // 1:10 "STRING" "\"A\"" 40 | // 1:13 "]" "" 41 | // 1:14 "\n" "" 42 | // 2:1 "IDENT" "color" 43 | // 2:7 "=" "" 44 | // 2:9 "STRING" "blue" 45 | // 2:14 "COMMENT" "; Comment" 46 | } 47 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/testdata/gcfg_test.gcfg: -------------------------------------------------------------------------------- 1 | ; Comment line 2 | [section] 3 | name=value # comment 4 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/testdata/gcfg_unicode_test.gcfg: -------------------------------------------------------------------------------- 1 | ; Comment line 2 | [甲] 3 | 乙=丙 # comment 4 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/token/serialize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package token 6 | 7 | type serializedFile struct { 8 | // fields correspond 1:1 to fields with same (lower-case) name in File 9 | Name string 10 | Base int 11 | Size int 12 | Lines []int 13 | Infos []lineInfo 14 | } 15 | 16 | type serializedFileSet struct { 17 | Base int 18 | Files []serializedFile 19 | } 20 | 21 | // Read calls decode to deserialize a file set into s; s must not be nil. 22 | func (s *FileSet) Read(decode func(interface{}) error) error { 23 | var ss serializedFileSet 24 | if err := decode(&ss); err != nil { 25 | return err 26 | } 27 | 28 | s.mutex.Lock() 29 | s.base = ss.Base 30 | files := make([]*File, len(ss.Files)) 31 | for i := 0; i < len(ss.Files); i++ { 32 | f := &ss.Files[i] 33 | files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos} 34 | } 35 | s.files = files 36 | s.last = nil 37 | s.mutex.Unlock() 38 | 39 | return nil 40 | } 41 | 42 | // Write calls encode to serialize the file set s. 43 | func (s *FileSet) Write(encode func(interface{}) error) error { 44 | var ss serializedFileSet 45 | 46 | s.mutex.Lock() 47 | ss.Base = s.base 48 | files := make([]serializedFile, len(s.files)) 49 | for i, f := range s.files { 50 | files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos} 51 | } 52 | ss.Files = files 53 | s.mutex.Unlock() 54 | 55 | return encode(ss) 56 | } 57 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/token/token.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package token defines constants representing the lexical tokens of the gcfg 6 | // configuration syntax and basic operations on tokens (printing, predicates). 7 | // 8 | // Note that the API for the token package may change to accommodate new 9 | // features or implementation changes in gcfg. 10 | // 11 | package token 12 | 13 | import "strconv" 14 | 15 | // Token is the set of lexical tokens of the gcfg configuration syntax. 16 | type Token int 17 | 18 | // The list of tokens. 19 | const ( 20 | // Special tokens 21 | ILLEGAL Token = iota 22 | EOF 23 | COMMENT 24 | 25 | literal_beg 26 | // Identifiers and basic type literals 27 | // (these tokens stand for classes of literals) 28 | IDENT // section-name, variable-name 29 | STRING // "subsection-name", variable value 30 | literal_end 31 | 32 | operator_beg 33 | // Operators and delimiters 34 | ASSIGN // = 35 | LBRACK // [ 36 | RBRACK // ] 37 | EOL // \n 38 | operator_end 39 | ) 40 | 41 | var tokens = [...]string{ 42 | ILLEGAL: "ILLEGAL", 43 | 44 | EOF: "EOF", 45 | COMMENT: "COMMENT", 46 | 47 | IDENT: "IDENT", 48 | STRING: "STRING", 49 | 50 | ASSIGN: "=", 51 | LBRACK: "[", 52 | RBRACK: "]", 53 | EOL: "\n", 54 | } 55 | 56 | // String returns the string corresponding to the token tok. 57 | // For operators and delimiters, the string is the actual token character 58 | // sequence (e.g., for the token ASSIGN, the string is "="). For all other 59 | // tokens the string corresponds to the token constant name (e.g. for the 60 | // token IDENT, the string is "IDENT"). 61 | // 62 | func (tok Token) String() string { 63 | s := "" 64 | if 0 <= tok && tok < Token(len(tokens)) { 65 | s = tokens[tok] 66 | } 67 | if s == "" { 68 | s = "token(" + strconv.Itoa(int(tok)) + ")" 69 | } 70 | return s 71 | } 72 | 73 | // Predicates 74 | 75 | // IsLiteral returns true for tokens corresponding to identifiers 76 | // and basic type literals; it returns false otherwise. 77 | // 78 | func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } 79 | 80 | // IsOperator returns true for tokens corresponding to operators and 81 | // delimiters; it returns false otherwise. 82 | // 83 | func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } 84 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/bool.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // BoolValues defines the name and value mappings for ParseBool. 4 | var BoolValues = map[string]interface{}{ 5 | "true": true, "yes": true, "on": true, "1": true, 6 | "false": false, "no": false, "off": false, "0": false, 7 | } 8 | 9 | var boolParser = func() *EnumParser { 10 | ep := &EnumParser{} 11 | ep.AddVals(BoolValues) 12 | return ep 13 | }() 14 | 15 | // ParseBool parses bool values according to the definitions in BoolValues. 16 | // Parsing is case-insensitive. 17 | func ParseBool(s string) (bool, error) { 18 | v, err := boolParser.Parse(s) 19 | if err != nil { 20 | return false, err 21 | } 22 | return v.(bool), nil 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/doc.go: -------------------------------------------------------------------------------- 1 | // Package types defines helpers for type conversions. 2 | // 3 | // The API for this package is not finalized yet. 4 | package types 5 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/enum.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // EnumParser parses "enum" values; i.e. a predefined set of strings to 10 | // predefined values. 11 | type EnumParser struct { 12 | Type string // type name; if not set, use type of first value added 13 | CaseMatch bool // if true, matching of strings is case-sensitive 14 | // PrefixMatch bool 15 | vals map[string]interface{} 16 | } 17 | 18 | // AddVals adds strings and values to an EnumParser. 19 | func (ep *EnumParser) AddVals(vals map[string]interface{}) { 20 | if ep.vals == nil { 21 | ep.vals = make(map[string]interface{}) 22 | } 23 | for k, v := range vals { 24 | if ep.Type == "" { 25 | ep.Type = reflect.TypeOf(v).Name() 26 | } 27 | if !ep.CaseMatch { 28 | k = strings.ToLower(k) 29 | } 30 | ep.vals[k] = v 31 | } 32 | } 33 | 34 | // Parse parses the string and returns the value or an error. 35 | func (ep EnumParser) Parse(s string) (interface{}, error) { 36 | if !ep.CaseMatch { 37 | s = strings.ToLower(s) 38 | } 39 | v, ok := ep.vals[s] 40 | if !ok { 41 | return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s) 42 | } 43 | return v, nil 44 | } 45 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/enum_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEnumParserBool(t *testing.T) { 8 | for _, tt := range []struct { 9 | val string 10 | res bool 11 | ok bool 12 | }{ 13 | {val: "tRuE", res: true, ok: true}, 14 | {val: "False", res: false, ok: true}, 15 | {val: "t", ok: false}, 16 | } { 17 | b, err := ParseBool(tt.val) 18 | switch { 19 | case tt.ok && err != nil: 20 | t.Errorf("%q: got error %v, want %v", tt.val, err, tt.res) 21 | case !tt.ok && err == nil: 22 | t.Errorf("%q: got %v, want error", tt.val, b) 23 | case tt.ok && b != tt.res: 24 | t.Errorf("%q: got %v, want %v", tt.val, b, tt.res) 25 | default: 26 | t.Logf("%q: got %v, %v", tt.val, b, err) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/int.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // An IntMode is a mode for parsing integer values, representing a set of 9 | // accepted bases. 10 | type IntMode uint8 11 | 12 | // IntMode values for ParseInt; can be combined using binary or. 13 | const ( 14 | Dec IntMode = 1 << iota 15 | Hex 16 | Oct 17 | ) 18 | 19 | // String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`. 20 | func (m IntMode) String() string { 21 | var modes []string 22 | if m&Dec != 0 { 23 | modes = append(modes, "Dec") 24 | } 25 | if m&Hex != 0 { 26 | modes = append(modes, "Hex") 27 | } 28 | if m&Oct != 0 { 29 | modes = append(modes, "Oct") 30 | } 31 | return "IntMode(" + strings.Join(modes, "|") + ")" 32 | } 33 | 34 | var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix") 35 | 36 | func prefix0(val string) bool { 37 | return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0") 38 | } 39 | 40 | func prefix0x(val string) bool { 41 | return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x") 42 | } 43 | 44 | // ParseInt parses val using mode into intptr, which must be a pointer to an 45 | // integer kind type. Non-decimal value require prefix `0` or `0x` in the cases 46 | // when mode permits ambiguity of base; otherwise the prefix can be omitted. 47 | func ParseInt(intptr interface{}, val string, mode IntMode) error { 48 | val = strings.TrimSpace(val) 49 | verb := byte(0) 50 | switch mode { 51 | case Dec: 52 | verb = 'd' 53 | case Dec + Hex: 54 | if prefix0x(val) { 55 | verb = 'v' 56 | } else { 57 | verb = 'd' 58 | } 59 | case Dec + Oct: 60 | if prefix0(val) && !prefix0x(val) { 61 | verb = 'v' 62 | } else { 63 | verb = 'd' 64 | } 65 | case Dec + Hex + Oct: 66 | verb = 'v' 67 | case Hex: 68 | if prefix0x(val) { 69 | verb = 'v' 70 | } else { 71 | verb = 'x' 72 | } 73 | case Oct: 74 | verb = 'o' 75 | case Hex + Oct: 76 | if prefix0(val) { 77 | verb = 'v' 78 | } else { 79 | return errIntAmbig 80 | } 81 | } 82 | if verb == 0 { 83 | panic("unsupported mode") 84 | } 85 | return ScanFully(intptr, val, verb) 86 | } 87 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/int_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func elem(p interface{}) interface{} { 9 | return reflect.ValueOf(p).Elem().Interface() 10 | } 11 | 12 | func TestParseInt(t *testing.T) { 13 | for _, tt := range []struct { 14 | val string 15 | mode IntMode 16 | exp interface{} 17 | ok bool 18 | }{ 19 | {"0", Dec, int(0), true}, 20 | {"10", Dec, int(10), true}, 21 | {"-10", Dec, int(-10), true}, 22 | {"x", Dec, int(0), false}, 23 | {"0xa", Hex, int(0xa), true}, 24 | {"a", Hex, int(0xa), true}, 25 | {"10", Hex, int(0x10), true}, 26 | {"-0xa", Hex, int(-0xa), true}, 27 | {"0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x 28 | {"-0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x 29 | {"-a", Hex, int(-0xa), true}, 30 | {"-10", Hex, int(-0x10), true}, 31 | {"x", Hex, int(0), false}, 32 | {"10", Oct, int(010), true}, 33 | {"010", Oct, int(010), true}, 34 | {"-10", Oct, int(-010), true}, 35 | {"-010", Oct, int(-010), true}, 36 | {"10", Dec | Hex, int(10), true}, 37 | {"010", Dec | Hex, int(10), true}, 38 | {"0x10", Dec | Hex, int(0x10), true}, 39 | {"10", Dec | Oct, int(10), true}, 40 | {"010", Dec | Oct, int(010), true}, 41 | {"0x10", Dec | Oct, int(0), false}, 42 | {"10", Hex | Oct, int(0), false}, // need prefix to distinguish Hex/Oct 43 | {"010", Hex | Oct, int(010), true}, 44 | {"0x10", Hex | Oct, int(0x10), true}, 45 | {"10", Dec | Hex | Oct, int(10), true}, 46 | {"010", Dec | Hex | Oct, int(010), true}, 47 | {"0x10", Dec | Hex | Oct, int(0x10), true}, 48 | } { 49 | typ := reflect.TypeOf(tt.exp) 50 | res := reflect.New(typ).Interface() 51 | err := ParseInt(res, tt.val, tt.mode) 52 | switch { 53 | case tt.ok && err != nil: 54 | t.Errorf("ParseInt(%v, %#v, %v): fail; got error %v, want ok", 55 | typ, tt.val, tt.mode, err) 56 | case !tt.ok && err == nil: 57 | t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want error", 58 | typ, tt.val, tt.mode, elem(res)) 59 | case tt.ok && !reflect.DeepEqual(elem(res), tt.exp): 60 | t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want %v", 61 | typ, tt.val, tt.mode, elem(res), tt.exp) 62 | default: 63 | t.Logf("ParseInt(%v, %#v, %s): pass; got %v, error %v", 64 | typ, tt.val, tt.mode, elem(res), err) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/scan.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "reflect" 7 | ) 8 | 9 | // ScanFully uses fmt.Sscanf with verb to fully scan val into ptr. 10 | func ScanFully(ptr interface{}, val string, verb byte) error { 11 | t := reflect.ValueOf(ptr).Elem().Type() 12 | // attempt to read extra bytes to make sure the value is consumed 13 | var b []byte 14 | n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b) 15 | switch { 16 | case n < 1 || n == 1 && err != io.EOF: 17 | return fmt.Errorf("failed to parse %q as %v: %v", val, t, err) 18 | case n > 1: 19 | return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b)) 20 | } 21 | // n == 1 && err == io.EOF 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopkg.in/gcfg.v1/types/scan_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestScanFully(t *testing.T) { 9 | for _, tt := range []struct { 10 | val string 11 | verb byte 12 | res interface{} 13 | ok bool 14 | }{ 15 | {"a", 'v', int(0), false}, 16 | {"0x", 'v', int(0), true}, 17 | {"0x", 'd', int(0), false}, 18 | } { 19 | d := reflect.New(reflect.TypeOf(tt.res)).Interface() 20 | err := ScanFully(d, tt.val, tt.verb) 21 | switch { 22 | case tt.ok && err != nil: 23 | t.Errorf("ScanFully(%T, %q, '%c'): want ok, got error %v", 24 | d, tt.val, tt.verb, err) 25 | case !tt.ok && err == nil: 26 | t.Errorf("ScanFully(%T, %q, '%c'): want error, got %v", 27 | d, tt.val, tt.verb, elem(d)) 28 | case tt.ok && err == nil && !reflect.DeepEqual(tt.res, elem(d)): 29 | t.Errorf("ScanFully(%T, %q, '%c'): want %v, got %v", 30 | d, tt.val, tt.verb, tt.res, elem(d)) 31 | default: 32 | t.Logf("ScanFully(%T, %q, '%c') = %v; *ptr==%v", 33 | d, tt.val, tt.verb, err, elem(d)) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "", 4 | "package": [ 5 | { 6 | "path": "appengine/cloudsql", 7 | "revision": "" 8 | }, 9 | { 10 | "checksumSHA1": "3xVanMXn/lblhNfZYORorpd8N6E=", 11 | "path": "github.com/go-sql-driver/mysql", 12 | "revision": "0b58b37b664c21f3010e836f1b931e1d0b0b0685", 13 | "revisionTime": "2016-08-02T11:38:42Z" 14 | }, 15 | { 16 | "checksumSHA1": "Em+idtZv9NvuTvzwBnkcCjv/j40=", 17 | "path": "github.com/patrickmn/go-cache", 18 | "revision": "1881a9bccb818787f68c52bfba648c6cf34c34fa", 19 | "revisionTime": "2016-01-27T17:00:04Z" 20 | } 21 | ], 22 | "rootPath": "github.com/outbrain/orchestrator" 23 | } 24 | --------------------------------------------------------------------------------