Initial commit

This commit is contained in:
Kevin P. Fleming
2018-12-04 13:33:45 -05:00
commit fa643e9be8
51 changed files with 7153 additions and 0 deletions

93
.gitignore vendored Normal file
View File

@@ -0,0 +1,93 @@
vendor/
bin/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject

23
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,23 @@
# Contributing to `Goldpinger`
If you'd like to help us improve and extend this project, then we welcome your contributions!
Below you will find some basic steps required to be able to contribute to the project. If you have any questions about this process or any other aspect of contributing to a Bloomberg open source project, feel free to send an email to opensource@bloomberg.net and we'll get your questions answered as quickly as we can.
Pull Requests are always welcome, however they will only be accepted if they provide a reasonably test coverage.
## Contribution Licensing
Since `Goldpinger` is distributed under the terms of the [Apache Version 2 license](LICENSE), contributions that you make are licensed under the same terms. In order for us to be able to accept your contributions, we will need explicit confirmation from you that you are able and willing to provide them under these terms, and the mechanism we use to do this is called a Developer's Certificate of Origin [DCO](DCO.md). This is very similar to the process used by the Linux(R) kernel, Samba, and many other major open source projects.
To participate under these terms, all that you must do is include a line like the following as the last line of the commit message for each commit in your contribution:
Signed-Off-By: Random J. Developer <random@developer.example.org>
The simplest way to accomplish this is to add `-s` or `--signoff` to your `git commit` command.
You must use your real name (sorry, no pseudonyms, and no anonymous contributions).
## Help / Documentation
The [README](README.md) is the best place to start.

25
DCO.md Normal file
View File

@@ -0,0 +1,25 @@
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
1. The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
2. The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
3. The contribution was provided directly to me by some other
person who certified (1), (2) or (3) and I have not modified
it.
4. I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

636
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,636 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:d1665c44bd5db19aaee18d1b6233c99b0b9a986e8bccb24ef54747547a48027f"
name = "github.com/PuerkitoBio/purell"
packages = ["."]
pruneopts = "UT"
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:c739832d67eb1e9cc478a19cc1a1ccd78df0397bf8a32978b759152e205f644b"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
pruneopts = "UT"
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
digest = "1:320e7ead93de9fd2b0e59b50fd92a4d50c1f8ab455d96bc2eb083267453a9709"
name = "github.com/asaskevich/govalidator"
packages = ["."]
pruneopts = "UT"
revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
version = "v9"
[[projects]]
branch = "master"
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "UT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:6f82cacd0af5921e99bf3f46748705239b36489464f4529a1589bc895764fb18"
name = "github.com/docker/go-units"
packages = ["."]
pruneopts = "UT"
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
version = "v0.3.3"
[[projects]]
digest = "1:c45cef8e0074ea2f8176a051df38553ba997a3616f1ec2d35222b1cf9864881e"
name = "github.com/ghodss/yaml"
packages = ["."]
pruneopts = "UT"
revision = "73d445a93680fa1a78ae23a5839bad48f32ba1ee"
[[projects]]
branch = "master"
digest = "1:7fb51688eadf38272411852d7a2b3538c7caff53309abee6c0964a83c00fe69e"
name = "github.com/globalsign/mgo"
packages = [
"bson",
"internal/json",
]
pruneopts = "UT"
revision = "eeefdecb41b842af6dc652aaea4026e8403e62df"
[[projects]]
digest = "1:c49164b7b1e34324258ae61deef2cba7912005ba9cb7a9ee4930fe6bdfec7b5d"
name = "github.com/go-openapi/analysis"
packages = [
".",
"internal",
]
pruneopts = "UT"
revision = "c701774f4e604d952e4e8c56dee260be696e33c3"
version = "v0.17.2"
[[projects]]
digest = "1:ac4b35a4bba11edb2110fca0707bae03ae92fbd8222e6b483465d98efaabfb97"
name = "github.com/go-openapi/errors"
packages = ["."]
pruneopts = "UT"
revision = "d9664f9fab8994271e573ed69cf2adfc09b7a800"
version = "v0.17.2"
[[projects]]
digest = "1:953a2628e4c5c72856b53f5470ed5e071c55eccf943d798d42908102af2a610f"
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
pruneopts = "UT"
revision = "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004"
version = "v0.17.2"
[[projects]]
digest = "1:81210e0af657a0fb3638932ec68e645236bceefa4c839823db0c4d918f080895"
name = "github.com/go-openapi/jsonreference"
packages = ["."]
pruneopts = "UT"
revision = "8483a886a90412cd6858df4ea3483dce9c8e35a3"
version = "v0.17.2"
[[projects]]
digest = "1:a20e8bf0e58e2010677432ffbe5533c1e83bdf368ba5b057f3e00e2071ca8b09"
name = "github.com/go-openapi/loads"
packages = ["."]
pruneopts = "UT"
revision = "150d36912387ec2f607be674c5be309ddccc0eed"
version = "v0.17.2"
[[projects]]
digest = "1:76b781f51e08cc0494b4abacf70d4b9da0a4732e270b7f2045ca9f0f875ad6d1"
name = "github.com/go-openapi/runtime"
packages = [
".",
"client",
"flagext",
"logger",
"middleware",
"middleware/denco",
"middleware/header",
"middleware/untyped",
"security",
]
pruneopts = "UT"
revision = "231d7876b7019dbcbfc97a7ba764379497b67c1d"
version = "v0.17.2"
[[projects]]
digest = "1:394fed5c0425fe01da3a34078adaa1682e4deaea6e5d232dde25c4034004c151"
name = "github.com/go-openapi/spec"
packages = ["."]
pruneopts = "UT"
revision = "5bae59e25b21498baea7f9d46e9c147ec106a42e"
version = "v0.17.2"
[[projects]]
digest = "1:ffa79f4705a3a85f2412d2d163c37acdf60d128c679e641c323a5de712e23d27"
name = "github.com/go-openapi/strfmt"
packages = ["."]
pruneopts = "UT"
revision = "edab9990ffc9b4a428f3306ecf4d18a069ca3317"
version = "v0.17.2"
[[projects]]
digest = "1:32f3d2e7f343de7263c550d696fb8a64d3c49d8df16b1ddfc8e80e1e4b3233ce"
name = "github.com/go-openapi/swag"
packages = ["."]
pruneopts = "UT"
revision = "5899d5c5e619fda5fa86e14795a835f473ca284c"
version = "v0.17.2"
[[projects]]
digest = "1:58541fddf3f4ec485710f1b346e7f647baf09a878a604e47e3668c600fe44076"
name = "github.com/go-openapi/validate"
packages = ["."]
pruneopts = "UT"
revision = "d2eab7d93009e9215fc85b2faa2c2f2a98c2af48"
version = "v0.17.2"
[[projects]]
digest = "1:f83d740263b44fdeef3e1bce6147b5d7283fcad1a693d39639be33993ecf3db1"
name = "github.com/gogo/protobuf"
packages = [
"proto",
"sortkeys",
]
pruneopts = "UT"
revision = "c0656edd0d9eab7c66d1eb0c568f9039345796f7"
[[projects]]
digest = "1:2edd2416f89b4e841df0e4a78802ce14d2bc7ad79eba1a45986e39f0f8cb7d87"
name = "github.com/golang/glog"
packages = ["."]
pruneopts = "UT"
revision = "44145f04b68cf362d9c4df2182967c2275eaefed"
[[projects]]
digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf"
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp",
]
pruneopts = "UT"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:0bfbe13936953a98ae3cfe8ed6670d396ad81edf069a806d2f6515d7bb6950df"
name = "github.com/google/btree"
packages = ["."]
pruneopts = "UT"
revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306"
[[projects]]
digest = "1:41bfd4219241b7f7d6e6fdb13fc712576f1337e68e6b895136283b76928fdd66"
name = "github.com/google/gofuzz"
packages = ["."]
pruneopts = "UT"
revision = "44d81051d367757e1c7c6a5a86423ece9afcf63c"
[[projects]]
digest = "1:75eb87381d25cc75212f52358df9c3a2719584eaa9685cd510ce28699122f39d"
name = "github.com/googleapis/gnostic"
packages = [
"OpenAPIv2",
"compiler",
"extensions",
]
pruneopts = "UT"
revision = "0c5108395e2debce0d731cf0287ddf7242066aba"
[[projects]]
digest = "1:878f0defa9b853f9acfaf4a162ba450a89d0050eff084f9fe7f5bd15948f172a"
name = "github.com/gregjones/httpcache"
packages = [
".",
"diskcache",
]
pruneopts = "UT"
revision = "787624de3eb7bd915c329cba748687a3b22666a6"
[[projects]]
digest = "1:3e260afa138eab6492b531a3b3d10ab4cb70512d423faa78b8949dec76e66a21"
name = "github.com/imdario/mergo"
packages = ["."]
pruneopts = "UT"
revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58"
version = "v0.3.5"
[[projects]]
digest = "1:a2cff208d4759f6ba1b1cd228587b0a1869f95f22542ec9cd17fff64430113c7"
name = "github.com/jessevdk/go-flags"
packages = ["."]
pruneopts = "UT"
revision = "c6ca198ec95c841fdb89fc0de7496fed11ab854e"
version = "v1.4.0"
[[projects]]
digest = "1:bb3cc4c1b21ea18cfa4e3e47440fc74d316ab25b0cf42927e8c1274917bd9891"
name = "github.com/json-iterator/go"
packages = ["."]
pruneopts = "UT"
revision = "f2b4162afba35581b6d4a50d3b8f34e33c144682"
[[projects]]
branch = "master"
digest = "1:84a5a2b67486d5d67060ac393aa255d05d24ed5ee41daecd5635ec22657b6492"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
"jwriter",
]
pruneopts = "UT"
revision = "60711f1a8329503b04e1c88535f419d0bb440bff"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "UT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"
[[projects]]
digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
name = "github.com/modern-go/concurrent"
packages = ["."]
pruneopts = "UT"
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
version = "1.0.3"
[[projects]]
digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855"
name = "github.com/modern-go/reflect2"
packages = ["."]
pruneopts = "UT"
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[[projects]]
branch = "master"
digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2"
name = "github.com/petar/GoLLRB"
packages = ["llrb"]
pruneopts = "UT"
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
[[projects]]
digest = "1:0e7775ebbcf00d8dd28ac663614af924411c868dca3d5aa762af0fae3808d852"
name = "github.com/peterbourgon/diskv"
packages = ["."]
pruneopts = "UT"
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
version = "v2.0.1"
[[projects]]
digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/internal",
"prometheus/promhttp",
]
pruneopts = "UT"
revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3"
version = "v0.9.1"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "UT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
branch = "master"
digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model",
]
pruneopts = "UT"
revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
[[projects]]
branch = "master"
digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs",
]
pruneopts = "UT"
revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
[[projects]]
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = "UT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
[[projects]]
branch = "master"
digest = "1:eb8583d4582ffbc5c6d3ec00132cd0835101edde42b6f24eaafa628d1596f881"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"netutil",
]
pruneopts = "UT"
revision = "10aee181995363b41f712a55844a0dd52ea04646"
[[projects]]
digest = "1:9359217acc6040b4be710ce34473acef28023ad39bfafecea34ffaea7f1e1890"
name = "golang.org/x/oauth2"
packages = [
".",
"internal",
]
pruneopts = "UT"
revision = "a6bd8cefa1811bd24b86f8902872e4e8225f74c4"
[[projects]]
branch = "master"
digest = "1:509feef845f6af582cb82caad5eb176016b30714facdc754d84869ca31305b59"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "UT"
revision = "7155702f2d47d94b134229da97195d0130cab001"
[[projects]]
digest = "1:0c56024909189aee3364b7f21a95a27459f718aa7c199a5c111c36cfffd9eaef"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
digest = "1:d37b0ef2944431fe9e8ef35c6fffc8990d9e2ca300588df94a6890f3649ae365"
name = "golang.org/x/time"
packages = ["rate"]
pruneopts = "UT"
revision = "f51c12702a4d776e4c1fa9b0fabab841babae631"
[[projects]]
digest = "1:08206298775e5b462e6c0333f4471b44e63f1a70e42952b6ede4ecc9572281eb"
name = "google.golang.org/appengine"
packages = [
"internal",
"internal/base",
"internal/datastore",
"internal/log",
"internal/remote_api",
"internal/urlfetch",
"urlfetch",
]
pruneopts = "UT"
revision = "4a4468ece617fc8205e99368fa2200e9d1fad421"
version = "v1.3.0"
[[projects]]
digest = "1:ef72505cf098abdd34efeea032103377bec06abb61d8a06f002d5d296a4b1185"
name = "gopkg.in/inf.v0"
packages = ["."]
pruneopts = "UT"
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
version = "v0.9.0"
[[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[[projects]]
digest = "1:910ec974550174f4ca48b9f4a3caec16b693e584c3762dc726dc0dcf28f8e318"
name = "k8s.io/api"
packages = [
"admissionregistration/v1alpha1",
"admissionregistration/v1beta1",
"apps/v1",
"apps/v1beta1",
"apps/v1beta2",
"authentication/v1",
"authentication/v1beta1",
"authorization/v1",
"authorization/v1beta1",
"autoscaling/v1",
"autoscaling/v2beta1",
"autoscaling/v2beta2",
"batch/v1",
"batch/v1beta1",
"batch/v2alpha1",
"certificates/v1beta1",
"coordination/v1beta1",
"core/v1",
"events/v1beta1",
"extensions/v1beta1",
"networking/v1",
"policy/v1beta1",
"rbac/v1",
"rbac/v1alpha1",
"rbac/v1beta1",
"scheduling/v1alpha1",
"scheduling/v1beta1",
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
"storage/v1beta1",
]
pruneopts = "UT"
revision = "fd83cbc87e7632ccd8bbab63d2b673d4e0c631cc"
version = "kubernetes-1.12.0"
[[projects]]
digest = "1:18f352651d6e8578fdbaf29c68334b042439b288b8ae4112c2b2ba9a6e35ced0"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/errors",
"pkg/api/meta",
"pkg/api/resource",
"pkg/apis/meta/v1",
"pkg/apis/meta/v1/unstructured",
"pkg/apis/meta/v1beta1",
"pkg/conversion",
"pkg/conversion/queryparams",
"pkg/fields",
"pkg/labels",
"pkg/runtime",
"pkg/runtime/schema",
"pkg/runtime/serializer",
"pkg/runtime/serializer/json",
"pkg/runtime/serializer/protobuf",
"pkg/runtime/serializer/recognizer",
"pkg/runtime/serializer/streaming",
"pkg/runtime/serializer/versioning",
"pkg/selection",
"pkg/types",
"pkg/util/clock",
"pkg/util/errors",
"pkg/util/framer",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/naming",
"pkg/util/net",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/validation",
"pkg/util/validation/field",
"pkg/util/yaml",
"pkg/version",
"pkg/watch",
"third_party/forked/golang/reflect",
]
pruneopts = "UT"
revision = "6dd46049f39503a1fc8d65de4bd566829e95faff"
version = "kubernetes-1.12.0"
[[projects]]
digest = "1:51fd9ac9f2be10d79f5af101a7a1d758ef283fdb028a0d11198754bd3d4a6020"
name = "k8s.io/client-go"
packages = [
"discovery",
"kubernetes",
"kubernetes/scheme",
"kubernetes/typed/admissionregistration/v1alpha1",
"kubernetes/typed/admissionregistration/v1beta1",
"kubernetes/typed/apps/v1",
"kubernetes/typed/apps/v1beta1",
"kubernetes/typed/apps/v1beta2",
"kubernetes/typed/authentication/v1",
"kubernetes/typed/authentication/v1beta1",
"kubernetes/typed/authorization/v1",
"kubernetes/typed/authorization/v1beta1",
"kubernetes/typed/autoscaling/v1",
"kubernetes/typed/autoscaling/v2beta1",
"kubernetes/typed/autoscaling/v2beta2",
"kubernetes/typed/batch/v1",
"kubernetes/typed/batch/v1beta1",
"kubernetes/typed/batch/v2alpha1",
"kubernetes/typed/certificates/v1beta1",
"kubernetes/typed/coordination/v1beta1",
"kubernetes/typed/core/v1",
"kubernetes/typed/events/v1beta1",
"kubernetes/typed/extensions/v1beta1",
"kubernetes/typed/networking/v1",
"kubernetes/typed/policy/v1beta1",
"kubernetes/typed/rbac/v1",
"kubernetes/typed/rbac/v1alpha1",
"kubernetes/typed/rbac/v1beta1",
"kubernetes/typed/scheduling/v1alpha1",
"kubernetes/typed/scheduling/v1beta1",
"kubernetes/typed/settings/v1alpha1",
"kubernetes/typed/storage/v1",
"kubernetes/typed/storage/v1alpha1",
"kubernetes/typed/storage/v1beta1",
"pkg/apis/clientauthentication",
"pkg/apis/clientauthentication/v1alpha1",
"pkg/apis/clientauthentication/v1beta1",
"pkg/version",
"plugin/pkg/client/auth/exec",
"rest",
"rest/watch",
"tools/auth",
"tools/clientcmd",
"tools/clientcmd/api",
"tools/clientcmd/api/latest",
"tools/clientcmd/api/v1",
"tools/metrics",
"tools/reference",
"transport",
"util/cert",
"util/connrotation",
"util/flowcontrol",
"util/homedir",
"util/integer",
]
pruneopts = "UT"
revision = "1638f8970cefaa404ff3a62950f88b08292b2696"
version = "v9.0.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/go-openapi/errors",
"github.com/go-openapi/loads",
"github.com/go-openapi/runtime",
"github.com/go-openapi/runtime/client",
"github.com/go-openapi/runtime/flagext",
"github.com/go-openapi/runtime/middleware",
"github.com/go-openapi/runtime/security",
"github.com/go-openapi/spec",
"github.com/go-openapi/strfmt",
"github.com/go-openapi/swag",
"github.com/go-openapi/validate",
"github.com/jessevdk/go-flags",
"github.com/prometheus/client_golang/prometheus",
"github.com/prometheus/client_golang/prometheus/promhttp",
"golang.org/x/net/context",
"golang.org/x/net/netutil",
"k8s.io/apimachinery/pkg/apis/meta/v1",
"k8s.io/client-go/kubernetes",
"k8s.io/client-go/tools/clientcmd",
]
solver-name = "gps-cdcl"
solver-version = 1

78
Gopkg.toml Normal file
View File

@@ -0,0 +1,78 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/go-openapi/errors"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/loads"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/runtime"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/spec"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/strfmt"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/swag"
version = "0.17.2"
[[constraint]]
name = "github.com/go-openapi/validate"
version = "0.17.2"
[[constraint]]
name = "github.com/jessevdk/go-flags"
version = "1.4.0"
[[constraint]]
name = "github.com/prometheus/client_golang"
version = "0.9.1"
[[constraint]]
branch = "master"
name = "golang.org/x/net"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.12.0"
[[constraint]]
name = "k8s.io/client-go"
version = "9.0.0"
[prune]
go-tests = true
unused-packages = true

202
LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Bloomberg Finance L.P.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

36
Makefile Normal file
View File

@@ -0,0 +1,36 @@
name ?= goldpinger
version ?= 1.0.1
bin ?= goldpinger
pkg ?= "github.com/bloomberg/goldpinger.git"
tag = $(name):$(version)
namespace ?= ""
files = $(shell find . -iname "*.go")
clean:
rm -rf ./vendor
rm -f ./bin/$(bin)
vendor:
rm -rf ./vendor
dep ensure -v
swagger:
swagger generate server -t pkg -f ./swagger.yml --exclude-main -A goldpinger && \
swagger generate client -t pkg -f ./swagger.yml -A goldpinger
bin/$(bin): $(files)
PKG=${pkg} ARCH=amd64 VERSION=${version} BIN=${bin} ./build/build.sh
build: bin/$(bin)
sudo docker build -t $(tag) -f ./build/Dockerfile .
tag:
sudo docker tag $(tag) $(namespace)$(tag)
push:
sudo docker push $(namespace)$(tag)
run:
go run ./cmd/goldpinger/main.go
.PHONY: clean vendor build-swagger build tag push run

196
README.md Normal file
View File

@@ -0,0 +1,196 @@
# Goldpinger
__Goldpinger__ makes calls between its instances for visibility and alerting.
It runs as a `DaemonSet` on `Kubernetes` and produces `Prometheus` metrics that can be scaped, visualised and alerted on.
Oh, and it gives you the graph below for your cluster.
![](./extras/screenshot.png)
## On the menu
- [Rationale](#rationale)
- [Building](#building)
- [Installation](#installation)
- [Authentication with Kubernetes API](#authentication-with-kubernetes-api)
- [Example YAML](#example-yaml)
- [Usage](#usage)
- [UI](#ui)
- [API](#api)
- [Prometheus](#prometheus)
- [Grafana](#grafana)
- [Alert Manager](#alert-manager)
- [Contributions](#contributions)
- [License](#license)
## Rationale
We built __Goldpinger__ to troubleshoot, visualise and alert on our networking layer, while adopting `Kubernetes` at Bloomberg. It has since become the goto tool to see connectivity and slowness issues.
It's small, simple and you'll wonder why you hadn't had it before.
## Building
In order to build `Goldpinger`, you are going to need `go` version 1.10+, `dep`, and `docker`.
Building from source code consists of compiling the binary and building a [Docker image](./build/Dockerfile):
```sh
# step 0: check out the code into your $GOPATH
# step 1: download the dependencies via dep ensure
make vendor
# step 2: compile the binary
make bin/goldpinger
# at this stage you should be able to run the binary
./bin/goldpinger --help
# step 3: build the docker image containing the binary
make build
# step 4: push the image somewhere
namespace="docker.io/myhandle/" make tag
namespace="docker.io/myhandle/" make push
```
## Installation
`Goldpinger` works by asking `Kubernetes` for pods with particular labels (`app=goldpinger`). While you can deploy `Golpinger` in a variety of ways, it works very nicely as a `DaemonSet` out of the box.
### Authentication with Kubernetes API
`Goldpinger` supports using a `kubeconfig` (specify with `--kubeconfig-path`) or service accounts.
### Example YAML
Here's an example of what you can do (using the in-cluster authentication to `Kubernetes` apiserver):
```yaml
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: goldpinger
spec:
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: goldpinger
version: "1.0.0"
template:
metadata:
labels:
app: goldpinger
version: "1.0.0"
spec:
containers:
- name: goldpinger
env:
- name: HOST
value: "0.0.0.0"
- name: PORT
value: "80"
# refresh interval (seconds) tells goldpinger to call every other instance with that frequency (set to 0 to disable)
- name: REFRESH_INTERVAL
value: "30"
# injecting real hostname will make for easier to understand graphs/metrics
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
image: "docker.io/mynamespace-replaceme/goldpinger:1.0.0"
ports:
- containerPort: 80
name: http
---
apiVersion: v1
kind: Service
metadata:
name: goldpinger
labels:
app: goldpinger
spec:
type: NodePort
ports:
- port: 80
nodePort: 30080
name: http
selector:
app: goldpinger
```
You can also see [an example of using `kubeconfig` in the `./extras`](./extras/example-with-kubeconfig.yaml).
## Usage
### UI
Once you have it running, you can hit any of the nodes (port 30080 in the example above) and see the UI.
![](./extras/screenshot.png)
You can click on various nodes to gray out the clutter and see more information.
### API
The API exposed is via a well-defined [`Swagger` spec](./swagger.yml).
The spec is used to generate both the server and the client of `Goldpinger`. If you make changes, you can re-generate them using [go-swagger](https://github.com/go-swagger/go-swagger) via [`make swagger`](./Makefile)
### Prometheus
Once running, `Goldpinger` exposes `Prometheus` metrics at `/metrics`. All the metrics are prefixed with `goldpinger_` for easy identification.
You can see the metrics by doing a `curl http://$POD_ID:80/metrics`.
These are probably the droids you are looking for:
```sh
goldpinger_peers_response_time_s_*
goldpinger_peers_response_time_s_*
goldpinger_nodes_health_total
goldpinger_stats_total
goldpinger_errors_total
```
### Grafana
You can find an example of a `Grafana` dashboard that shows what's going on in your cluster in [extras](./extras/goldpinger-dashboard.json). This should get you started, and once you're on the roll, why not :heart: contribute some kickass dashboards for others to use ?
### Alert Manager
Once you've gotten your metrics into `Prometheus`, you have all you need to set useful alerts.
To get you started, here's a rule that will trigger an alert if there are any nodes reported as unhealthy by any instance of `Goldpinger`.
```yaml
alert: goldpinger_nodes_unhealthy
expr: sum(goldpinger_nodes_healthy_total{status="unhealthy"})
BY (goldpinger_instance) > 0
for: 5m
annotations:
description: |
Goldpinger instance {{ $labels.goldpinger_instance }} has been reporting unhealthy nodes for at least 5 minutes.
summary: Instance {{ $labels.instance }} down
```
Similarly, why not :heart: contribute some amazing alerts for others to use ?
## Contributions
We :heart: contributions.
Have you had a good experience with `Goldpinger` ? Why not share some love and contribute code, dashboards and alerts ?
If you're thinking of making some code changes, please be aware that most of the code is auto-generated from the `Swagger` spec. The spec is used to generate both the server and the client of `Goldpinger`. If you make changes, you can re-generate them using [go-swagger](https://github.com/go-swagger/go-swagger) via [`make swagger`](./Makefile).
Before you create that PR, please make sure you read [CONTRIBUTING](./CONTRIBUTING.md) and [DCO](./DCO.md).
## License
Please read the [LICENSE](./LICENSE) file here.

100
cmd/goldpinger/main.go Normal file
View File

@@ -0,0 +1,100 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"log"
"os"
"github.com/go-openapi/loads"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/bloomberg/goldpinger/pkg/goldpinger"
"github.com/bloomberg/goldpinger/pkg/restapi"
"github.com/bloomberg/goldpinger/pkg/restapi/operations"
flags "github.com/jessevdk/go-flags"
)
func main() {
// load embedded swagger file
swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
if err != nil {
log.Fatalln(err)
}
// create new service API
api := operations.NewGoldpingerAPI(swaggerSpec)
server := restapi.NewServer(api)
defer server.Shutdown()
parser := flags.NewParser(server, flags.Default)
parser.ShortDescription = "Goldpinger"
parser.LongDescription = swaggerSpec.Spec().Info.Description
// parse flags
server.ConfigureFlags()
for _, optsGroup := range api.CommandLineOptionsGroups {
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
if err != nil {
log.Fatalln(err)
}
}
if _, err := parser.Parse(); err != nil {
code := 1
if fe, ok := err.(*flags.Error); ok {
if fe.Type == flags.ErrHelp {
code = 0
}
}
os.Exit(code)
}
// make a kubernetes client
var config *rest.Config
if goldpinger.GoldpingerConfig.KubeConfigPath == "" {
log.Println("Kubeconfig not specified, trying to use in cluster config")
config, err = rest.InClusterConfig()
} else {
log.Println("Kubeconfig specified in ", goldpinger.GoldpingerConfig.KubeConfigPath)
config, err = clientcmd.BuildConfigFromFlags("", goldpinger.GoldpingerConfig.KubeConfigPath)
}
if err != nil {
log.Fatalln("Error getting config ", err.Error())
}
// create the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatalln("kubernetes.NewForConfig error ", err.Error())
}
goldpinger.GoldpingerConfig.KubernetesClient = clientset
// Check if we have an override for the client, default to own port
if goldpinger.GoldpingerConfig.Port == 0 {
goldpinger.GoldpingerConfig.Port = server.Port
}
server.ConfigureAPI()
goldpinger.StartUpdater()
log.Println("All good, starting serving the API")
// serve API
if err := server.Serve(); err != nil {
log.Fatalln(err)
}
}

4
extras/README.md Normal file
View File

@@ -0,0 +1,4 @@
# Extras
You will find here examples of how to use and configure `Goldpinger`.

View File

@@ -0,0 +1,68 @@
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: goldpinger
labels:
app: goldpinger
version: "1.0.0"
spec:
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: goldpinger
version: "1.0.0"
template:
metadata:
labels:
app: goldpinger
version: "1.0.0"
spec:
# if you'd like to use a secret to inject a kubeconfig, you can do it like this
volumes:
- name: kubeconfig
secret:
secretName: goldpinger-kubeconfig
containers:
- name: goldpinger
env:
- name: HOST
value: "0.0.0.0"
- name: PORT
value: "80"
# kubeconfig needs to match the location of what's injected in the secret
# if not specified goldpinger will default to using in-cluster config
- name: KUBECONFIG
value: "./kube/config"
# refresh interval (seconds) tells goldpinger to call every other instance with that frequency (set to 0 to disable)
- name: REFRESH_INTERVAL
value: "30"
# injecting real hostname will make for easier to understand graphs/metrics
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
image: "docker.io/mynamespace-replaceme/goldpinger:1.0.0"
ports:
- containerPort: 80
name: http
volumeMounts:
- mountPath: /.kube/
name: kubeconfig
readOnly: true
---
apiVersion: v1
kind: Service
metadata:
name: goldpinger
labels:
app: goldpinger
spec:
type: NodePort
ports:
- port: 80
nodePort: 30080
name: http
selector:
app: goldpinger

View File

@@ -0,0 +1,919 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"hideControls": false,
"id": 1,
"links": [],
"refresh": "10s",
"rows": [
{
"collapse": false,
"height": 166,
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": true,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(55, 174, 50, 0.97)"
],
"datasource": "K8S",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"id": 9,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": true
},
"tableColumn": "",
"targets": [
{
"expr": "(count(goldpinger_nodes_health_total{status='healthy'}) + count(goldpinger_nodes_health_total{status='unhealthy'})) /2",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "",
"metric": "goldpinger_nodes_health",
"refId": "A",
"step": 600
}
],
"thresholds": "31,32",
"title": "Goldpinger Nodes",
"type": "singlestat",
"valueFontSize": "150%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"columns": [
{
"text": "Current",
"value": "current"
},
{
"text": "Max",
"value": "max"
},
{
"text": "Avg",
"value": "avg"
}
],
"datasource": "K8S",
"editable": true,
"error": false,
"fontSize": "100%",
"id": 11,
"links": [],
"pageSize": null,
"scroll": true,
"showHeader": true,
"sort": {
"col": null,
"desc": false
},
"span": 8,
"styles": [
{
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"pattern": "Time",
"type": "date"
},
{
"colorMode": "value",
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"decimals": 2,
"pattern": "/.*/",
"thresholds": [
"1",
"2"
],
"type": "number",
"unit": "short"
}
],
"targets": [
{
"expr": "sum(goldpinger_nodes_health_total{status='unhealthy'}) by (goldpinger_instance)",
"format": "table",
"intervalFactor": 2,
"legendFormat": "{{ goldpinger_instance }}",
"refId": "A",
"step": 40
}
],
"title": "Unhealthy seen by instance",
"transform": "table",
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 10,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null as zero",
"percentage": false,
"pointradius": 2,
"points": true,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "goldpinger_nodes_health_total{status=\"unhealthy\"}",
"format": "time_series",
"interval": "1m",
"intervalFactor": 1,
"legendFormat": "{{ goldpinger_instance }}",
"metric": "goldpinger_nodes_health",
"refId": "A",
"step": 60
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Unhealthy nodes reported",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 12,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "increase(goldpinger_nodes_health_total{status=\"unhealthy\"}[30m])",
"format": "time_series",
"interval": "5m",
"intervalFactor": 1,
"legendFormat": "{{ goldpinger_instance }}",
"metric": "goldpinger_nodes_health",
"refId": "A",
"step": 300
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Unhealthy nodes increase [30m]",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": true,
"title": "Overall Health",
"titleSize": "h6"
},
{
"collapse": false,
"height": "200",
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 6,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(increase(goldpinger_peers_response_time_s_sum[30m])) by (host_ip)",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 120
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Increase in response time from node [30m]",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 1,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "avg(goldpinger_peers_response_time_s_sum{call_type=\"ping\"}/goldpinger_peers_response_time_s_count{call_type=\"ping\"}) by (host_ip)",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 30
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Response time from node (AVG)",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 5,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "max(goldpinger_peers_response_time_s_sum{call_type=\"ping\"}/goldpinger_peers_response_time_s_count{call_type=\"ping\"}) by (host_ip)",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 30
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Response time from node (MAX)",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": true,
"title": "Connections to peers",
"titleSize": "h6"
},
{
"collapse": false,
"height": 209,
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"height": "",
"id": 13,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(increase(goldpinger_kube_master_response_time_s_sum[30m])) by (host_ip)",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 120
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Increase in response time from k8s api [30m]",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"height": "",
"id": 14,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "avg(goldpinger_kube_master_response_time_s_sum/goldpinger_kube_master_response_time_s_count) by (host_ip)",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 30
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Response time from k8s (AVG)",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "K8S",
"editable": true,
"error": false,
"fill": 1,
"grid": {},
"id": 15,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "max(goldpinger_kube_master_response_time_s_sum/goldpinger_kube_master_response_time_s_count) by (host_ip)",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "{{ host_ip }}",
"refId": "A",
"step": 30
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Response time from k8s (MAX)",
"tooltip": {
"msResolution": true,
"shared": false,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": true,
"title": "Connections to Kubernetes API",
"titleSize": "h6"
},
{
"collapse": false,
"height": 250,
"panels": [],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6"
}
],
"schemaVersion": 14,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "browser",
"title": "Cluster: Goldpinger",
"version": 3
}

BIN
extras/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -0,0 +1,131 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package client
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
strfmt "github.com/go-openapi/strfmt"
"github.com/bloomberg/goldpinger/pkg/client/operations"
)
// Default goldpinger HTTP client.
var Default = NewHTTPClient(nil)
const (
// DefaultHost is the default Host
// found in Meta (info) section of spec file
DefaultHost string = "localhost"
// DefaultBasePath is the default BasePath
// found in Meta (info) section of spec file
DefaultBasePath string = "/"
)
// DefaultSchemes are the default schemes found in Meta (info) section of spec file
var DefaultSchemes = []string{"http"}
// NewHTTPClient creates a new goldpinger HTTP client.
func NewHTTPClient(formats strfmt.Registry) *Goldpinger {
return NewHTTPClientWithConfig(formats, nil)
}
// NewHTTPClientWithConfig creates a new goldpinger HTTP client,
// using a customizable transport config.
func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *Goldpinger {
// ensure nullable parameters have default
if cfg == nil {
cfg = DefaultTransportConfig()
}
// create transport and client
transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes)
return New(transport, formats)
}
// New creates a new goldpinger client
func New(transport runtime.ClientTransport, formats strfmt.Registry) *Goldpinger {
// ensure nullable parameters have default
if formats == nil {
formats = strfmt.Default
}
cli := new(Goldpinger)
cli.Transport = transport
cli.Operations = operations.New(transport, formats)
return cli
}
// DefaultTransportConfig creates a TransportConfig with the
// default settings taken from the meta section of the spec file.
func DefaultTransportConfig() *TransportConfig {
return &TransportConfig{
Host: DefaultHost,
BasePath: DefaultBasePath,
Schemes: DefaultSchemes,
}
}
// TransportConfig contains the transport related info,
// found in the meta section of the spec file.
type TransportConfig struct {
Host string
BasePath string
Schemes []string
}
// WithHost overrides the default host,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithHost(host string) *TransportConfig {
cfg.Host = host
return cfg
}
// WithBasePath overrides the default basePath,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig {
cfg.BasePath = basePath
return cfg
}
// WithSchemes overrides the default schemes,
// provided by the meta section of the spec file.
func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig {
cfg.Schemes = schemes
return cfg
}
// Goldpinger is a client for goldpinger
type Goldpinger struct {
Operations *operations.Client
Transport runtime.ClientTransport
}
// SetTransport changes the transport on the client and all its subresources
func (c *Goldpinger) SetTransport(transport runtime.ClientTransport) {
c.Transport = transport
c.Operations.SetTransport(transport)
}

View File

@@ -0,0 +1,128 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"time"
"golang.org/x/net/context"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
cr "github.com/go-openapi/runtime/client"
strfmt "github.com/go-openapi/strfmt"
)
// NewCheckAllPodsParams creates a new CheckAllPodsParams object
// with the default values initialized.
func NewCheckAllPodsParams() *CheckAllPodsParams {
return &CheckAllPodsParams{
timeout: cr.DefaultTimeout,
}
}
// NewCheckAllPodsParamsWithTimeout creates a new CheckAllPodsParams object
// with the default values initialized, and the ability to set a timeout on a request
func NewCheckAllPodsParamsWithTimeout(timeout time.Duration) *CheckAllPodsParams {
return &CheckAllPodsParams{
timeout: timeout,
}
}
// NewCheckAllPodsParamsWithContext creates a new CheckAllPodsParams object
// with the default values initialized, and the ability to set a context for a request
func NewCheckAllPodsParamsWithContext(ctx context.Context) *CheckAllPodsParams {
return &CheckAllPodsParams{
Context: ctx,
}
}
// NewCheckAllPodsParamsWithHTTPClient creates a new CheckAllPodsParams object
// with the default values initialized, and the ability to set a custom HTTPClient for a request
func NewCheckAllPodsParamsWithHTTPClient(client *http.Client) *CheckAllPodsParams {
return &CheckAllPodsParams{
HTTPClient: client,
}
}
/*CheckAllPodsParams contains all the parameters to send to the API endpoint
for the check all pods operation typically these are written to a http.Request
*/
type CheckAllPodsParams struct {
timeout time.Duration
Context context.Context
HTTPClient *http.Client
}
// WithTimeout adds the timeout to the check all pods params
func (o *CheckAllPodsParams) WithTimeout(timeout time.Duration) *CheckAllPodsParams {
o.SetTimeout(timeout)
return o
}
// SetTimeout adds the timeout to the check all pods params
func (o *CheckAllPodsParams) SetTimeout(timeout time.Duration) {
o.timeout = timeout
}
// WithContext adds the context to the check all pods params
func (o *CheckAllPodsParams) WithContext(ctx context.Context) *CheckAllPodsParams {
o.SetContext(ctx)
return o
}
// SetContext adds the context to the check all pods params
func (o *CheckAllPodsParams) SetContext(ctx context.Context) {
o.Context = ctx
}
// WithHTTPClient adds the HTTPClient to the check all pods params
func (o *CheckAllPodsParams) WithHTTPClient(client *http.Client) *CheckAllPodsParams {
o.SetHTTPClient(client)
return o
}
// SetHTTPClient adds the HTTPClient to the check all pods params
func (o *CheckAllPodsParams) SetHTTPClient(client *http.Client) {
o.HTTPClient = client
}
// WriteToRequest writes these params to a swagger request
func (o *CheckAllPodsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
if err := r.SetTimeout(o.timeout); err != nil {
return err
}
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,81 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"github.com/go-openapi/runtime"
strfmt "github.com/go-openapi/strfmt"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// CheckAllPodsReader is a Reader for the CheckAllPods structure.
type CheckAllPodsReader struct {
formats strfmt.Registry
}
// ReadResponse reads a server response into the received o.
func (o *CheckAllPodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
switch response.Code() {
case 200:
result := NewCheckAllPodsOK()
if err := result.readResponse(response, consumer, o.formats); err != nil {
return nil, err
}
return result, nil
default:
return nil, runtime.NewAPIError("unknown error", response, response.Code())
}
}
// NewCheckAllPodsOK creates a CheckAllPodsOK with default headers values
func NewCheckAllPodsOK() *CheckAllPodsOK {
return &CheckAllPodsOK{}
}
/*CheckAllPodsOK handles this case with default header values.
Success, return response
*/
type CheckAllPodsOK struct {
Payload *models.CheckAllResults
}
func (o *CheckAllPodsOK) Error() string {
return fmt.Sprintf("[GET /check_all][%d] checkAllPodsOK %+v", 200, o.Payload)
}
func (o *CheckAllPodsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
o.Payload = new(models.CheckAllResults)
// response payload
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
return err
}
return nil
}

View File

@@ -0,0 +1,128 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"time"
"golang.org/x/net/context"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
cr "github.com/go-openapi/runtime/client"
strfmt "github.com/go-openapi/strfmt"
)
// NewCheckServicePodsParams creates a new CheckServicePodsParams object
// with the default values initialized.
func NewCheckServicePodsParams() *CheckServicePodsParams {
return &CheckServicePodsParams{
timeout: cr.DefaultTimeout,
}
}
// NewCheckServicePodsParamsWithTimeout creates a new CheckServicePodsParams object
// with the default values initialized, and the ability to set a timeout on a request
func NewCheckServicePodsParamsWithTimeout(timeout time.Duration) *CheckServicePodsParams {
return &CheckServicePodsParams{
timeout: timeout,
}
}
// NewCheckServicePodsParamsWithContext creates a new CheckServicePodsParams object
// with the default values initialized, and the ability to set a context for a request
func NewCheckServicePodsParamsWithContext(ctx context.Context) *CheckServicePodsParams {
return &CheckServicePodsParams{
Context: ctx,
}
}
// NewCheckServicePodsParamsWithHTTPClient creates a new CheckServicePodsParams object
// with the default values initialized, and the ability to set a custom HTTPClient for a request
func NewCheckServicePodsParamsWithHTTPClient(client *http.Client) *CheckServicePodsParams {
return &CheckServicePodsParams{
HTTPClient: client,
}
}
/*CheckServicePodsParams contains all the parameters to send to the API endpoint
for the check service pods operation typically these are written to a http.Request
*/
type CheckServicePodsParams struct {
timeout time.Duration
Context context.Context
HTTPClient *http.Client
}
// WithTimeout adds the timeout to the check service pods params
func (o *CheckServicePodsParams) WithTimeout(timeout time.Duration) *CheckServicePodsParams {
o.SetTimeout(timeout)
return o
}
// SetTimeout adds the timeout to the check service pods params
func (o *CheckServicePodsParams) SetTimeout(timeout time.Duration) {
o.timeout = timeout
}
// WithContext adds the context to the check service pods params
func (o *CheckServicePodsParams) WithContext(ctx context.Context) *CheckServicePodsParams {
o.SetContext(ctx)
return o
}
// SetContext adds the context to the check service pods params
func (o *CheckServicePodsParams) SetContext(ctx context.Context) {
o.Context = ctx
}
// WithHTTPClient adds the HTTPClient to the check service pods params
func (o *CheckServicePodsParams) WithHTTPClient(client *http.Client) *CheckServicePodsParams {
o.SetHTTPClient(client)
return o
}
// SetHTTPClient adds the HTTPClient to the check service pods params
func (o *CheckServicePodsParams) SetHTTPClient(client *http.Client) {
o.HTTPClient = client
}
// WriteToRequest writes these params to a swagger request
func (o *CheckServicePodsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
if err := r.SetTimeout(o.timeout); err != nil {
return err
}
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,79 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"github.com/go-openapi/runtime"
strfmt "github.com/go-openapi/strfmt"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// CheckServicePodsReader is a Reader for the CheckServicePods structure.
type CheckServicePodsReader struct {
formats strfmt.Registry
}
// ReadResponse reads a server response into the received o.
func (o *CheckServicePodsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
switch response.Code() {
case 200:
result := NewCheckServicePodsOK()
if err := result.readResponse(response, consumer, o.formats); err != nil {
return nil, err
}
return result, nil
default:
return nil, runtime.NewAPIError("unknown error", response, response.Code())
}
}
// NewCheckServicePodsOK creates a CheckServicePodsOK with default headers values
func NewCheckServicePodsOK() *CheckServicePodsOK {
return &CheckServicePodsOK{}
}
/*CheckServicePodsOK handles this case with default header values.
Success, return response
*/
type CheckServicePodsOK struct {
Payload models.CheckResults
}
func (o *CheckServicePodsOK) Error() string {
return fmt.Sprintf("[GET /check][%d] checkServicePodsOK %+v", 200, o.Payload)
}
func (o *CheckServicePodsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
// response payload
if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF {
return err
}
return nil
}

View File

@@ -0,0 +1,128 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"github.com/go-openapi/runtime"
strfmt "github.com/go-openapi/strfmt"
)
// New creates a new operations API client.
func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client {
return &Client{transport: transport, formats: formats}
}
/*
Client for operations API
*/
type Client struct {
transport runtime.ClientTransport
formats strfmt.Registry
}
/*
CheckAllPods Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.
*/
func (a *Client) CheckAllPods(params *CheckAllPodsParams) (*CheckAllPodsOK, error) {
// TODO: Validate the params before sending
if params == nil {
params = NewCheckAllPodsParams()
}
result, err := a.transport.Submit(&runtime.ClientOperation{
ID: "checkAllPods",
Method: "GET",
PathPattern: "/check_all",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{""},
Schemes: []string{"http"},
Params: params,
Reader: &CheckAllPodsReader{formats: a.formats},
Context: params.Context,
Client: params.HTTPClient,
})
if err != nil {
return nil, err
}
return result.(*CheckAllPodsOK), nil
}
/*
CheckServicePods Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint
*/
func (a *Client) CheckServicePods(params *CheckServicePodsParams) (*CheckServicePodsOK, error) {
// TODO: Validate the params before sending
if params == nil {
params = NewCheckServicePodsParams()
}
result, err := a.transport.Submit(&runtime.ClientOperation{
ID: "checkServicePods",
Method: "GET",
PathPattern: "/check",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{""},
Schemes: []string{"http"},
Params: params,
Reader: &CheckServicePodsReader{formats: a.formats},
Context: params.Context,
Client: params.HTTPClient,
})
if err != nil {
return nil, err
}
return result.(*CheckServicePodsOK), nil
}
/*
Ping return query stats
*/
func (a *Client) Ping(params *PingParams) (*PingOK, error) {
// TODO: Validate the params before sending
if params == nil {
params = NewPingParams()
}
result, err := a.transport.Submit(&runtime.ClientOperation{
ID: "ping",
Method: "GET",
PathPattern: "/ping",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{""},
Schemes: []string{"http"},
Params: params,
Reader: &PingReader{formats: a.formats},
Context: params.Context,
Client: params.HTTPClient,
})
if err != nil {
return nil, err
}
return result.(*PingOK), nil
}
// SetTransport changes the transport on the client
func (a *Client) SetTransport(transport runtime.ClientTransport) {
a.transport = transport
}

View File

@@ -0,0 +1,128 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"time"
"golang.org/x/net/context"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
cr "github.com/go-openapi/runtime/client"
strfmt "github.com/go-openapi/strfmt"
)
// NewPingParams creates a new PingParams object
// with the default values initialized.
func NewPingParams() *PingParams {
return &PingParams{
timeout: cr.DefaultTimeout,
}
}
// NewPingParamsWithTimeout creates a new PingParams object
// with the default values initialized, and the ability to set a timeout on a request
func NewPingParamsWithTimeout(timeout time.Duration) *PingParams {
return &PingParams{
timeout: timeout,
}
}
// NewPingParamsWithContext creates a new PingParams object
// with the default values initialized, and the ability to set a context for a request
func NewPingParamsWithContext(ctx context.Context) *PingParams {
return &PingParams{
Context: ctx,
}
}
// NewPingParamsWithHTTPClient creates a new PingParams object
// with the default values initialized, and the ability to set a custom HTTPClient for a request
func NewPingParamsWithHTTPClient(client *http.Client) *PingParams {
return &PingParams{
HTTPClient: client,
}
}
/*PingParams contains all the parameters to send to the API endpoint
for the ping operation typically these are written to a http.Request
*/
type PingParams struct {
timeout time.Duration
Context context.Context
HTTPClient *http.Client
}
// WithTimeout adds the timeout to the ping params
func (o *PingParams) WithTimeout(timeout time.Duration) *PingParams {
o.SetTimeout(timeout)
return o
}
// SetTimeout adds the timeout to the ping params
func (o *PingParams) SetTimeout(timeout time.Duration) {
o.timeout = timeout
}
// WithContext adds the context to the ping params
func (o *PingParams) WithContext(ctx context.Context) *PingParams {
o.SetContext(ctx)
return o
}
// SetContext adds the context to the ping params
func (o *PingParams) SetContext(ctx context.Context) {
o.Context = ctx
}
// WithHTTPClient adds the HTTPClient to the ping params
func (o *PingParams) WithHTTPClient(client *http.Client) *PingParams {
o.SetHTTPClient(client)
return o
}
// SetHTTPClient adds the HTTPClient to the ping params
func (o *PingParams) SetHTTPClient(client *http.Client) {
o.HTTPClient = client
}
// WriteToRequest writes these params to a swagger request
func (o *PingParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
if err := r.SetTimeout(o.timeout); err != nil {
return err
}
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,81 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"github.com/go-openapi/runtime"
strfmt "github.com/go-openapi/strfmt"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// PingReader is a Reader for the Ping structure.
type PingReader struct {
formats strfmt.Registry
}
// ReadResponse reads a server response into the received o.
func (o *PingReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
switch response.Code() {
case 200:
result := NewPingOK()
if err := result.readResponse(response, consumer, o.formats); err != nil {
return nil, err
}
return result, nil
default:
return nil, runtime.NewAPIError("unknown error", response, response.Code())
}
}
// NewPingOK creates a PingOK with default headers values
func NewPingOK() *PingOK {
return &PingOK{}
}
/*PingOK handles this case with default header values.
return success
*/
type PingOK struct {
Payload *models.PingResults
}
func (o *PingOK) Error() string {
return fmt.Sprintf("[GET /ping][%d] pingOK %+v", 200, o.Payload)
}
func (o *PingOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
o.Payload = new(models.PingResults)
// response payload
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
return err
}
return nil
}

172
pkg/goldpinger/client.go Normal file
View File

@@ -0,0 +1,172 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goldpinger
import (
"fmt"
"sync"
apiclient "github.com/bloomberg/goldpinger/pkg/client"
"github.com/bloomberg/goldpinger/pkg/models"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
)
// CheckNeighbours queries the kubernetes API server for all other goldpinger pods
// then calls Ping() on each one
func CheckNeighbours() models.CheckResults {
return PingAllPods(GetAllPods())
}
// CheckNeighboursNeighbours queries the kubernetes API server for all other goldpinger
// pods then calls Check() on each one
func CheckNeighboursNeighbours() *models.CheckAllResults {
return CheckAllPods(GetAllPods())
}
type PingAllPodsResult struct {
podResult models.PodResult
hostIPv4 strfmt.IPv4
podIP string
}
func pickPodHostIP(podIP, hostIP string) string {
if GoldpingerConfig.UseHostIP {
return hostIP
}
return podIP
}
func PingAllPods(pods map[string]string) models.CheckResults {
result := models.CheckResults{}
ch := make(chan PingAllPodsResult, len(pods))
wg := sync.WaitGroup{}
wg.Add(len(pods))
for podIP, hostIP := range pods {
go func(podIP string, hostIP string) {
var channelResult PingAllPodsResult
CountCall("made", "ping")
timer := GetLabeledPeersCallsTimer("ping", hostIP, podIP)
resp, err := getClient(pickPodHostIP(podIP, hostIP)).Operations.Ping(nil)
channelResult.hostIPv4.UnmarshalText([]byte(hostIP))
var OK = (err == nil)
if OK {
channelResult.podResult = models.PodResult{HostIP: channelResult.hostIPv4, OK: &OK, Response: resp.Payload, StatusCode: 200}
timer.ObserveDuration()
} else {
channelResult.podResult = models.PodResult{HostIP: channelResult.hostIPv4, OK: &OK, Error: err.Error(), StatusCode: 500}
CountError("ping")
}
channelResult.podIP = podIP
ch <- channelResult
wg.Done()
}(podIP, hostIP)
}
wg.Wait()
close(ch)
counterHealthy, counterUnhealthy := 0.0, 0.0
for response := range ch {
var podIPv4 strfmt.IPv4
podIPv4.UnmarshalText([]byte(response.podIP))
if *response.podResult.OK {
counterHealthy++
} else {
counterUnhealthy++
}
result[response.podIP] = response.podResult
}
CountHealthyUnhealthyNodes(counterHealthy, counterUnhealthy)
return result
}
type CheckServicePodsResult struct {
checkAllPodResult models.CheckAllPodResult
hostIPv4 strfmt.IPv4
podIP string
}
func CheckAllPods(pods map[string]string) *models.CheckAllResults {
result := models.CheckAllResults{Responses: make(map[string]models.CheckAllPodResult)}
ch := make(chan CheckServicePodsResult, len(pods))
wg := sync.WaitGroup{}
wg.Add(len(pods))
for podIP, hostIP := range pods {
go func(podIP string, hostIP string) {
var channelResult CheckServicePodsResult
CountCall("made", "check")
timer := GetLabeledPeersCallsTimer("check", hostIP, podIP)
resp, err := getClient(pickPodHostIP(podIP, hostIP)).Operations.CheckServicePods(nil)
channelResult.hostIPv4.UnmarshalText([]byte(hostIP))
var OK = (err == nil)
if OK {
channelResult.checkAllPodResult = models.CheckAllPodResult{
OK: &OK,
HostIP: channelResult.hostIPv4,
Response: resp.Payload,
}
timer.ObserveDuration()
} else {
channelResult.checkAllPodResult = models.CheckAllPodResult{
OK: &OK,
HostIP: channelResult.hostIPv4,
Error: err.Error(),
}
CountError("checkAll")
}
channelResult.podIP = podIP
ch <- channelResult
wg.Done()
}(podIP, hostIP)
}
wg.Wait()
close(ch)
for response := range ch {
var podIPv4 strfmt.IPv4
podIPv4.UnmarshalText([]byte(response.podIP))
result.Responses[response.podIP] = response.checkAllPodResult
result.Hosts = append(result.Hosts, &models.CheckAllResultsHostsItems0{
HostIP: response.hostIPv4,
PodIP: podIPv4,
})
}
return &result
}
func getClient(hostIP string) *apiclient.Goldpinger {
host := fmt.Sprintf("%s:%d", hostIP, GoldpingerConfig.Port)
transport := httptransport.New(host, "", nil)
client := apiclient.New(transport, strfmt.Default)
apiclient.Default.SetTransport(transport)
return client
}

29
pkg/goldpinger/config.go Normal file
View File

@@ -0,0 +1,29 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goldpinger
import (
"k8s.io/client-go/kubernetes"
)
var GoldpingerConfig = struct {
StaticFilePath string `long:"static-file-path" description:"Folder for serving static files" env:"STATIC_FILE_PATH"`
KubeConfigPath string `long:"kubeconfig" description:"Path to kubeconfig file" env:"KUBECONFIG"`
RefreshInterval int `long:"refresh-interval" description:"If > 0, will create a thread and collect stats every n seconds" env:"REFRESH_INTERVAL"`
Hostname string `long:"hostname" description:"Hostname to use" env:"HOSTNAME"`
Port int `long:"client-port-override" description:"(for testing) use this port when calling other instances" env:"CLIENT_PORT_OVERRIDE"`
UseHostIP bool `long:"use-host-ip" description:"When making the calls, use host ip (defaults to pod ip)" env:"USE_HOST_IP"`
KubernetesClient *kubernetes.Clientset
}{}

39
pkg/goldpinger/k8s.go Normal file
View File

@@ -0,0 +1,39 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goldpinger
import (
"log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func GetAllPods() map[string]string {
timer := GetLabeledKubernetesCallsTimer()
pods, err := GoldpingerConfig.KubernetesClient.CoreV1().Pods("").List(metav1.ListOptions{LabelSelector: "app=goldpinger"})
if err != nil {
log.Println("Error getting pods for selector: ", err.Error())
CountError("kubernetes_api")
} else {
timer.ObserveDuration()
}
var podsreturn = make(map[string]string)
for _, pod := range pods.Items {
podsreturn[pod.Status.PodIP] = pod.Status.HostIP
}
return podsreturn
}

173
pkg/goldpinger/stats.go Normal file
View File

@@ -0,0 +1,173 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goldpinger
import (
"log"
"time"
"github.com/bloomberg/goldpinger/pkg/models"
"github.com/go-openapi/strfmt"
"github.com/prometheus/client_golang/prometheus"
)
var (
goldpingerStatsCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "goldpinger_stats_total",
Help: "Statistics of calls made in goldpinger instances",
},
[]string{
"goldpinger_instance",
"group",
"action",
},
)
goldpingerNodesHealthGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "goldpinger_nodes_health_total",
Help: "Number of nodes seen as healthy/unhealthy from this instance's POV",
},
[]string{
"goldpinger_instance",
"status",
},
)
goldpingerResponseTimePeersHistogram = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "goldpinger_peers_response_time_s",
Help: "Histogram of response times from other hosts, when making peer calls",
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 30},
},
[]string{
"goldpinger_instance",
"call_type",
"host_ip",
"pod_ip",
},
)
goldpingerResponseTimeKubernetesHistogram = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "goldpinger_kube_master_response_time_s",
Help: "Histogram of response times from kubernetes API server, when listing other instances",
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 30},
},
[]string{
"goldpinger_instance",
},
)
goldpingerErrorsCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "goldpinger_errors_total",
Help: "Statistics of errors per instance",
},
[]string{
"goldpinger_instance",
"type",
},
)
groups = map[string]map[string]int64{
"received": map[string]int64{
"ping": 0,
"check": 0,
"check_all": 0,
},
"made": map[string]int64{
"ping": 0,
"check": 0,
"check_all": 0,
},
}
bootTime = time.Now()
)
func init() {
prometheus.MustRegister(goldpingerStatsCounter)
prometheus.MustRegister(goldpingerNodesHealthGauge)
prometheus.MustRegister(goldpingerResponseTimePeersHistogram)
prometheus.MustRegister(goldpingerResponseTimeKubernetesHistogram)
prometheus.MustRegister(goldpingerErrorsCounter)
log.Println("Metrics setup - see /metrics")
}
func GetStats() *models.PingResults {
var result models.PingResults
var calls models.CallStats
calls.Check = groups["received"]["check"]
calls.CheckAll = groups["received"]["check_all"]
calls.Ping = groups["received"]["ping"]
result.BootTime = strfmt.DateTime(bootTime)
result.Received = &calls
return &result
}
// counts various calls received and made
func CountCall(group string, call string) {
groups[group][call]++
goldpingerStatsCounter.WithLabelValues(
GoldpingerConfig.Hostname,
group,
call,
).Inc()
}
// counts healthy and unhealthy nodes
func CountHealthyUnhealthyNodes(healthy, unhealthy float64) {
goldpingerNodesHealthGauge.WithLabelValues(
GoldpingerConfig.Hostname,
"healthy",
).Set(healthy)
goldpingerNodesHealthGauge.WithLabelValues(
GoldpingerConfig.Hostname,
"unhealthy",
).Set(unhealthy)
}
// counts instances of various errors
func CountError(errorType string) {
goldpingerErrorsCounter.WithLabelValues(
GoldpingerConfig.Hostname,
errorType,
).Inc()
}
// returns a timer for easy observing of the durations of calls to kubernetes API
func GetLabeledKubernetesCallsTimer() *prometheus.Timer {
return prometheus.NewTimer(
goldpingerResponseTimeKubernetesHistogram.WithLabelValues(
GoldpingerConfig.Hostname,
),
)
}
// returns a timer for easy observing of the duration of calls to peers
func GetLabeledPeersCallsTimer(callType, hostIP, podIP string) *prometheus.Timer {
return prometheus.NewTimer(
goldpingerResponseTimePeersHistogram.WithLabelValues(
GoldpingerConfig.Hostname,
callType,
hostIP,
podIP,
),
)
}

45
pkg/goldpinger/updater.go Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goldpinger
import (
"fmt"
"log"
"time"
)
func StartUpdater() {
if GoldpingerConfig.RefreshInterval <= 0 {
log.Println("Not creating updater, period is 0")
return
}
// start the updater
go func() {
for {
results := PingAllPods(GetAllPods())
var troublemakers []string
for podIP, value := range results {
if *value.OK != true {
troublemakers = append(troublemakers, fmt.Sprintf("%s (%s)", podIP, value.HostIP.String()))
}
}
if len(troublemakers) > 0 {
log.Println("Updater ran into trouble with these peers: ", troublemakers)
}
time.Sleep(time.Duration(GoldpingerConfig.RefreshInterval) * time.Second)
}
}()
}

63
pkg/models/call_stats.go Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// CallStats call stats
// swagger:model CallStats
type CallStats struct {
// check
Check int64 `json:"check,omitempty"`
// check all
CheckAll int64 `json:"check_all,omitempty"`
// ping
Ping int64 `json:"ping,omitempty"`
}
// Validate validates this call stats
func (m *CallStats) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *CallStats) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CallStats) UnmarshalBinary(b []byte) error {
var res CallStats
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,114 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// CheckAllPodResult check all pod result
// swagger:model CheckAllPodResult
type CheckAllPodResult struct {
// host IP
// Format: ipv4
HostIP strfmt.IPv4 `json:"HostIP,omitempty"`
// o k
OK *bool `json:"OK,omitempty"`
// error
Error string `json:"error,omitempty"`
// response
Response CheckResults `json:"response,omitempty"`
// status code
StatusCode int32 `json:"status-code,omitempty"`
}
// Validate validates this check all pod result
func (m *CheckAllPodResult) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHostIP(formats); err != nil {
res = append(res, err)
}
if err := m.validateResponse(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CheckAllPodResult) validateHostIP(formats strfmt.Registry) error {
if swag.IsZero(m.HostIP) { // not required
return nil
}
if err := validate.FormatOf("HostIP", "body", "ipv4", m.HostIP.String(), formats); err != nil {
return err
}
return nil
}
func (m *CheckAllPodResult) validateResponse(formats strfmt.Registry) error {
if swag.IsZero(m.Response) { // not required
return nil
}
if err := m.Response.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("response")
}
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *CheckAllPodResult) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CheckAllPodResult) UnmarshalBinary(b []byte) error {
var res CheckAllPodResult
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,208 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"strconv"
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// CheckAllResults check all results
// swagger:model CheckAllResults
type CheckAllResults struct {
// o k
OK *bool `json:"OK,omitempty"`
// hosts
Hosts []*CheckAllResultsHostsItems0 `json:"hosts"`
// hosts healthy
HostsHealthy int32 `json:"hosts-healthy,omitempty"`
// hosts number
HostsNumber int32 `json:"hosts-number,omitempty"`
// responses
Responses map[string]CheckAllPodResult `json:"responses,omitempty"`
}
// Validate validates this check all results
func (m *CheckAllResults) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHosts(formats); err != nil {
res = append(res, err)
}
if err := m.validateResponses(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CheckAllResults) validateHosts(formats strfmt.Registry) error {
if swag.IsZero(m.Hosts) { // not required
return nil
}
for i := 0; i < len(m.Hosts); i++ {
if swag.IsZero(m.Hosts[i]) { // not required
continue
}
if m.Hosts[i] != nil {
if err := m.Hosts[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("hosts" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *CheckAllResults) validateResponses(formats strfmt.Registry) error {
if swag.IsZero(m.Responses) { // not required
return nil
}
for k := range m.Responses {
if err := validate.Required("responses"+"."+k, "body", m.Responses[k]); err != nil {
return err
}
if val, ok := m.Responses[k]; ok {
if err := val.Validate(formats); err != nil {
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *CheckAllResults) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CheckAllResults) UnmarshalBinary(b []byte) error {
var res CheckAllResults
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// CheckAllResultsHostsItems0 check all results hosts items0
// swagger:model CheckAllResultsHostsItems0
type CheckAllResultsHostsItems0 struct {
// host IP
// Format: ipv4
HostIP strfmt.IPv4 `json:"hostIP,omitempty"`
// pod IP
// Format: ipv4
PodIP strfmt.IPv4 `json:"podIP,omitempty"`
}
// Validate validates this check all results hosts items0
func (m *CheckAllResultsHostsItems0) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHostIP(formats); err != nil {
res = append(res, err)
}
if err := m.validatePodIP(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CheckAllResultsHostsItems0) validateHostIP(formats strfmt.Registry) error {
if swag.IsZero(m.HostIP) { // not required
return nil
}
if err := validate.FormatOf("hostIP", "body", "ipv4", m.HostIP.String(), formats); err != nil {
return err
}
return nil
}
func (m *CheckAllResultsHostsItems0) validatePodIP(formats strfmt.Registry) error {
if swag.IsZero(m.PodIP) { // not required
return nil
}
if err := validate.FormatOf("podIP", "body", "ipv4", m.PodIP.String(), formats); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *CheckAllResultsHostsItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CheckAllResultsHostsItems0) UnmarshalBinary(b []byte) error {
var res CheckAllResultsHostsItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,54 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/validate"
)
// CheckResults check results
// swagger:model CheckResults
type CheckResults map[string]PodResult
// Validate validates this check results
func (m CheckResults) Validate(formats strfmt.Registry) error {
var res []error
for k := range m {
if err := validate.Required(k, "body", m[k]); err != nil {
return err
}
if val, ok := m[k]; ok {
if err := val.Validate(formats); err != nil {
return err
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

107
pkg/models/ping_results.go Normal file
View File

@@ -0,0 +1,107 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// PingResults ping results
// swagger:model PingResults
type PingResults struct {
// boot time
// Format: date-time
BootTime strfmt.DateTime `json:"boot_time,omitempty"`
// received
Received *CallStats `json:"received,omitempty"`
}
// Validate validates this ping results
func (m *PingResults) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateBootTime(formats); err != nil {
res = append(res, err)
}
if err := m.validateReceived(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PingResults) validateBootTime(formats strfmt.Registry) error {
if swag.IsZero(m.BootTime) { // not required
return nil
}
if err := validate.FormatOf("boot_time", "body", "date-time", m.BootTime.String(), formats); err != nil {
return err
}
return nil
}
func (m *PingResults) validateReceived(formats strfmt.Registry) error {
if swag.IsZero(m.Received) { // not required
return nil
}
if m.Received != nil {
if err := m.Received.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("received")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *PingResults) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PingResults) UnmarshalBinary(b []byte) error {
var res PingResults
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

116
pkg/models/pod_result.go Normal file
View File

@@ -0,0 +1,116 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// PodResult pod result
// swagger:model PodResult
type PodResult struct {
// host IP
// Format: ipv4
HostIP strfmt.IPv4 `json:"HostIP,omitempty"`
// o k
OK *bool `json:"OK,omitempty"`
// error
Error string `json:"error,omitempty"`
// response
Response *PingResults `json:"response,omitempty"`
// status code
StatusCode int32 `json:"status-code,omitempty"`
}
// Validate validates this pod result
func (m *PodResult) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHostIP(formats); err != nil {
res = append(res, err)
}
if err := m.validateResponse(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PodResult) validateHostIP(formats strfmt.Registry) error {
if swag.IsZero(m.HostIP) { // not required
return nil
}
if err := validate.FormatOf("HostIP", "body", "ipv4", m.HostIP.String(), formats); err != nil {
return err
}
return nil
}
func (m *PodResult) validateResponse(formats strfmt.Registry) error {
if swag.IsZero(m.Response) { // not required
return nil
}
if m.Response != nil {
if err := m.Response.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("response")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *PodResult) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PodResult) UnmarshalBinary(b []byte) error {
var res PodResult
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,126 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file is safe to edit. Once it exists it will not be overwritten
package restapi
import (
"crypto/tls"
"log"
"net/http"
"strings"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/bloomberg/goldpinger/pkg/goldpinger"
"github.com/bloomberg/goldpinger/pkg/restapi/operations"
"github.com/go-openapi/swag"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
//go:generate swagger generate server --target ../goldpinger --name Goldpinger --spec ../swagger.yml --exclude-main
func configureFlags(api *operations.GoldpingerAPI) {
api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
swag.CommandLineOptionsGroup{
ShortDescription: "Goldpinger Config",
LongDescription: "",
Options: &goldpinger.GoldpingerConfig,
},
}
}
func configureAPI(api *operations.GoldpingerAPI) http.Handler {
// configure the api here
api.ServeError = errors.ServeError
api.JSONConsumer = runtime.JSONConsumer()
api.JSONProducer = runtime.JSONProducer()
api.PingHandler = operations.PingHandlerFunc(
func(params operations.PingParams) middleware.Responder {
goldpinger.CountCall("received", "ping")
return operations.NewPingOK().WithPayload(goldpinger.GetStats())
})
api.CheckServicePodsHandler = operations.CheckServicePodsHandlerFunc(
func(params operations.CheckServicePodsParams) middleware.Responder {
goldpinger.CountCall("received", "check")
return operations.NewCheckServicePodsOK().WithPayload(goldpinger.CheckNeighbours())
})
api.CheckAllPodsHandler = operations.CheckAllPodsHandlerFunc(
func(params operations.CheckAllPodsParams) middleware.Responder {
goldpinger.CountCall("received", "check_all")
return operations.NewCheckAllPodsOK().WithPayload(goldpinger.CheckNeighboursNeighbours())
})
api.ServerShutdown = func() {}
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}
// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
// Make all necessary changes to the TLS configuration here.
}
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix"
func configureServer(s *http.Server, scheme, addr string) {
}
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares(handler http.Handler) http.Handler {
return handler
}
func fileServerMiddleware(next http.Handler) http.Handler {
log.Println("Added the static middleware")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fileServer := http.FileServer(http.Dir(goldpinger.GoldpingerConfig.StaticFilePath))
if r.URL.Path == "/" {
http.StripPrefix("/", fileServer).ServeHTTP(w, r)
} else if strings.HasPrefix(r.URL.Path, "/static/") {
http.StripPrefix("/static/", fileServer).ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
func prometheusMetricsMiddleware(next http.Handler) http.Handler {
log.Println("Added the prometheus middleware")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/metrics" {
http.StripPrefix("/metrics", promhttp.Handler()).ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return prometheusMetricsMiddleware(fileServerMiddleware(handler))
}

34
pkg/restapi/doc.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
/*
Package restapi Goldpinger
Schemes:
http
Host: localhost
BasePath: /
Version: 1.0.0
Consumes:
- application/json
Produces:
- application/json
swagger:meta
*/
package restapi

View File

@@ -0,0 +1,390 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package restapi
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"encoding/json"
)
var (
// SwaggerJSON embedded version of the swagger document used at generation time
SwaggerJSON json.RawMessage
// FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
FlatSwaggerJSON json.RawMessage
)
func init() {
SwaggerJSON = json.RawMessage([]byte(`{
"swagger": "2.0",
"info": {
"title": "Goldpinger",
"version": "1.0.0"
},
"paths": {
"/check": {
"get": {
"description": "Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint",
"produces": [
"application/json"
],
"operationId": "checkServicePods",
"responses": {
"200": {
"description": "Success, return response",
"schema": {
"$ref": "#/definitions/CheckResults"
}
}
}
}
},
"/check_all": {
"get": {
"description": "Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.",
"produces": [
"application/json"
],
"operationId": "checkAllPods",
"responses": {
"200": {
"description": "Success, return response",
"schema": {
"$ref": "#/definitions/CheckAllResults"
}
}
}
}
},
"/ping": {
"get": {
"description": "return query stats",
"produces": [
"application/json"
],
"operationId": "ping",
"responses": {
"200": {
"description": "return success",
"schema": {
"$ref": "#/definitions/PingResults"
}
}
}
}
}
},
"definitions": {
"CallStats": {
"properties": {
"check": {
"type": "integer"
},
"check_all": {
"type": "integer"
},
"ping": {
"type": "integer"
}
}
},
"CheckAllPodResult": {
"type": "object",
"properties": {
"HostIP": {
"type": "string",
"format": "ipv4"
},
"OK": {
"type": "boolean",
"default": false
},
"error": {
"type": "string"
},
"response": {
"$ref": "#/definitions/CheckResults"
},
"status-code": {
"type": "integer",
"format": "int32"
}
}
},
"CheckAllResults": {
"type": "object",
"properties": {
"OK": {
"type": "boolean",
"default": false
},
"hosts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"hostIP": {
"type": "string",
"format": "ipv4"
},
"podIP": {
"type": "string",
"format": "ipv4"
}
}
}
},
"hosts-healthy": {
"type": "integer",
"format": "int32"
},
"hosts-number": {
"type": "integer",
"format": "int32"
},
"responses": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/CheckAllPodResult"
}
}
}
},
"CheckResults": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/PodResult"
}
},
"PingResults": {
"type": "object",
"properties": {
"boot_time": {
"type": "string",
"format": "date-time"
},
"received": {
"$ref": "#/definitions/CallStats"
}
}
},
"PodResult": {
"type": "object",
"properties": {
"HostIP": {
"type": "string",
"format": "ipv4"
},
"OK": {
"type": "boolean",
"default": false
},
"error": {
"type": "string"
},
"response": {
"$ref": "#/definitions/PingResults"
},
"status-code": {
"type": "integer",
"format": "int32"
}
}
}
}
}`))
FlatSwaggerJSON = json.RawMessage([]byte(`{
"swagger": "2.0",
"info": {
"title": "Goldpinger",
"version": "1.0.0"
},
"paths": {
"/check": {
"get": {
"description": "Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint",
"produces": [
"application/json"
],
"operationId": "checkServicePods",
"responses": {
"200": {
"description": "Success, return response",
"schema": {
"$ref": "#/definitions/CheckResults"
}
}
}
}
},
"/check_all": {
"get": {
"description": "Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.",
"produces": [
"application/json"
],
"operationId": "checkAllPods",
"responses": {
"200": {
"description": "Success, return response",
"schema": {
"$ref": "#/definitions/CheckAllResults"
}
}
}
}
},
"/ping": {
"get": {
"description": "return query stats",
"produces": [
"application/json"
],
"operationId": "ping",
"responses": {
"200": {
"description": "return success",
"schema": {
"$ref": "#/definitions/PingResults"
}
}
}
}
}
},
"definitions": {
"CallStats": {
"properties": {
"check": {
"type": "integer"
},
"check_all": {
"type": "integer"
},
"ping": {
"type": "integer"
}
}
},
"CheckAllPodResult": {
"type": "object",
"properties": {
"HostIP": {
"type": "string",
"format": "ipv4"
},
"OK": {
"type": "boolean",
"default": false
},
"error": {
"type": "string"
},
"response": {
"$ref": "#/definitions/CheckResults"
},
"status-code": {
"type": "integer",
"format": "int32"
}
}
},
"CheckAllResults": {
"type": "object",
"properties": {
"OK": {
"type": "boolean",
"default": false
},
"hosts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"hostIP": {
"type": "string",
"format": "ipv4"
},
"podIP": {
"type": "string",
"format": "ipv4"
}
}
}
},
"hosts-healthy": {
"type": "integer",
"format": "int32"
},
"hosts-number": {
"type": "integer",
"format": "int32"
},
"responses": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/CheckAllPodResult"
}
}
}
},
"CheckResults": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/PodResult"
}
},
"PingResults": {
"type": "object",
"properties": {
"boot_time": {
"type": "string",
"format": "date-time"
},
"received": {
"$ref": "#/definitions/CallStats"
}
}
},
"PodResult": {
"type": "object",
"properties": {
"HostIP": {
"type": "string",
"format": "ipv4"
},
"OK": {
"type": "boolean",
"default": false
},
"error": {
"type": "string"
},
"response": {
"$ref": "#/definitions/PingResults"
},
"status-code": {
"type": "integer",
"format": "int32"
}
}
}
}
}`))
}

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
middleware "github.com/go-openapi/runtime/middleware"
)
// CheckAllPodsHandlerFunc turns a function with the right signature into a check all pods handler
type CheckAllPodsHandlerFunc func(CheckAllPodsParams) middleware.Responder
// Handle executing the request and returning a response
func (fn CheckAllPodsHandlerFunc) Handle(params CheckAllPodsParams) middleware.Responder {
return fn(params)
}
// CheckAllPodsHandler interface for that can handle valid check all pods params
type CheckAllPodsHandler interface {
Handle(CheckAllPodsParams) middleware.Responder
}
// NewCheckAllPods creates a new http.Handler for the check all pods operation
func NewCheckAllPods(ctx *middleware.Context, handler CheckAllPodsHandler) *CheckAllPods {
return &CheckAllPods{Context: ctx, Handler: handler}
}
/*CheckAllPods swagger:route GET /check_all checkAllPods
Queries the API server for all other pods in this service, and makes all of them query all of their neighbours, using their pods IPs. Calls their /check endpoint.
*/
type CheckAllPods struct {
Context *middleware.Context
Handler CheckAllPodsHandler
}
func (o *CheckAllPods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewCheckAllPodsParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,59 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewCheckAllPodsParams creates a new CheckAllPodsParams object
// no default values defined in spec.
func NewCheckAllPodsParams() CheckAllPodsParams {
return CheckAllPodsParams{}
}
// CheckAllPodsParams contains all the bound params for the check all pods operation
// typically these are obtained from a http.Request
//
// swagger:parameters checkAllPods
type CheckAllPodsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewCheckAllPodsParams() beforehand.
func (o *CheckAllPodsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// CheckAllPodsOKCode is the HTTP code returned for type CheckAllPodsOK
const CheckAllPodsOKCode int = 200
/*CheckAllPodsOK Success, return response
swagger:response checkAllPodsOK
*/
type CheckAllPodsOK struct {
/*
In: Body
*/
Payload *models.CheckAllResults `json:"body,omitempty"`
}
// NewCheckAllPodsOK creates CheckAllPodsOK with default headers values
func NewCheckAllPodsOK() *CheckAllPodsOK {
return &CheckAllPodsOK{}
}
// WithPayload adds the payload to the check all pods o k response
func (o *CheckAllPodsOK) WithPayload(payload *models.CheckAllResults) *CheckAllPodsOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the check all pods o k response
func (o *CheckAllPodsOK) SetPayload(payload *models.CheckAllResults) {
o.Payload = payload
}
// WriteResponse to the client
func (o *CheckAllPodsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,98 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// CheckAllPodsURL generates an URL for the check all pods operation
type CheckAllPodsURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CheckAllPodsURL) WithBasePath(bp string) *CheckAllPodsURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CheckAllPodsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *CheckAllPodsURL) Build() (*url.URL, error) {
var result url.URL
var _path = "/check_all"
_basePath := o._basePath
result.Path = golangswaggerpaths.Join(_basePath, _path)
return &result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *CheckAllPodsURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *CheckAllPodsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *CheckAllPodsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on CheckAllPodsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on CheckAllPodsURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *CheckAllPodsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
middleware "github.com/go-openapi/runtime/middleware"
)
// CheckServicePodsHandlerFunc turns a function with the right signature into a check service pods handler
type CheckServicePodsHandlerFunc func(CheckServicePodsParams) middleware.Responder
// Handle executing the request and returning a response
func (fn CheckServicePodsHandlerFunc) Handle(params CheckServicePodsParams) middleware.Responder {
return fn(params)
}
// CheckServicePodsHandler interface for that can handle valid check service pods params
type CheckServicePodsHandler interface {
Handle(CheckServicePodsParams) middleware.Responder
}
// NewCheckServicePods creates a new http.Handler for the check service pods operation
func NewCheckServicePods(ctx *middleware.Context, handler CheckServicePodsHandler) *CheckServicePods {
return &CheckServicePods{Context: ctx, Handler: handler}
}
/*CheckServicePods swagger:route GET /check checkServicePods
Queries the API server for all other pods in this service, and pings them via their pods IPs. Calls their /ping endpoint
*/
type CheckServicePods struct {
Context *middleware.Context
Handler CheckServicePodsHandler
}
func (o *CheckServicePods) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewCheckServicePodsParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,59 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewCheckServicePodsParams creates a new CheckServicePodsParams object
// no default values defined in spec.
func NewCheckServicePodsParams() CheckServicePodsParams {
return CheckServicePodsParams{}
}
// CheckServicePodsParams contains all the bound params for the check service pods operation
// typically these are obtained from a http.Request
//
// swagger:parameters checkServicePods
type CheckServicePodsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewCheckServicePodsParams() beforehand.
func (o *CheckServicePodsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,71 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// CheckServicePodsOKCode is the HTTP code returned for type CheckServicePodsOK
const CheckServicePodsOKCode int = 200
/*CheckServicePodsOK Success, return response
swagger:response checkServicePodsOK
*/
type CheckServicePodsOK struct {
/*
In: Body
*/
Payload models.CheckResults `json:"body,omitempty"`
}
// NewCheckServicePodsOK creates CheckServicePodsOK with default headers values
func NewCheckServicePodsOK() *CheckServicePodsOK {
return &CheckServicePodsOK{}
}
// WithPayload adds the payload to the check service pods o k response
func (o *CheckServicePodsOK) WithPayload(payload models.CheckResults) *CheckServicePodsOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the check service pods o k response
func (o *CheckServicePodsOK) SetPayload(payload models.CheckResults) {
o.Payload = payload
}
// WriteResponse to the client
func (o *CheckServicePodsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}

View File

@@ -0,0 +1,98 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// CheckServicePodsURL generates an URL for the check service pods operation
type CheckServicePodsURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CheckServicePodsURL) WithBasePath(bp string) *CheckServicePodsURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CheckServicePodsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *CheckServicePodsURL) Build() (*url.URL, error) {
var result url.URL
var _path = "/check"
_basePath := o._basePath
result.Path = golangswaggerpaths.Join(_basePath, _path)
return &result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *CheckServicePodsURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *CheckServicePodsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *CheckServicePodsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on CheckServicePodsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on CheckServicePodsURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *CheckServicePodsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,316 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"net/http"
"strings"
errors "github.com/go-openapi/errors"
loads "github.com/go-openapi/loads"
runtime "github.com/go-openapi/runtime"
middleware "github.com/go-openapi/runtime/middleware"
security "github.com/go-openapi/runtime/security"
spec "github.com/go-openapi/spec"
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// NewGoldpingerAPI creates a new Goldpinger instance
func NewGoldpingerAPI(spec *loads.Document) *GoldpingerAPI {
return &GoldpingerAPI{
handlers: make(map[string]map[string]http.Handler),
formats: strfmt.Default,
defaultConsumes: "application/json",
defaultProduces: "application/json",
customConsumers: make(map[string]runtime.Consumer),
customProducers: make(map[string]runtime.Producer),
ServerShutdown: func() {},
spec: spec,
ServeError: errors.ServeError,
BasicAuthenticator: security.BasicAuth,
APIKeyAuthenticator: security.APIKeyAuth,
BearerAuthenticator: security.BearerAuth,
JSONConsumer: runtime.JSONConsumer(),
JSONProducer: runtime.JSONProducer(),
CheckAllPodsHandler: CheckAllPodsHandlerFunc(func(params CheckAllPodsParams) middleware.Responder {
return middleware.NotImplemented("operation CheckAllPods has not yet been implemented")
}),
CheckServicePodsHandler: CheckServicePodsHandlerFunc(func(params CheckServicePodsParams) middleware.Responder {
return middleware.NotImplemented("operation CheckServicePods has not yet been implemented")
}),
PingHandler: PingHandlerFunc(func(params PingParams) middleware.Responder {
return middleware.NotImplemented("operation Ping has not yet been implemented")
}),
}
}
/*GoldpingerAPI the goldpinger API */
type GoldpingerAPI struct {
spec *loads.Document
context *middleware.Context
handlers map[string]map[string]http.Handler
formats strfmt.Registry
customConsumers map[string]runtime.Consumer
customProducers map[string]runtime.Producer
defaultConsumes string
defaultProduces string
Middleware func(middleware.Builder) http.Handler
// BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
// APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
// BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
// JSONConsumer registers a consumer for a "application/json" mime type
JSONConsumer runtime.Consumer
// JSONProducer registers a producer for a "application/json" mime type
JSONProducer runtime.Producer
// CheckAllPodsHandler sets the operation handler for the check all pods operation
CheckAllPodsHandler CheckAllPodsHandler
// CheckServicePodsHandler sets the operation handler for the check service pods operation
CheckServicePodsHandler CheckServicePodsHandler
// PingHandler sets the operation handler for the ping operation
PingHandler PingHandler
// ServeError is called when an error is received, there is a default handler
// but you can set your own with this
ServeError func(http.ResponseWriter, *http.Request, error)
// ServerShutdown is called when the HTTP(S) server is shut down and done
// handling all active connections and does not accept connections any more
ServerShutdown func()
// Custom command line argument groups with their descriptions
CommandLineOptionsGroups []swag.CommandLineOptionsGroup
// User defined logger function.
Logger func(string, ...interface{})
}
// SetDefaultProduces sets the default produces media type
func (o *GoldpingerAPI) SetDefaultProduces(mediaType string) {
o.defaultProduces = mediaType
}
// SetDefaultConsumes returns the default consumes media type
func (o *GoldpingerAPI) SetDefaultConsumes(mediaType string) {
o.defaultConsumes = mediaType
}
// SetSpec sets a spec that will be served for the clients.
func (o *GoldpingerAPI) SetSpec(spec *loads.Document) {
o.spec = spec
}
// DefaultProduces returns the default produces media type
func (o *GoldpingerAPI) DefaultProduces() string {
return o.defaultProduces
}
// DefaultConsumes returns the default consumes media type
func (o *GoldpingerAPI) DefaultConsumes() string {
return o.defaultConsumes
}
// Formats returns the registered string formats
func (o *GoldpingerAPI) Formats() strfmt.Registry {
return o.formats
}
// RegisterFormat registers a custom format validator
func (o *GoldpingerAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
o.formats.Add(name, format, validator)
}
// Validate validates the registrations in the GoldpingerAPI
func (o *GoldpingerAPI) Validate() error {
var unregistered []string
if o.JSONConsumer == nil {
unregistered = append(unregistered, "JSONConsumer")
}
if o.JSONProducer == nil {
unregistered = append(unregistered, "JSONProducer")
}
if o.CheckAllPodsHandler == nil {
unregistered = append(unregistered, "CheckAllPodsHandler")
}
if o.CheckServicePodsHandler == nil {
unregistered = append(unregistered, "CheckServicePodsHandler")
}
if o.PingHandler == nil {
unregistered = append(unregistered, "PingHandler")
}
if len(unregistered) > 0 {
return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
}
return nil
}
// ServeErrorFor gets a error handler for a given operation id
func (o *GoldpingerAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
return o.ServeError
}
// AuthenticatorsFor gets the authenticators for the specified security schemes
func (o *GoldpingerAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
return nil
}
// Authorizer returns the registered authorizer
func (o *GoldpingerAPI) Authorizer() runtime.Authorizer {
return nil
}
// ConsumersFor gets the consumers for the specified media types
func (o *GoldpingerAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
result := make(map[string]runtime.Consumer)
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONConsumer
}
if c, ok := o.customConsumers[mt]; ok {
result[mt] = c
}
}
return result
}
// ProducersFor gets the producers for the specified media types
func (o *GoldpingerAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
result := make(map[string]runtime.Producer)
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONProducer
}
if p, ok := o.customProducers[mt]; ok {
result[mt] = p
}
}
return result
}
// HandlerFor gets a http.Handler for the provided operation method and path
func (o *GoldpingerAPI) HandlerFor(method, path string) (http.Handler, bool) {
if o.handlers == nil {
return nil, false
}
um := strings.ToUpper(method)
if _, ok := o.handlers[um]; !ok {
return nil, false
}
if path == "/" {
path = ""
}
h, ok := o.handlers[um][path]
return h, ok
}
// Context returns the middleware context for the goldpinger API
func (o *GoldpingerAPI) Context() *middleware.Context {
if o.context == nil {
o.context = middleware.NewRoutableContext(o.spec, o, nil)
}
return o.context
}
func (o *GoldpingerAPI) initHandlerCache() {
o.Context() // don't care about the result, just that the initialization happened
if o.handlers == nil {
o.handlers = make(map[string]map[string]http.Handler)
}
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/check_all"] = NewCheckAllPods(o.context, o.CheckAllPodsHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/check"] = NewCheckServicePods(o.context, o.CheckServicePodsHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/ping"] = NewPing(o.context, o.PingHandler)
}
// Serve creates a http handler to serve the API over HTTP
// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
func (o *GoldpingerAPI) Serve(builder middleware.Builder) http.Handler {
o.Init()
if o.Middleware != nil {
return o.Middleware(builder)
}
return o.context.APIHandler(builder)
}
// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
func (o *GoldpingerAPI) Init() {
if len(o.handlers) == 0 {
o.initHandlerCache()
}
}
// RegisterConsumer allows you to add (or override) a consumer for a media type.
func (o *GoldpingerAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
o.customConsumers[mediaType] = consumer
}
// RegisterProducer allows you to add (or override) a producer for a media type.
func (o *GoldpingerAPI) RegisterProducer(mediaType string, producer runtime.Producer) {
o.customProducers[mediaType] = producer
}

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
middleware "github.com/go-openapi/runtime/middleware"
)
// PingHandlerFunc turns a function with the right signature into a ping handler
type PingHandlerFunc func(PingParams) middleware.Responder
// Handle executing the request and returning a response
func (fn PingHandlerFunc) Handle(params PingParams) middleware.Responder {
return fn(params)
}
// PingHandler interface for that can handle valid ping params
type PingHandler interface {
Handle(PingParams) middleware.Responder
}
// NewPing creates a new http.Handler for the ping operation
func NewPing(ctx *middleware.Context, handler PingHandler) *Ping {
return &Ping{Context: ctx, Handler: handler}
}
/*Ping swagger:route GET /ping ping
return query stats
*/
type Ping struct {
Context *middleware.Context
Handler PingHandler
}
func (o *Ping) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewPingParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,59 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewPingParams creates a new PingParams object
// no default values defined in spec.
func NewPingParams() PingParams {
return PingParams{}
}
// PingParams contains all the bound params for the ping operation
// typically these are obtained from a http.Request
//
// swagger:parameters ping
type PingParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewPingParams() beforehand.
func (o *PingParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
models "github.com/bloomberg/goldpinger/pkg/models"
)
// PingOKCode is the HTTP code returned for type PingOK
const PingOKCode int = 200
/*PingOK return success
swagger:response pingOK
*/
type PingOK struct {
/*
In: Body
*/
Payload *models.PingResults `json:"body,omitempty"`
}
// NewPingOK creates PingOK with default headers values
func NewPingOK() *PingOK {
return &PingOK{}
}
// WithPayload adds the payload to the ping o k response
func (o *PingOK) WithPayload(payload *models.PingResults) *PingOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the ping o k response
func (o *PingOK) SetPayload(payload *models.PingResults) {
o.Payload = payload
}
// WriteResponse to the client
func (o *PingOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,98 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// PingURL generates an URL for the ping operation
type PingURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *PingURL) WithBasePath(bp string) *PingURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *PingURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *PingURL) Build() (*url.URL, error) {
var result url.URL
var _path = "/ping"
_basePath := o._basePath
result.Path = golangswaggerpaths.Join(_basePath, _path)
return &result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *PingURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *PingURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *PingURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on PingURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on PingURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *PingURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

522
pkg/restapi/server.go Normal file
View File

@@ -0,0 +1,522 @@
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by go-swagger; DO NOT EDIT.
package restapi
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/go-openapi/runtime/flagext"
"github.com/go-openapi/swag"
flags "github.com/jessevdk/go-flags"
"golang.org/x/net/netutil"
"github.com/bloomberg/goldpinger/pkg/restapi/operations"
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
schemeUnix = "unix"
)
var defaultSchemes []string
func init() {
defaultSchemes = []string{
schemeHTTP,
}
}
// NewServer creates a new api goldpinger server but does not configure it
func NewServer(api *operations.GoldpingerAPI) *Server {
s := new(Server)
s.shutdown = make(chan struct{})
s.api = api
s.interrupt = make(chan os.Signal, 1)
return s
}
// ConfigureAPI configures the API and handlers.
func (s *Server) ConfigureAPI() {
if s.api != nil {
s.handler = configureAPI(s.api)
}
}
// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
func (s *Server) ConfigureFlags() {
if s.api != nil {
configureFlags(s.api)
}
}
// Server for the goldpinger API
type Server struct {
EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/goldpinger.sock"`
domainSocketL net.Listener
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
httpServerL net.Listener
TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"`
TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
httpsServerL net.Listener
api *operations.GoldpingerAPI
handler http.Handler
hasListeners bool
shutdown chan struct{}
shuttingDown int32
interrupted bool
interrupt chan os.Signal
}
// Logf logs message either via defined user logger or via system one if no user logger is defined.
func (s *Server) Logf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
} else {
log.Printf(f, args...)
}
}
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
// Exits with non-zero status after printing
func (s *Server) Fatalf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
os.Exit(1)
} else {
log.Fatalf(f, args...)
}
}
// SetAPI configures the server with the specified API. Needs to be called before Serve
func (s *Server) SetAPI(api *operations.GoldpingerAPI) {
if api == nil {
s.api = nil
s.handler = nil
return
}
s.api = api
s.api.Logger = log.Printf
s.handler = configureAPI(api)
}
func (s *Server) hasScheme(scheme string) bool {
schemes := s.EnabledListeners
if len(schemes) == 0 {
schemes = defaultSchemes
}
for _, v := range schemes {
if v == scheme {
return true
}
}
return false
}
// Serve the api
func (s *Server) Serve() (err error) {
if !s.hasListeners {
if err = s.Listen(); err != nil {
return err
}
}
// set default handler, if none is set
if s.handler == nil {
if s.api == nil {
return errors.New("can't create the default handler, as no api is set")
}
s.SetHandler(s.api.Serve(nil))
}
wg := new(sync.WaitGroup)
once := new(sync.Once)
signalNotify(s.interrupt)
go handleInterrupt(once, s)
servers := []*http.Server{}
wg.Add(1)
go s.handleShutdown(wg, &servers)
if s.hasScheme(schemeUnix) {
domainSocket := new(http.Server)
domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
domainSocket.Handler = s.handler
if int64(s.CleanupTimeout) > 0 {
domainSocket.IdleTimeout = s.CleanupTimeout
}
configureServer(domainSocket, "unix", string(s.SocketPath))
wg.Add(1)
s.Logf("Serving goldpinger at unix://%s", s.SocketPath)
go func(l net.Listener) {
defer wg.Done()
if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving goldpinger at unix://%s", s.SocketPath)
}(s.domainSocketL)
servers = append(servers, domainSocket)
}
if s.hasScheme(schemeHTTP) {
httpServer := new(http.Server)
httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpServer.ReadTimeout = s.ReadTimeout
httpServer.WriteTimeout = s.WriteTimeout
httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
if s.ListenLimit > 0 {
s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpServer.IdleTimeout = s.CleanupTimeout
}
httpServer.Handler = s.handler
configureServer(httpServer, "http", s.httpServerL.Addr().String())
wg.Add(1)
s.Logf("Serving goldpinger at http://%s", s.httpServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving goldpinger at http://%s", l.Addr())
}(s.httpServerL)
servers = append(servers, httpServer)
}
if s.hasScheme(schemeHTTPS) {
httpsServer := new(http.Server)
httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpsServer.ReadTimeout = s.TLSReadTimeout
httpsServer.WriteTimeout = s.TLSWriteTimeout
httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
if s.TLSListenLimit > 0 {
s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpsServer.IdleTimeout = s.CleanupTimeout
}
httpsServer.Handler = s.handler
// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
httpsServer.TLSConfig = &tls.Config{
// Causes servers to use Go's default ciphersuite preferences,
// which are tuned to avoid attacks. Does nothing on clients.
PreferServerCipherSuites: true,
// Only use curves which have assembly implementations
// https://github.com/golang/go/tree/master/src/crypto/elliptic
CurvePreferences: []tls.CurveID{tls.CurveP256},
// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
NextProtos: []string{"http/1.1", "h2"},
// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
MinVersion: tls.VersionTLS12,
// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
}
// build standard config from server options
if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
if err != nil {
return err
}
}
if s.TLSCACertificate != "" {
// include specified CA certificate
caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate))
if caCertErr != nil {
return caCertErr
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
return fmt.Errorf("cannot parse CA certificate")
}
httpsServer.TLSConfig.ClientCAs = caCertPool
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
// call custom TLS configurator
configureTLS(httpsServer.TLSConfig)
if len(httpsServer.TLSConfig.Certificates) == 0 {
// after standard and custom config are passed, this ends up with no certificate
if s.TLSCertificate == "" {
if s.TLSCertificateKey == "" {
s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
}
s.Fatalf("the required flag `--tls-certificate` was not specified")
}
if s.TLSCertificateKey == "" {
s.Fatalf("the required flag `--tls-key` was not specified")
}
// this happens with a wrong custom TLS configurator
s.Fatalf("no certificate was configured for TLS")
}
// must have at least one certificate or panics
httpsServer.TLSConfig.BuildNameToCertificate()
configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
wg.Add(1)
s.Logf("Serving goldpinger at https://%s", s.httpsServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving goldpinger at https://%s", l.Addr())
}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
servers = append(servers, httpsServer)
}
wg.Wait()
return nil
}
// Listen creates the listeners for the server
func (s *Server) Listen() error {
if s.hasListeners { // already done this
return nil
}
if s.hasScheme(schemeHTTPS) {
// Use http host if https host wasn't defined
if s.TLSHost == "" {
s.TLSHost = s.Host
}
// Use http listen limit if https listen limit wasn't defined
if s.TLSListenLimit == 0 {
s.TLSListenLimit = s.ListenLimit
}
// Use http tcp keep alive if https tcp keep alive wasn't defined
if int64(s.TLSKeepAlive) == 0 {
s.TLSKeepAlive = s.KeepAlive
}
// Use http read timeout if https read timeout wasn't defined
if int64(s.TLSReadTimeout) == 0 {
s.TLSReadTimeout = s.ReadTimeout
}
// Use http write timeout if https write timeout wasn't defined
if int64(s.TLSWriteTimeout) == 0 {
s.TLSWriteTimeout = s.WriteTimeout
}
}
if s.hasScheme(schemeUnix) {
domSockListener, err := net.Listen("unix", string(s.SocketPath))
if err != nil {
return err
}
s.domainSocketL = domSockListener
}
if s.hasScheme(schemeHTTP) {
listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
if err != nil {
return err
}
h, p, err := swag.SplitHostPort(listener.Addr().String())
if err != nil {
return err
}
s.Host = h
s.Port = p
s.httpServerL = listener
}
if s.hasScheme(schemeHTTPS) {
tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
if err != nil {
return err
}
sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
if err != nil {
return err
}
s.TLSHost = sh
s.TLSPort = sp
s.httpsServerL = tlsListener
}
s.hasListeners = true
return nil
}
// Shutdown server and clean up resources
func (s *Server) Shutdown() error {
if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
close(s.shutdown)
}
return nil
}
func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
// wg.Done must occur last, after s.api.ServerShutdown()
// (to preserve old behaviour)
defer wg.Done()
<-s.shutdown
servers := *serversPtr
ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
defer cancel()
shutdownChan := make(chan bool)
for i := range servers {
server := servers[i]
go func() {
var success bool
defer func() {
shutdownChan <- success
}()
if err := server.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout:
s.Logf("HTTP server Shutdown: %v", err)
} else {
success = true
}
}()
}
// Wait until all listeners have successfully shut down before calling ServerShutdown
success := true
for range servers {
success = success && <-shutdownChan
}
if success {
s.api.ServerShutdown()
}
}
// GetHandler returns a handler useful for testing
func (s *Server) GetHandler() http.Handler {
return s.handler
}
// SetHandler allows for setting a http handler on this server
func (s *Server) SetHandler(handler http.Handler) {
s.handler = handler
}
// UnixListener returns the domain socket listener
func (s *Server) UnixListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.domainSocketL, nil
}
// HTTPListener returns the http listener
func (s *Server) HTTPListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpServerL, nil
}
// TLSListener returns the https listener
func (s *Server) TLSListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpsServerL, nil
}
func handleInterrupt(once *sync.Once, s *Server) {
once.Do(func() {
for _ = range s.interrupt {
if s.interrupted {
s.Logf("Server already shutting down")
continue
}
s.interrupted = true
s.Logf("Shutting down... ")
if err := s.Shutdown(); err != nil {
s.Logf("HTTP server Shutdown: %v", err)
}
}
})
}
func signalNotify(interrupt chan<- os.Signal) {
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
}

357
static/index.html Normal file
View File

@@ -0,0 +1,357 @@
<!--
Copyright 2018 Bloomberg Finance L.P.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Goldpinger</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/lodash/4.14.0/lodash.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/sigma.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.exporters.svg.min.js"></script>:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.layout.forceAtlas2.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.neo4j.cypher.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.parsers.cypher.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.parsers.gexf.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.parsers.json.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.pathfinding.astar.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.plugins.animate.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.plugins.dragNodes.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.plugins.filter.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.plugins.neighborhoods.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.plugins.relativeSize.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.renderers.customEdgeShapes.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.renderers.customShapes.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.renderers.edgeLabels.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.renderers.parallelEdges.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.renderers.snapshot.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/plugins/sigma.statistics.HITS.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<style type="text/css">
body {
margin: 0;
}
#container {
}
#graph-container {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
/*background-color: white;*/
}
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Goldpinger</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Graph</a></li>
<li><a href="#" id="show-data">Data</a></li>
<li><a href="/check_all" >Raw</a></li>
<li><a href="/metrics" >Metrics</a></li>
</ul>
<div class="navbar-form navbar-left" role="search">
<div class="form-group">
<!-- <input id="timeout" type="number" step="any" class="form-control" placeholder="5.0" value="5.0"> -->
</div>
<button id="reload-graph" class="btn btn-default">reload</button>
</div>
</div>
</div>
</nav>
<div class="container">
<div id="graph-container"></div>
</div>
</div>
<div id="modal-window-code" class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="modal-title">title</h4>
</div>
<div class="modal-body" id="modal-body">
body
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
var fetchJSON = function(url) {
return new Promise(function(resolve, reject) {
console.log("calling " + url);
var req = new XMLHttpRequest();
req.open('get', url, true);
req.responseType = 'json';
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
} else {
reject({
status: req.status,
response: req.response,
statusText: req.statusText
});
}
};
req.send();
});
};
var prepareJSON = function(subject){
return "<code style='white-space: pre;'>"
+ JSON.stringify(subject, undefined, 4)
+ "</code>"
}
var loadingDialog = function(enable){
$('#modal-title').html("Loading");
$('#modal-body').html(prepareJSON({stuff:{will_be_there:true, soon: "Math.random() > 0.5"}}));
// show the things
if (enable){
$('#modal-window-code').modal('show');
} else {
$('#modal-window-code').modal('hide');
}
};
var global_data = undefined;
var s; // the sigma graph
var main = function(timeout){
timeout = timeout || Number($("#timeout").val()) || 5.0;
loadingDialog(true);
fetchJSON("/check_all?timeout="+timeout)
.then(function(data){
loadingDialog(false);
//console.log(data);
global_data = data;
// prepare nodes
var nodes = [];
var resp = data.responses;
_.forOwn(resp, function(value, podIP) {
nodes.push({
"label": podIP+ " (" + value.HostIP + ")",
"id": podIP,
"x": 0,
"y": 0,
_data: value
});
});
//console.log(nodes);
// prepare the edges
var edges = [];
var resp = data.responses;
_.forOwn(resp, function(value, source) {
var call = value.response;
if (!_.isString(call)){
_.forOwn(call, function(value, target){
edges.push({
source: source,
target: target,
_data: value
});
});
}
});
//console.log(edges);
// build the actual graph
s = new sigma({
graph: {
nodes: [],
edges: []
},
renderer: {
container: document.getElementById('graph-container'),
type: 'canvas'
},
settings: {
edgeLabelSize: 'proportional',
minArrowSize: 7,
minNodeSize: 10,
sideMargin: 0.45
}
});
// generate the nodes on the graph
nodes.forEach(function(node, i, a){
node.x = Math.cos(Math.PI * 2 * i / a.length);
node.y = Math.sin(Math.PI * 2 * i / a.length);
node.size = 10;
node.color = "#4CC40B";
if (!node._data.OK) {
node.color = "red";
}
//console.log(node);
s.graph.addNode(node);
});
// generate the edges
edges.forEach(function(edge, i){
var color = "#ccc";
var type = "curvedArrow";
if (edge.source == edge.target){
color = "gray";
type = "curve";
}
if (!edge._data.OK) {
color = "red";
}
var edge = {
id: "e" + i,
source: edge.source,
target: edge.target,
type: type,
color: color,
label: edge._data.elapsed,
size: 7
}
//console.log(edge);
s.graph.addEdge(edge)
});
console.log(s.graph.nodes());
console.log(s.graph.edges());
s.refresh();
var hideNotTouching = function(dst){
s.graph.edges().forEach(function(edge){
if (dst == undefined) {
// revert everything to the original state
if (edge._color){
edge.color = edge._color;
delete edge._color;
}
} else if (edge.source != dst){
// hide
if (!edge._color){
edge._color = edge.color;
}
edge.color = "#faf8f6";
} else {
// show
if (edge._color) {
edge.color = edge._color;
}
}
});
s.refresh();
}
s.bind("clickNode", function (e) {
var node = e.data.node;
hideNotTouching(node.id);
// fill the voids
$('#modal-title').html(node.id);
$('#modal-body').html(prepareJSON(node._data));
// show the things
$('#modal-window-code').modal('show');
});
s.bind("clickStage", function (e) {
hideNotTouching();
});
s.bind("clickEdge", function (e) {
var edge = e.data.edge;
console.log(edge);
// fill the voids
$('#modal-title').html(edge.id);
$('#modal-body').html(prepareJSON(edge._data));
// show the things
$('#modal-window-code').modal('show');
});
})
.catch(function(err){
console.error(err);
$('#modal-title').html("Error");
$('#modal-body').html(prepareJSON(err));
$('#modal-window-code').modal('show');
});
};
main();
$("#show-data").click(function (e) {
// fill the voids
$('#modal-title').html("/check_all");
$('#modal-body').html(prepareJSON(global_data));
// show the things
$('#modal-window-code').modal('show');
});
$("#reload-graph").click(function (e) {
s.kill();
main();
});
</script>
</body>
</html>

122
swagger.yml Normal file
View File

@@ -0,0 +1,122 @@
---
swagger: '2.0'
info:
version: 1.0.0
title: Goldpinger
definitions:
CallStats:
properties:
check:
type: integer
check_all:
type: integer
ping:
type: integer
PingResults:
type: object
properties:
boot_time:
format: date-time
type: string
received:
$ref: '#/definitions/CallStats'
PodResult:
type: object
properties:
OK:
type: boolean
default: false
HostIP:
type: string
format: ipv4
response:
$ref: '#/definitions/PingResults'
error:
type: string
status-code:
type: integer
format: int32
CheckResults:
type: object
additionalProperties:
$ref: '#/definitions/PodResult'
CheckAllPodResult:
type: object
properties:
OK:
type: boolean
default: false
HostIP:
type: string
format: ipv4
response:
$ref: '#/definitions/CheckResults'
error:
type: string
status-code:
type: integer
format: int32
CheckAllResults:
type: object
properties:
OK:
type: boolean
default: false
hosts-healthy:
type: integer
format: int32
hosts-number:
type: integer
format: int32
hosts:
type: array
items:
type: object
properties:
hostIP:
type: string
format: ipv4
podIP:
type: string
format: ipv4
responses:
type: object
additionalProperties:
$ref: '#/definitions/CheckAllPodResult'
paths:
/ping:
get:
description: return query stats
produces:
- application/json
operationId: ping
responses:
200:
description: return success
schema:
$ref: '#/definitions/PingResults'
/check:
get:
description: Queries the API server for all other pods in this service,
and pings them via their pods IPs. Calls their /ping endpoint
produces:
- application/json
operationId: checkServicePods
responses:
200:
description: Success, return response
schema:
$ref: '#/definitions/CheckResults'
/check_all:
get:
description: Queries the API server for all other pods in this service,
and makes all of them query all of their neighbours,
using their pods IPs. Calls their /check endpoint.
produces:
- application/json
operationId: checkAllPods
responses:
200:
description: Success, return response
schema:
$ref: '#/definitions/CheckAllResults'