diff --git a/.editorconfig b/.editorconfig index 831bb8696cc..3701d80b453 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*.go] -indent_style = tabs +indent_style = tab indent_size = 2 charset = utf-8 trim_trailing_whitespace = true diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e79741bbe6c..fe0a1d6c548 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -12,11 +12,11 @@ grunt karma:dev ### Run tests for backend assets before commit ``` -test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" +test -z "$(gofmt -s -l . | grep -v -E 'vendor/(github.com|golang.org|gopkg.in)' | tee /dev/stderr)" ``` ### Run tests for frontend assets before commit ``` -grunt test -godep go test -v ./pkg/... +npm test +go test -v ./pkg/... ``` diff --git a/.gitignore b/.gitignore index 515c19e7e0b..721a2a71ad4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ public/css/*.min.css *.swp .idea/ *.iml +.vscode/ /data/* /bin/* @@ -37,4 +38,4 @@ profile.cov .notouch /pkg/cmd/grafana-cli/grafana-cli /pkg/cmd/grafana-server/grafana-server -/examples/*/dist \ No newline at end of file +/examples/*/dist diff --git a/.hooks/pre-commit b/.hooks/pre-commit index 2519d4c44f4..0d7c6c9ee5f 100755 --- a/.hooks/pre-commit +++ b/.hooks/pre-commit @@ -1,6 +1,6 @@ #!/usr/bin/env bash -test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" +test -z "$(gofmt -s -l . | grep -v vendor/src/ | tee /dev/stderr)" if [ $? -gt 0 ]; then echo "Some files aren't formatted, please run 'go fmt ./pkg/...' to format your source code before committing" exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c4614d024d3..c842b3b031a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,26 @@ * **SingleStat**: Add seriename as option in singlestat panel, closes [#4740](https://github.com/grafana/grafana/issues/4740) * **Localization**: Week start day now dependant on browser locale setting, closes [#3003](https://github.com/grafana/grafana/issues/3003) * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021) +* **Templating**: Add support for numeric and alphabetical sorting of variable values, closes [#2839](https://github.com/grafana/grafana/issues/2839) * **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689) * **Navigation**: Add search to org swithcer, closes [#2609](https://github.com/grafana/grafana/issues/2609) * **Database**: Allow database config using one propertie, closes [#5456](https://github.com/grafana/grafana/pull/5456) * **Graphite**: Add support for groupByNode, closes [#5613](https://github.com/grafana/grafana/pull/5613) * **Influxdb**: Add support for elapsed(), closes [#5827](https://github.com/grafana/grafana/pull/5827) +* **OAuth**: Add support for generic oauth, closes [#4718](https://github.com/grafana/grafana/pull/4718) +* **Cloudwatch**: Add support to expand multi select template variable, closes [#5003](https://github.com/grafana/grafana/pull/5003) +* **Graph Panel**: Now supports flexible lower/upper bounds on Y-Max and Y-Min, PR [#5720](https://github.com/grafana/grafana/pull/5720) + +### Breaking changes +* **SystemD**: Change systemd description, closes [#5971](https://github.com/grafana/grafana/pull/5971) +* **lodash upgrade**: Upgraded lodash from 2.4.2 to 4.15.0, this contains a number of breaking changes that could effect plugins. closes [#6021](https://github.com/grafana/grafana/pull/6021) + +### Bugfixes +* **Table Panel**: Fixed problem when switching to Mixed datasource in metrics tab, fixes [#5999](https://github.com/grafana/grafana/pull/5999) +* **Playlist**: Fixed problem with play order not matching order defined in playlist, fixes [#5467](https://github.com/grafana/grafana/pull/5467) +* **Graph panel**: Fixed problem with auto decimals on y axis when datamin=datamax, fixes [#6070](https://github.com/grafana/grafana/pull/6070) +* **Snapshot**: Can view embedded panels/png rendered panels in snapshots without login, fixes [#3769](https://github.com/grafana/grafana/pull/3769) +* **Elasticsearch**: Fix for query template variable when looking up terms without query, no longer relies on elasticsearch default field, fixes [#3887](https://github.com/grafana/grafana/pull/3887) # 3.1.2 (unreleased) * **Templating**: Fixed issue when combining row & panel repeats, fixes [#5790](https://github.com/grafana/grafana/issues/5790) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json deleted file mode 100644 index 7d0d7f15009..00000000000 --- a/Godeps/Godeps.json +++ /dev/null @@ -1,384 +0,0 @@ -{ - "ImportPath": "github.com/grafana/grafana", - "GoVersion": "go1.5.1", - "GodepVersion": "v60", - "Packages": [ - "./pkg/..." - ], - "Deps": [ - { - "ImportPath": "github.com/BurntSushi/toml", - "Comment": "v0.1.0-21-g056c9bc", - "Rev": "056c9bc7be7190eaa7715723883caffa5f8fa3e4" - }, - { - "ImportPath": "github.com/Unknwon/com", - "Rev": "d9bcf409c8a368d06c9b347705c381e7c12d54df" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/awserr", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/client", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/defaults", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/request", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/session", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/endpoints", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/aws/signer/v4", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/private/waiter", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/service/ec2", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/aws/aws-sdk-go/service/sts", - "Comment": "v1.4.1", - "Rev": "f80e7d0182a463dff0c0da6bbed57f21369d4346" - }, - { - "ImportPath": "github.com/benbjohnson/clock", - "Rev": "a620c1cc9866f84a2550ad53f4f353ec030fa26b" - }, - { - "ImportPath": "github.com/bmizerany/assert", - "Comment": "release.r60-6-ge17e998", - "Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3" - }, - { - "ImportPath": "github.com/bradfitz/gomemcache/memcache", - "Comment": "release.r60-40-g72a6864", - "Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8" - }, - { - "ImportPath": "github.com/codegangsta/cli", - "Comment": "1.2.0-187-gc31a797", - "Rev": "c31a7975863e7810c92e2e288a9ab074f9a88f29" - }, - { - "ImportPath": "github.com/davecgh/go-spew/spew", - "Rev": "2df174808ee097f90d259e432cc04442cf60be21" - }, - { - "ImportPath": "github.com/fatih/color", - "Comment": "v0.1-16-g4f7bcef", - "Rev": "4f7bcef27eec7925456d0c30c5e7b0408b3339be" - }, - { - "ImportPath": "github.com/franela/goreq", - "Rev": "3ddeded65be21dacb5a2e2d0b95af9ff6862a2b5" - }, - { - "ImportPath": "github.com/go-ini/ini", - "Comment": "v0-48-g060d7da", - "Rev": "060d7da055ba6ec5ea7a31f116332fe5efa04ce0" - }, - { - "ImportPath": "github.com/go-ldap/ldap", - "Comment": "v2.2.1", - "Rev": "07a7330929b9ee80495c88a4439657d89c7dbd87" - }, - { - "ImportPath": "github.com/go-macaron/binding", - "Rev": "2502aaf4bce3a4e6451b4610847bfb8dffdb6266" - }, - { - "ImportPath": "github.com/go-macaron/gzip", - "Rev": "4938e9be6b279d8426cb1c89a6bcf7af70b0c21d" - }, - { - "ImportPath": "github.com/go-macaron/inject", - "Rev": "c5ab7bf3a307593cd44cb272d1a5beea473dd072" - }, - { - "ImportPath": "github.com/go-macaron/session", - "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" - }, - { - "ImportPath": "github.com/go-macaron/session/memcache", - "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" - }, - { - "ImportPath": "github.com/go-macaron/session/mysql", - "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" - }, - { - "ImportPath": "github.com/go-macaron/session/postgres", - "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" - }, - { - "ImportPath": "github.com/go-macaron/session/redis", - "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" - }, - { - "ImportPath": "github.com/go-sql-driver/mysql", - "Comment": "v1.2-171-g267b128", - "Rev": "267b128680c46286b9ca13475c3cca5de8f79bd7" - }, - { - "ImportPath": "github.com/go-stack/stack", - "Comment": "v1.5.2", - "Rev": "100eb0c0a9c5b306ca2fb4f165df21d80ada4b82" - }, - { - "ImportPath": "github.com/go-xorm/core", - "Comment": "v0.4.4-7-g9e608f7", - "Rev": "9e608f7330b9d16fe2818cfe731128b3f156cb9a" - }, - { - "ImportPath": "github.com/go-xorm/xorm", - "Comment": "v0.4.4-44-gf561133", - "Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58" - }, - { - "ImportPath": "github.com/gorilla/websocket", - "Rev": "c45a635370221f34fea2d5163fd156fcb4e38e8a" - }, - { - "ImportPath": "github.com/gosimple/slug", - "Rev": "8d258463b4459f161f51d6a357edacd3eef9d663" - }, - { - "ImportPath": "github.com/hashicorp/go-version", - "Rev": "7e3c02b30806fa5779d3bdfc152ce4c6f40e7b38" - }, - { - "ImportPath": "github.com/inconshreveable/log15", - "Comment": "v2.3-61-g20bca5a", - "Rev": "20bca5a7a57282e241fac83ec9ea42538027f1c1" - }, - { - "ImportPath": "github.com/inconshreveable/log15/term", - "Comment": "v2.3-61-g20bca5a", - "Rev": "20bca5a7a57282e241fac83ec9ea42538027f1c1" - }, - { - "ImportPath": "github.com/jmespath/go-jmespath", - "Comment": "0.2.2", - "Rev": "3433f3ea46d9f8019119e7dd41274e112a2359a9" - }, - { - "ImportPath": "github.com/jtolds/gls", - "Rev": "f1ac7f4f24f50328e6bc838ca4437d1612a0243c" - }, - { - "ImportPath": "github.com/klauspost/compress/flate", - "Rev": "7b02889a2005228347aef0e76beeaee564d82f8c" - }, - { - "ImportPath": "github.com/klauspost/compress/gzip", - "Rev": "7b02889a2005228347aef0e76beeaee564d82f8c" - }, - { - "ImportPath": "github.com/klauspost/cpuid", - "Rev": "349c675778172472f5e8f3a3e0fe187e302e5a10" - }, - { - "ImportPath": "github.com/klauspost/crc32", - "Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d" - }, - { - "ImportPath": "github.com/kr/pretty", - "Comment": "go.weekly.2011-12-22-27-ge6ac2fc", - "Rev": "e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb" - }, - { - "ImportPath": "github.com/kr/s3", - "Rev": "c070c8f9a8f0032d48f0d2a77d4e382788bd8a1d" - }, - { - "ImportPath": "github.com/kr/s3/s3util", - "Rev": "c070c8f9a8f0032d48f0d2a77d4e382788bd8a1d" - }, - { - "ImportPath": "github.com/kr/text", - "Rev": "bb797dc4fb8320488f47bf11de07a733d7233e1f" - }, - { - "ImportPath": "github.com/lib/pq", - "Comment": "go1.0-cutoff-13-g19eeca3", - "Rev": "19eeca3e30d2577b1761db471ec130810e67f532" - }, - { - "ImportPath": "github.com/lib/pq/oid", - "Comment": "go1.0-cutoff-13-g19eeca3", - "Rev": "19eeca3e30d2577b1761db471ec130810e67f532" - }, - { - "ImportPath": "github.com/mattn/go-colorable", - "Rev": "9cbef7c35391cca05f15f8181dc0b18bc9736dbb" - }, - { - "ImportPath": "github.com/mattn/go-isatty", - "Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639" - }, - { - "ImportPath": "github.com/mattn/go-sqlite3", - "Comment": "v1.1.0-67-g7204887", - "Rev": "7204887cf3a42df1cfaa5505dc3a3427f6dded8b" - }, - { - "ImportPath": "github.com/rainycape/unidecode", - "Rev": "836ef0a715aedf08a12d595ed73ec8ed5b288cac" - }, - { - "ImportPath": "github.com/smartystreets/goconvey/convey", - "Comment": "1.5.0-356-gfbc0a1c", - "Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123" - }, - { - "ImportPath": "github.com/smartystreets/goconvey/convey/assertions", - "Comment": "1.5.0-356-gfbc0a1c", - "Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123" - }, - { - "ImportPath": "github.com/smartystreets/goconvey/convey/assertions/oglematchers", - "Comment": "1.5.0-356-gfbc0a1c", - "Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123" - }, - { - "ImportPath": "github.com/smartystreets/goconvey/convey/gotest", - "Comment": "1.5.0-356-gfbc0a1c", - "Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123" - }, - { - "ImportPath": "github.com/smartystreets/goconvey/convey/reporting", - "Comment": "1.5.0-356-gfbc0a1c", - "Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123" - }, - { - "ImportPath": "github.com/streadway/amqp", - "Rev": "150b7f24d6ad507e6026c13d85ce1f1391ac7400" - }, - { - "ImportPath": "golang.org/x/net/context", - "Rev": "972f0c5fbe4ae29e666c3f78c3ed42ae7a448b0a" - }, - { - "ImportPath": "golang.org/x/oauth2", - "Rev": "c58fcf0ffc1c772aa2e1ee4894bc19f2649263b2" - }, - { - "ImportPath": "golang.org/x/sys/unix", - "Rev": "7a56174f0086b32866ebd746a794417edbc678a1" - }, - { - "ImportPath": "gopkg.in/asn1-ber.v1", - "Comment": "v1", - "Rev": "9eae18c3681ae3d3c677ac2b80a8fe57de45fc09" - }, - { - "ImportPath": "gopkg.in/bufio.v1", - "Comment": "v1", - "Rev": "567b2bfa514e796916c4747494d6ff5132a1dfce" - }, - { - "ImportPath": "gopkg.in/ini.v1", - "Comment": "v0-16-g1772191", - "Rev": "177219109c97e7920c933e21c9b25f874357b237" - }, - { - "ImportPath": "gopkg.in/macaron.v1", - "Rev": "1c6dd87797ae9319b4658cbd48d1d0420b279fd5" - }, - { - "ImportPath": "gopkg.in/redis.v2", - "Comment": "v2.3.2", - "Rev": "e6179049628164864e6e84e973cfb56335748dea" - } - ] -} diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore deleted file mode 100644 index f037d684ef2..00000000000 --- a/Godeps/_workspace/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/pkg -/bin diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awsmigrate/awsmigrate-renamer/Godeps/Readme b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awsmigrate/awsmigrate-renamer/Godeps/Readme deleted file mode 100644 index 4cdaa53d56d..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awsmigrate/awsmigrate-renamer/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert.go deleted file mode 100644 index 10ad72767ba..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert.go +++ /dev/null @@ -1,163 +0,0 @@ -package awstesting - -import ( - "encoding/json" - "encoding/xml" - "fmt" - "net/url" - "reflect" - "regexp" - "sort" - "testing" -) - -// Match is a testing helper to test for testing error by comparing expected -// with a regular expression. -func Match(t *testing.T, regex, expected string) { - if !regexp.MustCompile(regex).Match([]byte(expected)) { - t.Errorf("%q\n\tdoes not match /%s/", expected, regex) - } -} - -// AssertURL verifies the expected URL is matches the actual. -func AssertURL(t *testing.T, expect, actual string, msgAndArgs ...interface{}) bool { - expectURL, err := url.Parse(expect) - if err != nil { - t.Errorf(errMsg("unable to parse expected URL", err, msgAndArgs)) - return false - } - actualURL, err := url.Parse(actual) - if err != nil { - t.Errorf(errMsg("unable to parse actual URL", err, msgAndArgs)) - return false - } - - equal(t, expectURL.Host, actualURL.Host, msgAndArgs...) - equal(t, expectURL.Scheme, actualURL.Scheme, msgAndArgs...) - equal(t, expectURL.Path, actualURL.Path, msgAndArgs...) - - return AssertQuery(t, expectURL.Query().Encode(), actualURL.Query().Encode(), msgAndArgs...) -} - -// AssertQuery verifies the expect HTTP query string matches the actual. -func AssertQuery(t *testing.T, expect, actual string, msgAndArgs ...interface{}) bool { - expectQ, err := url.ParseQuery(expect) - if err != nil { - t.Errorf(errMsg("unable to parse expected Query", err, msgAndArgs)) - return false - } - actualQ, err := url.ParseQuery(expect) - if err != nil { - t.Errorf(errMsg("unable to parse actual Query", err, msgAndArgs)) - return false - } - - // Make sure the keys are the same - if !equal(t, queryValueKeys(expectQ), queryValueKeys(actualQ), msgAndArgs...) { - return false - } - - for k, expectQVals := range expectQ { - sort.Strings(expectQVals) - actualQVals := actualQ[k] - sort.Strings(actualQVals) - equal(t, expectQVals, actualQVals, msgAndArgs...) - } - - return true -} - -// AssertJSON verifies that the expect json string matches the actual. -func AssertJSON(t *testing.T, expect, actual string, msgAndArgs ...interface{}) bool { - expectVal := map[string]interface{}{} - if err := json.Unmarshal([]byte(expect), &expectVal); err != nil { - t.Errorf(errMsg("unable to parse expected JSON", err, msgAndArgs...)) - return false - } - - actualVal := map[string]interface{}{} - if err := json.Unmarshal([]byte(actual), &actualVal); err != nil { - t.Errorf(errMsg("unable to parse actual JSON", err, msgAndArgs...)) - return false - } - - return equal(t, expectVal, actualVal, msgAndArgs...) -} - -// AssertXML verifies that the expect xml string matches the actual. -func AssertXML(t *testing.T, expect, actual string, container interface{}, msgAndArgs ...interface{}) bool { - expectVal := container - if err := xml.Unmarshal([]byte(expect), &expectVal); err != nil { - t.Errorf(errMsg("unable to parse expected XML", err, msgAndArgs...)) - } - - actualVal := container - if err := xml.Unmarshal([]byte(actual), &actualVal); err != nil { - t.Errorf(errMsg("unable to parse actual XML", err, msgAndArgs...)) - } - return equal(t, expectVal, actualVal, msgAndArgs...) -} - -// objectsAreEqual determines if two objects are considered equal. -// -// This function does no assertion of any kind. -// -// Based on github.com/stretchr/testify/assert.ObjectsAreEqual -// Copied locally to prevent non-test build dependencies on testify -func objectsAreEqual(expected, actual interface{}) bool { - if expected == nil || actual == nil { - return expected == actual - } - - return reflect.DeepEqual(expected, actual) -} - -// Equal asserts that two objects are equal. -// -// assert.Equal(t, 123, 123, "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Based on github.com/stretchr/testify/assert.Equal -// Copied locally to prevent non-test build dependencies on testify -func equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if !objectsAreEqual(expected, actual) { - t.Errorf("Not Equal:\n\t%#v (expected)\n\t%#v (actual), %s", - expected, actual, messageFromMsgAndArgs(msgAndArgs)) - return false - } - - return true -} - -func errMsg(baseMsg string, err error, msgAndArgs ...interface{}) string { - message := messageFromMsgAndArgs(msgAndArgs) - if message != "" { - message += ", " - } - return fmt.Sprintf("%s%s, %v", message, baseMsg, err) -} - -// Based on github.com/stretchr/testify/assert.messageFromMsgAndArgs -// Copied locally to prevent non-test build dependencies on testify -func messageFromMsgAndArgs(msgAndArgs []interface{}) string { - if len(msgAndArgs) == 0 || msgAndArgs == nil { - return "" - } - if len(msgAndArgs) == 1 { - return msgAndArgs[0].(string) - } - if len(msgAndArgs) > 1 { - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } - return "" -} - -func queryValueKeys(v url.Values) []string { - keys := make([]string, 0, len(v)) - for k := range v { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert_test.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert_test.go deleted file mode 100644 index 45903a5d35c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/assert_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package awstesting_test - -import ( - "encoding/xml" - "testing" - - "github.com/aws/aws-sdk-go/awstesting" -) - -func TestAssertJSON(t *testing.T) { - cases := []struct { - e, a string - asserts bool - }{ - { - e: `{"RecursiveStruct":{"RecursiveMap":{"foo":{"NoRecurse":"foo"},"bar":{"NoRecurse":"bar"}}}}`, - a: `{"RecursiveStruct":{"RecursiveMap":{"bar":{"NoRecurse":"bar"},"foo":{"NoRecurse":"foo"}}}}`, - asserts: true, - }, - } - - for i, c := range cases { - mockT := &testing.T{} - if awstesting.AssertJSON(mockT, c.e, c.a) != c.asserts { - t.Error("Assert JSON result was not expected.", i) - } - } -} - -func TestAssertXML(t *testing.T) { - cases := []struct { - e, a string - asserts bool - container struct { - XMLName xml.Name `xml:"OperationRequest"` - NS string `xml:"xmlns,attr"` - RecursiveStruct struct { - RecursiveMap struct { - Entries []struct { - XMLName xml.Name `xml:"entries"` - Key string `xml:"key"` - Value struct { - XMLName xml.Name `xml:"value"` - NoRecurse string - } - } - } - } - } - }{ - { - e: `foofoobarbar`, - a: `barbarfoofoo`, - asserts: true, - }, - } - - for i, c := range cases { - // mockT := &testing.T{} - if awstesting.AssertXML(t, c.e, c.a, c.container) != c.asserts { - t.Error("Assert XML result was not expected.", i) - } - } -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/client.go deleted file mode 100644 index ca64a447815..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/client.go +++ /dev/null @@ -1,20 +0,0 @@ -package awstesting - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/client/metadata" - "github.com/aws/aws-sdk-go/aws/defaults" -) - -// NewClient creates and initializes a generic service client for testing. -func NewClient(cfgs ...*aws.Config) *client.Client { - info := metadata.ClientInfo{ - Endpoint: "http://endpoint", - SigningName: "", - } - def := defaults.Get() - def.Config.MergeIn(cfgs...) - - return client.New(*def.Config, info, def.Handlers) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/integration_test.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/integration_test.go deleted file mode 100644 index 93d5ff60f51..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/integration_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build integration - -// Package s3_test runs integration tests for S3 -package s3_test - -import ( - "bytes" - "fmt" - "io/ioutil" - "net/http" - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration" - "github.com/aws/aws-sdk-go/service/s3" -) - -var bucketName *string -var svc *s3.S3 - -func TestMain(m *testing.M) { - setup() - defer teardown() // only called if we panic - result := m.Run() - teardown() - os.Exit(result) -} - -// Create a bucket for testing -func setup() { - svc = s3.New(integration.Session) - bucketName = aws.String( - fmt.Sprintf("aws-sdk-go-integration-%d-%s", time.Now().Unix(), integration.UniqueID())) - - for i := 0; i < 10; i++ { - _, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName}) - if err == nil { - break - } - } - - for { - _, err := svc.HeadBucket(&s3.HeadBucketInput{Bucket: bucketName}) - if err == nil { - break - } - time.Sleep(1 * time.Second) - } -} - -// Delete the bucket -func teardown() { - resp, _ := svc.ListObjects(&s3.ListObjectsInput{Bucket: bucketName}) - for _, o := range resp.Contents { - svc.DeleteObject(&s3.DeleteObjectInput{Bucket: bucketName, Key: o.Key}) - } - svc.DeleteBucket(&s3.DeleteBucketInput{Bucket: bucketName}) -} - -func TestWriteToObject(t *testing.T) { - _, err := svc.PutObject(&s3.PutObjectInput{ - Bucket: bucketName, - Key: aws.String("key name"), - Body: bytes.NewReader([]byte("hello world")), - }) - assert.NoError(t, err) - - resp, err := svc.GetObject(&s3.GetObjectInput{ - Bucket: bucketName, - Key: aws.String("key name"), - }) - assert.NoError(t, err) - - b, _ := ioutil.ReadAll(resp.Body) - assert.Equal(t, []byte("hello world"), b) -} - -func TestPresignedGetPut(t *testing.T) { - putreq, _ := svc.PutObjectRequest(&s3.PutObjectInput{ - Bucket: bucketName, - Key: aws.String("presigned-key"), - }) - var err error - - // Presign a PUT request - var puturl string - puturl, err = putreq.Presign(300 * time.Second) - assert.NoError(t, err) - - // PUT to the presigned URL with a body - var puthttpreq *http.Request - buf := bytes.NewReader([]byte("hello world")) - puthttpreq, err = http.NewRequest("PUT", puturl, buf) - assert.NoError(t, err) - - var putresp *http.Response - putresp, err = http.DefaultClient.Do(puthttpreq) - assert.NoError(t, err) - assert.Equal(t, 200, putresp.StatusCode) - - // Presign a GET on the same URL - getreq, _ := svc.GetObjectRequest(&s3.GetObjectInput{ - Bucket: bucketName, - Key: aws.String("presigned-key"), - }) - - var geturl string - geturl, err = getreq.Presign(300 * time.Second) - assert.NoError(t, err) - - // Get the body - var getresp *http.Response - getresp, err = http.Get(geturl) - assert.NoError(t, err) - - var b []byte - defer getresp.Body.Close() - b, err = ioutil.ReadAll(getresp.Body) - assert.Equal(t, "hello world", string(b)) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/client.go deleted file mode 100644 index 83632d1a36b..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/client.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build integration - -//Package s3crypto provides gucumber integration tests support. -package s3crypto - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3crypto" - - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@s3crypto", func() { - sess := session.New((&aws.Config{ - Region: aws.String("us-west-2"), - }).WithLogLevel(aws.LogDebugWithRequestRetries | aws.LogDebugWithRequestErrors)) - encryptionClient := s3crypto.NewEncryptionClient(sess, nil, func(c *s3crypto.EncryptionClient) { - }) - gucumber.World["encryptionClient"] = encryptionClient - - decryptionClient := s3crypto.NewDecryptionClient(sess) - gucumber.World["decryptionClient"] = decryptionClient - - gucumber.World["client"] = s3.New(sess) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/s3_crypto.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/s3_crypto.feature deleted file mode 100644 index a7d433a9da7..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/s3_crypto.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@s3crypto @client -Feature: S3 Integration Crypto Tests - - Scenario: Get all plaintext fixtures for symmetric masterkey aes cbc - When I get all fixtures for "aes_gcm" from "aws-s3-shared-tests" - Then I decrypt each fixture against "Java" "version_2" - And I compare the decrypted ciphertext to the plaintext - - Scenario: Uploading Go's SDK fixtures - When I get all fixtures for "aes_gcm" from "aws-s3-shared-tests" - Then I encrypt each fixture with "kms" "AWS_SDK_TEST_ALIAS" "us-west-2" and "aes_gcm" - And upload "Go" data with folder "version_2" - - Scenario: Get all plaintext fixtures for symmetric masterkey aes gcm - When I get all fixtures for "aes_gcm" from "aws-s3-shared-tests" - Then I decrypt each fixture against "Go" "version_2" - And I compare the decrypted ciphertext to the plaintext diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/stepdef.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/stepdef.go deleted file mode 100644 index b558d8a715e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3crypto/stepdef.go +++ /dev/null @@ -1,192 +0,0 @@ -// +build integration - -// Package s3crypto contains shared step definitions that are used across integration tests -package s3crypto - -import ( - "bytes" - "encoding/base64" - "errors" - "io/ioutil" - "strings" - - "github.com/gucumber/gucumber" - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/kms" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3crypto" -) - -func init() { - gucumber.When(`^I get all fixtures for "(.+?)" from "(.+?)"$`, - func(cekAlg, bucket string) { - prefix := "plaintext_test_case_" - baseFolder := "crypto_tests/" + cekAlg - s3Client := gucumber.World["client"].(*s3.S3) - - out, err := s3Client.ListObjects(&s3.ListObjectsInput{ - Bucket: aws.String(bucket), - Prefix: aws.String(baseFolder + "/" + prefix), - }) - assert.NoError(gucumber.T, err) - - plaintexts := make(map[string][]byte) - for _, obj := range out.Contents { - plaintextKey := obj.Key - ptObj, err := s3Client.GetObject(&s3.GetObjectInput{ - Bucket: aws.String(bucket), - Key: plaintextKey, - }) - assert.NoError(gucumber.T, err) - caseKey := strings.TrimPrefix(*plaintextKey, baseFolder+"/"+prefix) - plaintext, err := ioutil.ReadAll(ptObj.Body) - assert.NoError(gucumber.T, err) - - plaintexts[caseKey] = plaintext - } - gucumber.World["baseFolder"] = baseFolder - gucumber.World["bucket"] = bucket - gucumber.World["plaintexts"] = plaintexts - }) - - gucumber.Then(`^I decrypt each fixture against "(.+?)" "(.+?)"$`, func(lang, version string) { - plaintexts := gucumber.World["plaintexts"].(map[string][]byte) - baseFolder := gucumber.World["baseFolder"].(string) - bucket := gucumber.World["bucket"].(string) - prefix := "ciphertext_test_case_" - s3Client := gucumber.World["client"].(*s3.S3) - s3CryptoClient := gucumber.World["decryptionClient"].(*s3crypto.DecryptionClient) - language := "language_" + lang - - ciphertexts := make(map[string][]byte) - for caseKey := range plaintexts { - cipherKey := baseFolder + "/" + version + "/" + language + "/" + prefix + caseKey - - // To get metadata for encryption key - ctObj, err := s3Client.GetObject(&s3.GetObjectInput{ - Bucket: aws.String(bucket), - Key: &cipherKey, - }) - if err != nil { - continue - } - - // We don't support wrap, so skip it - if *ctObj.Metadata["X-Amz-Wrap-Alg"] != "kms" { - continue - } - //masterkeyB64 := ctObj.Metadata["Masterkey"] - //masterkey, err := base64.StdEncoding.DecodeString(*masterkeyB64) - //assert.NoError(T, err) - - //s3CryptoClient.Config.MasterKey = masterkey - ctObj, err = s3CryptoClient.GetObject(&s3.GetObjectInput{ - Bucket: aws.String(bucket), - Key: &cipherKey, - }, - ) - assert.NoError(gucumber.T, err) - - ciphertext, err := ioutil.ReadAll(ctObj.Body) - assert.NoError(gucumber.T, err) - ciphertexts[caseKey] = ciphertext - } - gucumber.World["ciphertexts"] = ciphertexts - }) - - gucumber.And(`^I compare the decrypted ciphertext to the plaintext$`, func() { - plaintexts := gucumber.World["plaintexts"].(map[string][]byte) - ciphertexts := gucumber.World["ciphertexts"].(map[string][]byte) - for caseKey, ciphertext := range ciphertexts { - assert.Equal(gucumber.T, len(plaintexts[caseKey]), len(ciphertext)) - assert.True(gucumber.T, bytes.Equal(plaintexts[caseKey], ciphertext)) - } - }) - - gucumber.Then(`^I encrypt each fixture with "(.+?)" "(.+?)" "(.+?)" and "(.+?)"$`, func(kek, v1, v2, cek string) { - var handler s3crypto.CipherDataGenerator - var builder s3crypto.ContentCipherBuilder - switch kek { - case "kms": - arn, err := getAliasInformation(v1, v2) - assert.Nil(gucumber.T, err) - - b64Arn := base64.StdEncoding.EncodeToString([]byte(arn)) - assert.Nil(gucumber.T, err) - gucumber.World["Masterkey"] = b64Arn - - handler = s3crypto.NewKMSKeyGenerator(kms.New(session.New(&aws.Config{ - Region: &v2, - })), arn) - assert.Nil(gucumber.T, err) - default: - gucumber.T.Skip() - } - - switch cek { - case "aes_gcm": - builder = s3crypto.AESGCMContentCipherBuilder(handler) - default: - gucumber.T.Skip() - } - - sess := session.New(&aws.Config{ - Region: aws.String("us-west-2"), - }) - c := s3crypto.NewEncryptionClient(sess, builder, func(c *s3crypto.EncryptionClient) { - }) - gucumber.World["encryptionClient"] = c - gucumber.World["cek"] = cek - }) - - gucumber.And(`^upload "(.+?)" data with folder "(.+?)"$`, func(language, folder string) { - c := gucumber.World["encryptionClient"].(*s3crypto.EncryptionClient) - cek := gucumber.World["cek"].(string) - bucket := gucumber.World["bucket"].(string) - plaintexts := gucumber.World["plaintexts"].(map[string][]byte) - key := gucumber.World["Masterkey"].(string) - for caseKey, plaintext := range plaintexts { - input := &s3.PutObjectInput{ - Bucket: &bucket, - Key: aws.String("crypto_tests/" + cek + "/" + folder + "/language_" + language + "/ciphertext_test_case_" + caseKey), - Body: bytes.NewReader(plaintext), - Metadata: map[string]*string{ - "Masterkey": &key, - }, - } - - _, err := c.PutObject(input) - assert.Nil(gucumber.T, err) - } - }) -} - -func getAliasInformation(alias, region string) (string, error) { - arn := "" - svc := kms.New(session.New(&aws.Config{ - Region: ®ion, - })) - - truncated := true - var marker *string - for truncated { - out, err := svc.ListAliases(&kms.ListAliasesInput{ - Marker: marker, - }) - if err != nil { - return arn, err - } - for _, aliasEntry := range out.Aliases { - if *aliasEntry.AliasName == "alias/"+alias { - return *aliasEntry.AliasArn, nil - } - } - truncated = *out.Truncated - marker = out.NextMarker - } - - return "", errors.New("The alias " + alias + " does not exist in your account. Please add the proper alias to a key") -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/integration_test.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/integration_test.go deleted file mode 100644 index 3a0dd600e1f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/integration_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// +build integration - -// Package s3manager provides -package s3manager - -import ( - "bytes" - "crypto/md5" - "fmt" - "io" - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/awstesting/integration" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" -) - -var integBuf12MB = make([]byte, 1024*1024*12) -var integMD512MB = fmt.Sprintf("%x", md5.Sum(integBuf12MB)) -var bucketName *string - -func TestMain(m *testing.M) { - setup() - defer teardown() // only called if we panic - result := m.Run() - teardown() - os.Exit(result) -} - -func setup() { - // Create a bucket for testing - svc := s3.New(integration.Session) - bucketName = aws.String( - fmt.Sprintf("aws-sdk-go-integration-%d-%s", time.Now().Unix(), integration.UniqueID())) - - for i := 0; i < 10; i++ { - _, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName}) - if err == nil { - break - } - } - - for { - _, err := svc.HeadBucket(&s3.HeadBucketInput{Bucket: bucketName}) - if err == nil { - break - } - time.Sleep(1 * time.Second) - } -} - -// Delete the bucket -func teardown() { - svc := s3.New(integration.Session) - - objs, _ := svc.ListObjects(&s3.ListObjectsInput{Bucket: bucketName}) - for _, o := range objs.Contents { - svc.DeleteObject(&s3.DeleteObjectInput{Bucket: bucketName, Key: o.Key}) - } - - uploads, _ := svc.ListMultipartUploads(&s3.ListMultipartUploadsInput{Bucket: bucketName}) - for _, u := range uploads.Uploads { - svc.AbortMultipartUpload(&s3.AbortMultipartUploadInput{ - Bucket: bucketName, - Key: u.Key, - UploadId: u.UploadId, - }) - } - - svc.DeleteBucket(&s3.DeleteBucketInput{Bucket: bucketName}) -} - -type dlwriter struct { - buf []byte -} - -func newDLWriter(size int) *dlwriter { - return &dlwriter{buf: make([]byte, size)} -} - -func (d dlwriter) WriteAt(p []byte, pos int64) (n int, err error) { - if pos > int64(len(d.buf)) { - return 0, io.EOF - } - - written := 0 - for i, b := range p { - if i >= len(d.buf) { - break - } - d.buf[pos+int64(i)] = b - written++ - } - return written, nil -} - -func validate(t *testing.T, key string, md5value string) { - mgr := s3manager.NewDownloader(integration.Session) - params := &s3.GetObjectInput{Bucket: bucketName, Key: &key} - - w := newDLWriter(1024 * 1024 * 20) - n, err := mgr.Download(w, params) - assert.NoError(t, err) - assert.Equal(t, md5value, fmt.Sprintf("%x", md5.Sum(w.buf[0:n]))) -} - -func TestUploadConcurrently(t *testing.T) { - key := "12mb-1" - mgr := s3manager.NewUploader(integration.Session) - out, err := mgr.Upload(&s3manager.UploadInput{ - Bucket: bucketName, - Key: &key, - Body: bytes.NewReader(integBuf12MB), - }) - - assert.NoError(t, err) - assert.NotEqual(t, "", out.UploadID) - assert.Regexp(t, `^https?://.+/`+key+`$`, out.Location) - - validate(t, key, integMD512MB) -} - -func TestUploadFailCleanup(t *testing.T) { - svc := s3.New(integration.Session) - - // Break checksum on 2nd part so it fails - part := 0 - svc.Handlers.Build.PushBack(func(r *request.Request) { - if r.Operation.Name == "UploadPart" { - if part == 1 { - r.HTTPRequest.Header.Set("X-Amz-Content-Sha256", "000") - } - part++ - } - }) - - key := "12mb-leave" - mgr := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) { - u.LeavePartsOnError = false - }) - _, err := mgr.Upload(&s3manager.UploadInput{ - Bucket: bucketName, - Key: &key, - Body: bytes.NewReader(integBuf12MB), - }) - assert.Error(t, err) - assert.NotContains(t, err.Error(), "MissingRegion") - uploadID := "" - if merr, ok := err.(s3manager.MultiUploadFailure); ok { - uploadID = merr.UploadID() - } - assert.NotEmpty(t, uploadID) - - _, err = svc.ListParts(&s3.ListPartsInput{ - Bucket: bucketName, Key: &key, UploadId: &uploadID}) - assert.Error(t, err) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/stub.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/stub.go deleted file mode 100644 index 9434ae97095..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/s3manager/stub.go +++ /dev/null @@ -1 +0,0 @@ -package s3manager diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/stub.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/stub.go deleted file mode 100644 index 3ed7f97237d..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/customizations/s3/stub.go +++ /dev/null @@ -1 +0,0 @@ -package s3 diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/integration.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/integration.go deleted file mode 100644 index 88bcf1636a7..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/integration.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build integration - -// Package integration performs initialization and validation for integration -// tests. -package integration - -import ( - "crypto/rand" - "fmt" - "io" - "os" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" -) - -// Session is a shared session for all integration tests to use. -var Session = session.Must(session.NewSession()) - -func init() { - logLevel := Session.Config.LogLevel - if os.Getenv("DEBUG") != "" { - logLevel = aws.LogLevel(aws.LogDebug) - } - if os.Getenv("DEBUG_SIGNING") != "" { - logLevel = aws.LogLevel(aws.LogDebugWithSigning) - } - if os.Getenv("DEBUG_BODY") != "" { - logLevel = aws.LogLevel(aws.LogDebugWithSigning | aws.LogDebugWithHTTPBody) - } - Session.Config.LogLevel = logLevel - - if aws.StringValue(Session.Config.Region) == "" { - panic("AWS_REGION must be configured to run integration tests") - } -} - -// UniqueID returns a unique UUID-like identifier for use in generating -// resources for integration tests. -func UniqueID() string { - uuid := make([]byte, 16) - io.ReadFull(rand.Reader, uuid) - return fmt.Sprintf("%x", uuid) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/acm.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/acm.feature deleted file mode 100644 index dc28b553381..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/acm.feature +++ /dev/null @@ -1,14 +0,0 @@ -#language en -@acm @client -Feature: AWS Certificate Manager - - Scenario: Making a request - When I call the "ListCertificates" API - Then the request should be successful - - Scenario: Handling errors - When I attempt to call the "GetCertificate" API with: - | CertificateArn | arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012 | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message not be empty - diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/client.go deleted file mode 100644 index fdb6438273a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/acm/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package acm provides gucumber integration tests support. -package acm - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/acm" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@acm", func() { - gucumber.World["client"] = acm.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/apigateway.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/apigateway.feature deleted file mode 100644 index 4286b81304a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/apigateway.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@apigateway @client -Feature: Amazon API Gateway - - Scenario: Making a request - When I call the "GetAccountRequest" API - Then the request should be successful - - Scenario: Handing errors - When I attempt to call the "GetRestApi" API with: - | RestApiId | api123 | - Then I expect the response error code to be "NotFoundException" - And I expect the response error message to include: - """ - Invalid REST API identifier specified - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/client.go deleted file mode 100644 index 10ee2de8777..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/apigateway/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package apigateway provides gucumber integration tests support. -package apigateway - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@apigateway", func() { - gucumber.World["client"] = apigateway.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/applicationdiscoveryservice.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/applicationdiscoveryservice.feature deleted file mode 100644 index 02ae2874e7f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/applicationdiscoveryservice.feature +++ /dev/null @@ -1,8 +0,0 @@ -#language en -@applicationdiscoveryservice @client -Feature: AWS Application Discovery Service - - Scenario: Making a request - When I call the "DescribeAgents" API - Then the request should be successful - diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/client.go deleted file mode 100644 index 85a4dab86c9..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/applicationdiscoveryservice/client.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build integration - -//Package applicationdiscoveryservice provides gucumber integration tests support. -package applicationdiscoveryservice - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/applicationdiscoveryservice" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@applicationdiscoveryservice", func() { - gucumber.World["client"] = applicationdiscoveryservice.New( - smoke.Session, &aws.Config{Region: aws.String("us-west-2")}, - ) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/autoscaling.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/autoscaling.feature deleted file mode 100644 index 7c2bdf6cf1b..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/autoscaling.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@autoscaling @client -Feature: Auto Scaling - - Scenario: Making a request - When I call the "DescribeScalingProcessTypes" API - Then the value at "Processes" should be a list - - Scenario: Handing errors - When I attempt to call the "CreateLaunchConfiguration" API with: - | LaunchConfigurationName | | - | ImageId | ami-12345678 | - | InstanceType | m1.small | - Then I expect the response error code to be "InvalidParameter" - And I expect the response error message to include: - """ - LaunchConfigurationName - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/client.go deleted file mode 100644 index 55c68d1cc9b..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/autoscaling/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package autoscaling provides gucumber integration tests support. -package autoscaling - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@autoscaling", func() { - gucumber.World["client"] = autoscaling.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/client.go deleted file mode 100644 index 079fde7881f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudformation provides gucumber integration tests support. -package cloudformation - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudformation" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudformation", func() { - gucumber.World["client"] = cloudformation.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/cloudformation.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/cloudformation.feature deleted file mode 100644 index 3eafaf6082e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudformation/cloudformation.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@cloudformation @client -Feature: AWS CloudFormation - - Scenario: Making a request - When I call the "ListStacks" API - Then the value at "StackSummaries" should be a list - - Scenario: Handling errors - When I attempt to call the "CreateStack" API with: - | StackName | fakestack | - | TemplateURL | http://s3.amazonaws.com/foo/bar | - Then I expect the response error code to be "ValidationError" - And I expect the response error message to include: - """ - TemplateURL must reference a valid S3 object to which you have access. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/client.go deleted file mode 100644 index c958362e121..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudfront provides gucumber integration tests support. -package cloudfront - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudfront" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudfront", func() { - gucumber.World["client"] = cloudfront.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/cloudfront.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/cloudfront.feature deleted file mode 100644 index bbb2a8d2ac1..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudfront/cloudfront.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@cloudfront @client -Feature: Amazon CloudFront - - Scenario: Making a basic request - When I call the "ListDistributions" API with: - | MaxItems | 1 | - Then the value at "DistributionList.Items" should be a list - - Scenario: Error handling - When I attempt to call the "GetDistribution" API with: - | Id | fake-id | - Then I expect the response error code to be "NoSuchDistribution" - And I expect the response error message to include: - """ - The specified distribution does not exist. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/client.go deleted file mode 100644 index 23f24beb169..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudhsm provides gucumber integration tests support. -package cloudhsm - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudhsm" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudhsm", func() { - gucumber.World["client"] = cloudhsm.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/cloudhsm.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/cloudhsm.feature deleted file mode 100644 index 545ca4efefc..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudhsm/cloudhsm.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@cloudhsm @client -Feature: Amazon CloudHSM - - Scenario: Making a request - When I call the "ListHapgs" API - Then the value at "HapgList" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeHapg" API with: - | HapgArn | bogus-arn | - Then I expect the response error code to be "ValidationException" - And I expect the response error message to include: - """ - Value 'bogus-arn' at 'hapgArn' failed to satisfy constraint - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/client.go deleted file mode 100644 index c346b28217c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudsearch provides gucumber integration tests support. -package cloudsearch - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudsearch" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudsearch", func() { - gucumber.World["client"] = cloudsearch.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/cloudsearch.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/cloudsearch.feature deleted file mode 100644 index 160e916d242..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudsearch/cloudsearch.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@cloudsearch @client -Feature: Amazon CloudSearch - - Scenario: Making a request - When I call the "DescribeDomains" API - Then the response should contain a "DomainStatusList" - - Scenario: Handling errors - When I attempt to call the "DescribeIndexFields" API with: - | DomainName | fakedomain | - Then I expect the response error code to be "ResourceNotFound" - And I expect the response error message to include: - """ - Domain not found: fakedomain - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/client.go deleted file mode 100644 index 97c7bfa47cd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudtrail provides gucumber integration tests support. -package cloudtrail - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudtrail" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudtrail", func() { - gucumber.World["client"] = cloudtrail.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/cloudtrail.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/cloudtrail.feature deleted file mode 100644 index 7b5166a76cd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudtrail/cloudtrail.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@cloudtrail @client -Feature: AWS CloudTrail - - Scenario: Making a request - When I call the "DescribeTrails" API - Then the response should contain a "trailList" - - Scenario: Handling errors - When I attempt to call the "DeleteTrail" API with: - | Name | faketrail | - Then I expect the response error code to be "TrailNotFoundException" - And I expect the response error message to include: - """ - Unknown trail - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/client.go deleted file mode 100644 index ebc339d2ddd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudwatch provides gucumber integration tests support. -package cloudwatch - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudwatch", func() { - gucumber.World["client"] = cloudwatch.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/cloudwatch.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/cloudwatch.feature deleted file mode 100644 index 84307ef2ac0..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatch/cloudwatch.feature +++ /dev/null @@ -1,19 +0,0 @@ -# language: en -@cloudwatch @monitoring @client -Feature: Amazon CloudWatch - - Scenario: Making a request - When I call the "ListMetrics" API with: - | Namespace | AWS/EC2 | - Then the value at "Metrics" should be a list - - Scenario: Handling errors - When I attempt to call the "SetAlarmState" API with: - | AlarmName | abc | - | StateValue | mno | - | StateReason | xyz | - Then I expect the response error code to be "ValidationError" - And I expect the response error message to include: - """ - failed to satisfy constraint - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/client.go deleted file mode 100644 index 75fa2c55651..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cloudwatchlogs provides gucumber integration tests support. -package cloudwatchlogs - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cloudwatchlogs", func() { - gucumber.World["client"] = cloudwatchlogs.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/cloudwatchlogs.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/cloudwatchlogs.feature deleted file mode 100644 index 5711c4e85fe..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cloudwatchlogs/cloudwatchlogs.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@cloudwatchlogs @logs -Feature: Amazon CloudWatch Logs - - Scenario: Making a request - When I call the "DescribeLogGroups" API - Then the value at "logGroups" should be a list - - Scenario: Handling errors - When I attempt to call the "GetLogEvents" API with: - | logGroupName | fakegroup | - | logStreamName | fakestream | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - The specified log group does not exist. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/client.go deleted file mode 100644 index 2f9da3419ab..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package codecommit provides gucumber integration tests support. -package codecommit - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/codecommit" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@codecommit", func() { - gucumber.World["client"] = codecommit.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/codecommit.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/codecommit.feature deleted file mode 100644 index c5c019055f8..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codecommit/codecommit.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@codecommit @client -Feature: Amazon CodeCommit - - Scenario: Making a request - When I call the "ListRepositories" API - Then the value at "repositories" should be a list - - Scenario: Handling errors - When I attempt to call the "ListBranches" API with: - | repositoryName | fake-repo | - Then I expect the response error code to be "RepositoryDoesNotExistException" - And I expect the response error message to include: - """ - fake-repo does not exist - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/client.go deleted file mode 100644 index 29587b9e61e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package codedeploy provides gucumber integration tests support. -package codedeploy - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/codedeploy" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@codedeploy", func() { - gucumber.World["client"] = codedeploy.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/codedeploy.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/codedeploy.feature deleted file mode 100644 index 45dfd2fa256..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codedeploy/codedeploy.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@codedeploy @client -Feature: Amazon CodeDeploy - - Scenario: Making a request - When I call the "ListApplications" API - Then the value at "applications" should be a list - - Scenario: Handling errors - When I attempt to call the "GetDeployment" API with: - | deploymentId | d-USUAELQEX | - Then I expect the response error code to be "DeploymentDoesNotExistException" - And I expect the response error message to include: - """ - The deployment d-USUAELQEX could not be found - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/client.go deleted file mode 100644 index edc34f61c27..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package codepipeline provides gucumber integration tests support. -package codepipeline - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/codepipeline" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@codepipeline", func() { - gucumber.World["client"] = codepipeline.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/codepipeline.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/codepipeline.feature deleted file mode 100644 index cb962cc89b1..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/codepipeline/codepipeline.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@codepipeline @client -Feature: Amazon CodePipeline - - Scenario: Making a request - When I call the "ListPipelines" API - Then the value at "pipelines" should be a list - - Scenario: Handling errors - When I attempt to call the "GetPipeline" API with: - | name | fake-pipeline | - Then I expect the response error code to be "PipelineNotFoundException" - And I expect the response error message to include: - """ - does not have a pipeline with name 'fake-pipeline' - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/client.go deleted file mode 100644 index 476169f5bb4..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cognitoidentity provides gucumber integration tests support. -package cognitoidentity - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cognitoidentity" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cognitoidentity", func() { - gucumber.World["client"] = cognitoidentity.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/cognitoidentity.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/cognitoidentity.feature deleted file mode 100644 index 12abcc8b8fb..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitoidentity/cognitoidentity.feature +++ /dev/null @@ -1,19 +0,0 @@ -# language: en -@cognitoidentity @client -Feature: Amazon Cognito Idenity - - Scenario: Making a request - When I call the "ListIdentityPools" API with JSON: - """ - {"MaxResults": 10} - """ - Then the value at "IdentityPools" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeIdentityPool" API with: - | IdentityPoolId | us-east-1:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - IdentityPool 'us-east-1:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' not found - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/client.go deleted file mode 100644 index 585e47c64dd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package cognitosync provides gucumber integration tests support. -package cognitosync - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/cognitosync" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@cognitosync", func() { - gucumber.World["client"] = cognitosync.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/cognitosync.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/cognitosync.feature deleted file mode 100644 index 3cdf84ec1b4..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/cognitosync/cognitosync.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@cognitosync @client -Feature: Amazon Cognito Sync - - Scenario: Making a request - When I call the "ListIdentityPoolUsage" API - Then the value at "IdentityPoolUsages" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeIdentityPoolUsage" API with: - | IdentityPoolId | us-east-1:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - IdentityPool 'us-east-1:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' not found - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/client.go deleted file mode 100644 index fed62290a7c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package configservice provides gucumber integration tests support. -package configservice - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/configservice" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@configservice", func() { - gucumber.World["client"] = configservice.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/configservice.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/configservice.feature deleted file mode 100644 index ccc3af6f86a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/configservice/configservice.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@configservice @config @client -Feature: AWS Config - - Scenario: Making a request - When I call the "DescribeConfigurationRecorders" API - Then the value at "ConfigurationRecorders" should be a list - - Scenario: Handling errors - When I attempt to call the "GetResourceConfigHistory" API with: - | resourceType | fake-type | - | resourceId | fake-id | - Then I expect the response error code to be "ValidationException" - And I expect the response error message to include: - """ - failed to satisfy constraint - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/client.go deleted file mode 100644 index 10bb6f1d8e6..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package datapipeline provides gucumber integration tests support. -package datapipeline - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/datapipeline" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@datapipeline", func() { - gucumber.World["client"] = datapipeline.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/datapipeline.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/datapipeline.feature deleted file mode 100644 index db315184abd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/datapipeline/datapipeline.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@datapipeline @client -Feature: AWS Data Pipeline - - Scenario: Making a request - When I call the "ListPipelines" API - Then the response should contain a "pipelineIdList" - - Scenario: Handling errors - When I attempt to call the "GetPipelineDefinition" API with: - | pipelineId | fake-id | - Then I expect the response error code to be "PipelineNotFoundException" - And I expect the response error message to include: - """ - does not exist - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/client.go deleted file mode 100644 index f1bcbf71b1f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/client.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build integration - -//Package devicefarm provides gucumber integration tests support. -package devicefarm - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/devicefarm" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@devicefarm", func() { - // FIXME remove custom region - gucumber.World["client"] = devicefarm.New(smoke.Session, - aws.NewConfig().WithRegion("us-west-2")) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/devicefarm.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/devicefarm.feature deleted file mode 100644 index 1d200a9d798..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/devicefarm/devicefarm.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@devicefarm @client -Feature: AWS Device Farm - - Scenario: Making a request - When I call the "ListDevices" API - Then the value at "devices" should be a list - - Scenario: Handling errors - When I attempt to call the "GetDevice" API with: - | arn | arn:aws:devicefarm:us-west-2::device:000000000000000000000000fake-arn | - Then I expect the response error code to be "NotFoundException" - And I expect the response error message to include: - """ - No device was found for arn - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/client.go deleted file mode 100644 index c86e5d8cf88..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package directconnect provides gucumber integration tests support. -package directconnect - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@directconnect", func() { - gucumber.World["client"] = directconnect.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/directconnect.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/directconnect.feature deleted file mode 100644 index 3efd9c7dcdd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directconnect/directconnect.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@directconnect @client -Feature: AWS Direct Connect - - Scenario: Making a request - When I call the "DescribeConnections" API - Then the value at "connections" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeConnections" API with: - | connectionId | fake-connection | - Then I expect the response error code to be "DirectConnectClientException" - And I expect the response error message to include: - """ - Connection ID fake-connection has an invalid format - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/client.go deleted file mode 100644 index ae2fbbac169..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package directoryservice provides gucumber integration tests support. -package directoryservice - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/directoryservice" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@directoryservice", func() { - gucumber.World["client"] = directoryservice.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/directoryservice.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/directoryservice.feature deleted file mode 100644 index 315839b66fa..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/directoryservice/directoryservice.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@directoryservice @ds @client -Feature: AWS Directory Service - - I want to use AWS Directory Service - - Scenario: Making a request - When I call the "DescribeDirectories" API - Then the value at "DirectoryDescriptions" should be a list - - Scenario: Handling errors - When I attempt to call the "CreateDirectory" API with: - | Name | | - | Password | | - | Size | | - Then I expect the response error code to be "ValidationException" - diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/client.go deleted file mode 100644 index 5e3d3fb5f75..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package dynamodb provides gucumber integration tests support. -package dynamodb - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@dynamodb", func() { - gucumber.World["client"] = dynamodb.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/dynamodb.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/dynamodb.feature deleted file mode 100644 index 1df6b3ccb6f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodb/dynamodb.feature +++ /dev/null @@ -1,19 +0,0 @@ -# language: en -@dynamodb @client -Feature: Amazon DynamoDB - - Scenario: Making a request - When I call the "ListTables" API with JSON: - """ - {"Limit": 1} - """ - Then the value at "TableNames" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeTable" API with: - | TableName | fake-table | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - Requested resource not found: Table: fake-table not found - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/client.go deleted file mode 100644 index 64cedf27414..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package dynamodbstreams provides gucumber integration tests support. -package dynamodbstreams - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/dynamodbstreams" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@dynamodbstreams", func() { - gucumber.World["client"] = dynamodbstreams.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/dynamodbstreams.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/dynamodbstreams.feature deleted file mode 100644 index 6e35e29eb9f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/dynamodbstreams/dynamodbstreams.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@dynamodbstreams @client -Feature: Amazon DynamoDB Streams - - Scenario: Making a request - When I call the "ListStreams" API - Then the value at "Streams" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeStream" API with: - | StreamArn | fake-stream | - Then I expect the response error code to be "InvalidParameter" - And I expect the response error message to include: - """ - StreamArn - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/client.go deleted file mode 100644 index 68201536c2c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package ec2 provides gucumber integration tests support. -package ec2 - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@ec2", func() { - gucumber.World["client"] = ec2.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/ec2.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/ec2.feature deleted file mode 100644 index e238c2cd67e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ec2/ec2.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@ec2 @client -Feature: Amazon Elastic Compute Cloud - - Scenario: Making a request - When I call the "DescribeRegions" API - Then the value at "Regions" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeInstances" API with JSON: - """ - {"InstanceIds": ["i-12345678"]} - """ - Then I expect the response error code to be "InvalidInstanceID.NotFound" - And I expect the response error message to include: - """ - The instance ID 'i-12345678' does not exist - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/client.go deleted file mode 100644 index 0db822495b2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/client.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build integration - -//Package ecs provides gucumber integration tests support. -package ecs - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@ecs", func() { - // FIXME remove custom region - gucumber.World["client"] = ecs.New(smoke.Session, - aws.NewConfig().WithRegion("us-west-2")) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/ecs.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/ecs.feature deleted file mode 100644 index 69421378581..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ecs/ecs.feature +++ /dev/null @@ -1,14 +0,0 @@ -# language: en -@ecs @client -Feature: Amazon ECS - - I want to use Amazon ECS - - Scenario: Making a request - When I call the "ListClusters" API - Then the value at "clusterArns" should be a list - - Scenario: Handling errors - When I attempt to call the "StopTask" API with: - | task | xxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxx | - Then the error code should be "ClusterNotFoundException" diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/client.go deleted file mode 100644 index fba6a32aa8a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/client.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build integration - -//Package efs provides gucumber integration tests support. -package efs - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/efs" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@efs", func() { - // FIXME remove custom region - gucumber.World["client"] = efs.New(smoke.Session, - aws.NewConfig().WithRegion("us-west-2")) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/efs.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/efs.feature deleted file mode 100644 index 113dd350115..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/efs/efs.feature +++ /dev/null @@ -1,14 +0,0 @@ -# language: en -@efs @elasticfilesystem @client -Feature: Amazon Elastic File System - - I want to use Amazon Elastic File System - - Scenario: Making a request - When I call the "DescribeFileSystems" API - Then the value at "FileSystems" should be a list - - Scenario: Handling errors - When I attempt to call the "DeleteFileSystem" API with: - | FileSystemId | fake-id | - Then the error code should be "BadRequest" diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/client.go deleted file mode 100644 index 386237fcf39..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package elasticache provides gucumber integration tests support. -package elasticache - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/elasticache" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@elasticache", func() { - gucumber.World["client"] = elasticache.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/elasticache.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/elasticache.feature deleted file mode 100644 index 48828ca2d3f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticache/elasticache.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@elasticache @client -Feature: ElastiCache - - Scenario: Making a request - When I call the "DescribeEvents" API - Then the value at "Events" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeCacheClusters" API with: - | CacheClusterId | fake_cluster | - Then I expect the response error code to be "InvalidParameterValue" - And I expect the response error message to include: - """ - The parameter CacheClusterIdentifier is not a valid identifier. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/client.go deleted file mode 100644 index 61cb2e1e74f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package elasticbeanstalk provides gucumber integration tests support. -package elasticbeanstalk - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/elasticbeanstalk" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@elasticbeanstalk", func() { - gucumber.World["client"] = elasticbeanstalk.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/elasticbeanstalk.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/elasticbeanstalk.feature deleted file mode 100644 index 35b1ad884ab..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticbeanstalk/elasticbeanstalk.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@elasticbeanstalk @client -Feature: AWS Elastic Beanstalk - - Scenario: Making a request - When I call the "ListAvailableSolutionStacks" API - Then the value at "SolutionStacks" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeEnvironmentResources" API with: - | EnvironmentId | fake_environment | - Then I expect the response error code to be "InvalidParameterValue" - And I expect the response error message to include: - """ - No Environment found for EnvironmentId = 'fake_environment'. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/client.go deleted file mode 100644 index 6682a779c48..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package elasticloadbalancing provides gucumber integration tests support. -package elasticloadbalancing - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/elb" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@elasticloadbalancing", func() { - gucumber.World["client"] = elb.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/elasticloadbalancing.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/elasticloadbalancing.feature deleted file mode 100644 index a8c72090849..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elasticloadbalancing/elasticloadbalancing.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@elasticloadbalancing @client -Feature: Elastic Load Balancing - - Scenario: Making a request - When I call the "DescribeLoadBalancers" API - Then the value at "LoadBalancerDescriptions" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeLoadBalancers" API with JSON: - """ - {"LoadBalancerNames": ["fake_load_balancer"]} - """ - Then I expect the response error code to be "ValidationError" - And I expect the response error message to include: - """ - LoadBalancer name cannot contain characters that are not letters, or digits or the dash. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/client.go deleted file mode 100644 index 7e29b473636..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package elastictranscoder provides gucumber integration tests support. -package elastictranscoder - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/elastictranscoder" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@elastictranscoder", func() { - gucumber.World["client"] = elastictranscoder.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/elastictranscoder.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/elastictranscoder.feature deleted file mode 100644 index 77658e66836..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/elastictranscoder/elastictranscoder.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@elastictranscoder @client -Feature: Amazon Elastic Transcoder - - Scenario: Making a request - When I call the "ListPresets" API - Then the value at "Presets" should be a list - - Scenario: Handling errors - When I attempt to call the "ReadJob" API with: - | Id | fake_job | - Then I expect the response error code to be "ValidationException" - And I expect the response error message to include: - """ - Value 'fake_job' at 'id' failed to satisfy constraint - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/client.go deleted file mode 100644 index 41295c77ac1..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package emr provides gucumber integration tests support. -package emr - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/emr" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@emr", func() { - gucumber.World["client"] = emr.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/emr.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/emr.feature deleted file mode 100644 index 133c17412d2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/emr/emr.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@emr @client @elasticmapreduce -Feature: Amazon EMR - - Scenario: Making a request - When I call the "ListClusters" API - Then the value at "Clusters" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeCluster" API with: - | ClusterId | fake_cluster | - Then I expect the response error code to be "InvalidRequestException" - And I expect the response error message to include: - """ - Cluster id 'fake_cluster' is not valid. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/client.go deleted file mode 100644 index 33e59c4cb03..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package es provides gucumber integration tests support. -package es - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/elasticsearchservice" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@es", func() { - gucumber.World["client"] = elasticsearchservice.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/es.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/es.feature deleted file mode 100644 index 8bd1f1e4967..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/es/es.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@es @elasticsearchservice -Feature: Amazon ElasticsearchService - - Scenario: Making a request - When I call the "ListDomainNames" API - Then the value at "DomainNames" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeElasticsearchDomain" API with: - | DomainName | not-a-domain | - Then the error code should be "ResourceNotFoundException" - And I expect the response error message to include: - """ - Domain not found: not-a-domain - """ \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/client.go deleted file mode 100644 index 26235abd11e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package glacier provides gucumber integration tests support. -package glacier - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/glacier" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@glacier", func() { - gucumber.World["client"] = glacier.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/glacier.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/glacier.feature deleted file mode 100644 index 0e1a113a3b6..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/glacier/glacier.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@glacier @client -Feature: Amazon Glacier - - Scenario: Making a request - When I call the "ListVaults" API - Then the response should contain a "VaultList" - - Scenario: Handling errors - When I attempt to call the "ListVaults" API with: - | accountId | abcmnoxyz | - Then I expect the response error code to be "UnrecognizedClientException" - And I expect the response error message to include: - """ - No account found for the given parameters - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/client.go deleted file mode 100644 index d551c73fb46..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package iam provides gucumber integration tests support. -package iam - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@iam", func() { - gucumber.World["client"] = iam.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/iam.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/iam.feature deleted file mode 100644 index 0da6463ae01..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iam/iam.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@iam @client -Feature: AWS Identity and Access Management - - Scenario: Making a request - When I call the "ListUsers" API - Then the value at "Users" should be a list - - Scenario: Handling errors - When I attempt to call the "GetUser" API with: - | UserName | fake_user | - Then I expect the response error code to be "NoSuchEntity" - And I expect the response error message to include: - """ - The user with name fake_user cannot be found. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/client.go deleted file mode 100644 index 30921c332d3..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/client.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build integration - -//Package iotdataplane provides gucumber integration tests support. -package iotdataplane - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/iot" - "github.com/aws/aws-sdk-go/service/iotdataplane" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@iotdataplane", func() { - svc := iot.New(smoke.Session) - result, err := svc.DescribeEndpoint(&iot.DescribeEndpointInput{}) - if err != nil { - gucumber.World["error"] = err - return - } - - gucumber.World["client"] = iotdataplane.New(smoke.Session, aws.NewConfig(). - WithEndpoint(*result.EndpointAddress)) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/iotdataplane.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/iotdataplane.feature deleted file mode 100644 index a6ced14d7b9..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/iotdataplane/iotdataplane.feature +++ /dev/null @@ -1,12 +0,0 @@ -# language: en -@iotdataplane @client -Feature: AWS IoT Data Plane - - Scenario: Handling errors - When I attempt to call the "GetThingShadow" API with: - | ThingName | "fakeThing" | - Then I expect the response error code to be "InvalidRequestException" - And I expect the response error message to include: - """ - Invalid thing name - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/client.go deleted file mode 100644 index 5081bfe0f37..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package kinesis provides gucumber integration tests support. -package kinesis - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/kinesis" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@kinesis", func() { - gucumber.World["client"] = kinesis.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/kinesis.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/kinesis.feature deleted file mode 100644 index 570505cd275..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kinesis/kinesis.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@kinesis @client -Feature: AWS Kinesis - - Scenario: Making a request - When I call the "ListStreams" API - Then the value at "StreamNames" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeStream" API with: - | StreamName | bogus-stream-name | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - Stream bogus-stream-name under account - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/client.go deleted file mode 100644 index e9498b30048..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package kms provides gucumber integration tests support. -package kms - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/kms" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@kms", func() { - gucumber.World["client"] = kms.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/kms.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/kms.feature deleted file mode 100644 index ee428abb86c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/kms/kms.feature +++ /dev/null @@ -1,13 +0,0 @@ -# language: en -@kms @client -Feature: Amazon Key Management Service - - Scenario: Making a request - When I call the "ListAliases" API - Then the value at "Aliases" should be a list - - Scenario: Handling errors - When I attempt to call the "GetKeyPolicy" API with: - | KeyId | fake-key | - | PolicyName | fakepolicy | - Then I expect the response error code to be "NotFoundException" diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/client.go deleted file mode 100644 index 257bc264bcc..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package lambda provides gucumber integration tests support. -package lambda - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/lambda" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@lambda", func() { - gucumber.World["client"] = lambda.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/lambda.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/lambda.feature deleted file mode 100644 index 6ff9cf4a304..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/lambda/lambda.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@lambda @client -Feature: Amazon Lambda - - Scenario: Making a request - When I call the "ListFunctions" API - Then the value at "Functions" should be a list - - Scenario: Handling errors - When I attempt to call the "Invoke" API with: - | FunctionName | bogus-function | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - Function not found - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/client.go deleted file mode 100644 index a8ba24c9c99..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package machinelearning provides gucumber integration tests support. -package machinelearning - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/machinelearning" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@machinelearning", func() { - gucumber.World["client"] = machinelearning.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/machinelearning.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/machinelearning.feature deleted file mode 100644 index 2d9b0649ada..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/machinelearning/machinelearning.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@machinelearning @client -Feature: Amazon Machine Learning - - I want to use Amazon Machine Learning - - Scenario: Making a request - When I call the "DescribeMLModels" API - Then the value at "Results" should be a list - - Scenario: Error handling - When I attempt to call the "GetBatchPrediction" API with: - | BatchPredictionId | fake-id | - Then the error code should be "ResourceNotFoundException" - And the error message should contain: - """ - No BatchPrediction with id fake-id exists - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/client.go deleted file mode 100644 index 8f3f537a3cf..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package opsworks provides gucumber integration tests support. -package opsworks - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/opsworks" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@opsworks", func() { - gucumber.World["client"] = opsworks.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/opsworks.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/opsworks.feature deleted file mode 100644 index a9cfe52b56f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/opsworks/opsworks.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@opsworks @client -Feature: AWS OpsWorks - - Scenario: Making a request - When I call the "DescribeStacks" API - Then the value at "Stacks" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeLayers" API with: - | StackId | fake_stack | - Then I expect the response error code to be "ResourceNotFoundException" - And I expect the response error message to include: - """ - Unable to find stack with ID fake_stack - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/client.go deleted file mode 100644 index a12c73bf3bb..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package rds provides gucumber integration tests support. -package rds - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@rds", func() { - gucumber.World["client"] = rds.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/rds.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/rds.feature deleted file mode 100644 index 547d76db845..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/rds/rds.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@rds @client -Feature: Amazon RDS - - Scenario: Making a request - When I call the "DescribeDBEngineVersions" API - Then the value at "DBEngineVersions" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeDBInstances" API with: - | DBInstanceIdentifier | fake-id | - Then I expect the response error code to be "DBInstanceNotFound" - And I expect the response error message to include: - """ - DBInstance fake-id not found. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/client.go deleted file mode 100644 index 9e6da95427a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package redshift provides gucumber integration tests support. -package redshift - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@redshift", func() { - gucumber.World["client"] = redshift.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/redshift.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/redshift.feature deleted file mode 100644 index 8cb45b14cda..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/redshift/redshift.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@redshift @client -Feature: Amazon Redshift - - Scenario: Making a request - When I call the "DescribeClusterVersions" API - Then the value at "ClusterVersions" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeClusters" API with: - | ClusterIdentifier | fake-cluster | - Then I expect the response error code to be "ClusterNotFound" - And I expect the response error message to include: - """ - Cluster fake-cluster not found. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/client.go deleted file mode 100644 index a55a14ee224..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package route53 provides gucumber integration tests support. -package route53 - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@route53", func() { - gucumber.World["client"] = route53.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/route53.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/route53.feature deleted file mode 100644 index 51463c5245f..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53/route53.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@route53 @client -Feature: Amazon Route 53 - - Scenario: Making a request - When I call the "ListHostedZones" API - Then the value at "HostedZones" should be a list - - Scenario: Handling errors - When I attempt to call the "GetHostedZone" API with: - | Id | fake-zone | - Then I expect the response error code to be "NoSuchHostedZone" - And I expect the response error message to include: - """ - No hosted zone found with ID: fake-zone - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/client.go deleted file mode 100644 index c47de45f0f1..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package route53domains provides gucumber integration tests support. -package route53domains - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/route53domains" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@route53domains", func() { - gucumber.World["client"] = route53domains.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/route53domains.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/route53domains.feature deleted file mode 100644 index f18dcc4e14c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/route53domains/route53domains.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@route53domains @client -Feature: Amazon Route53 Domains - - Scenario: Making a request - When I call the "ListDomains" API - Then the value at "Domains" should be a list - - Scenario: Handling errors - When I attempt to call the "GetDomainDetail" API with: - | DomainName | fake-domain-name | - Then I expect the response error code to be "InvalidInput" - And I expect the response error message to include: - """ - domain name must contain more than 1 label - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/client.go deleted file mode 100644 index f81947ac68a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package ses provides gucumber integration tests support. -package ses - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/ses" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@ses", func() { - gucumber.World["client"] = ses.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/ses.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/ses.feature deleted file mode 100644 index 6b67fa7f3d9..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ses/ses.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@ses @email @client -Feature: Amazon Simple Email Service - - Scenario: Making a request - When I call the "ListIdentities" API - Then the value at "Identities" should be a list - - Scenario: Handling errors - When I attempt to call the "VerifyEmailIdentity" API with: - | EmailAddress | fake_email | - Then I expect the response error code to be "InvalidParameterValue" - And I expect the response error message to include: - """ - Invalid email address. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/shared.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/shared.go deleted file mode 100644 index dbb1338f9b0..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/shared.go +++ /dev/null @@ -1,230 +0,0 @@ -// +build integration - -// Package smoke contains shared step definitions that are used across integration tests -package smoke - -import ( - "encoding/json" - "fmt" - "os" - "reflect" - "regexp" - "strconv" - "strings" - - "github.com/gucumber/gucumber" - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/awsutil" - "github.com/aws/aws-sdk-go/aws/session" -) - -// Session is a shared session for all integration smoke tests to use. -var Session = session.Must(session.NewSession()) - -func init() { - logLevel := Session.Config.LogLevel - if os.Getenv("DEBUG") != "" { - logLevel = aws.LogLevel(aws.LogDebug) - } - if os.Getenv("DEBUG_SIGNING") != "" { - logLevel = aws.LogLevel(aws.LogDebugWithSigning) - } - if os.Getenv("DEBUG_BODY") != "" { - logLevel = aws.LogLevel(aws.LogDebugWithHTTPBody) - } - Session.Config.LogLevel = logLevel - - gucumber.When(`^I call the "(.+?)" API$`, func(op string) { - call(op, nil, false) - }) - - gucumber.When(`^I call the "(.+?)" API with:$`, func(op string, args [][]string) { - call(op, args, false) - }) - - gucumber.Then(`^the value at "(.+?)" should be a list$`, func(member string) { - vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member) - assert.NotNil(gucumber.T, vals) - }) - - gucumber.Then(`^the response should contain a "(.+?)"$`, func(member string) { - vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member) - assert.NotEmpty(gucumber.T, vals) - }) - - gucumber.When(`^I attempt to call the "(.+?)" API with:$`, func(op string, args [][]string) { - call(op, args, true) - }) - - gucumber.Then(`^I expect the response error code to be "(.+?)"$`, func(code string) { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - if ok { - assert.Equal(gucumber.T, code, err.Code(), "Error: %v", err) - } - }) - - gucumber.And(`^I expect the response error message to include:$`, func(data string) { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - if ok { - assert.Contains(gucumber.T, err.Error(), data) - } - }) - - gucumber.And(`^I expect the response error message to include one of:$`, func(table [][]string) { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - if ok { - found := false - for _, row := range table { - if strings.Contains(err.Error(), row[0]) { - found = true - break - } - } - - assert.True(gucumber.T, found, fmt.Sprintf("no error messages matched: \"%s\"", err.Error())) - } - }) - - gucumber.And(`^I expect the response error message not be empty$`, func() { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - assert.NotEmpty(gucumber.T, err.Message()) - }) - - gucumber.When(`^I call the "(.+?)" API with JSON:$`, func(s1 string, data string) { - callWithJSON(s1, data, false) - }) - - gucumber.When(`^I attempt to call the "(.+?)" API with JSON:$`, func(s1 string, data string) { - callWithJSON(s1, data, true) - }) - - gucumber.Then(`^the error code should be "(.+?)"$`, func(s1 string) { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - assert.Equal(gucumber.T, s1, err.Code()) - }) - - gucumber.And(`^the error message should contain:$`, func(data string) { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - assert.Contains(gucumber.T, err.Error(), data) - }) - - gucumber.Then(`^the request should fail$`, func() { - err, ok := gucumber.World["error"].(awserr.Error) - assert.True(gucumber.T, ok, "no error returned") - assert.Error(gucumber.T, err) - }) - - gucumber.Then(`^the request should be successful$`, func() { - err, ok := gucumber.World["error"].(awserr.Error) - assert.False(gucumber.T, ok, "error returned") - assert.NoError(gucumber.T, err) - }) -} - -// findMethod finds the op operation on the v structure using a case-insensitive -// lookup. Returns nil if no method is found. -func findMethod(v reflect.Value, op string) *reflect.Value { - t := v.Type() - op = strings.ToLower(op) - for i := 0; i < t.NumMethod(); i++ { - name := t.Method(i).Name - if strings.ToLower(name) == op { - m := v.MethodByName(name) - return &m - } - } - return nil -} - -// call calls an operation on gucumber.World["client"] by the name op using the args -// table of arguments to set. -func call(op string, args [][]string, allowError bool) { - v := reflect.ValueOf(gucumber.World["client"]) - if m := findMethod(v, op); m != nil { - t := m.Type() - in := reflect.New(t.In(0).Elem()) - fillArgs(in, args) - - resps := m.Call([]reflect.Value{in}) - gucumber.World["response"] = resps[0].Interface() - gucumber.World["error"] = resps[1].Interface() - - if !allowError { - err, _ := gucumber.World["error"].(error) - assert.NoError(gucumber.T, err) - } - } else { - assert.Fail(gucumber.T, "failed to find operation "+op) - } -} - -// reIsNum is a regular expression matching a numeric input (integer) -var reIsNum = regexp.MustCompile(`^\d+$`) - -// reIsArray is a regular expression matching a list -var reIsArray = regexp.MustCompile(`^\['.*?'\]$`) -var reArrayElem = regexp.MustCompile(`'(.+?)'`) - -// fillArgs fills arguments on the input structure using the args table of -// arguments. -func fillArgs(in reflect.Value, args [][]string) { - if args == nil { - return - } - - for _, row := range args { - path := row[0] - var val interface{} = row[1] - if reIsArray.MatchString(row[1]) { - quotedStrs := reArrayElem.FindAllString(row[1], -1) - strs := make([]*string, len(quotedStrs)) - for i, e := range quotedStrs { - str := e[1 : len(e)-1] - strs[i] = &str - } - val = strs - } else if reIsNum.MatchString(row[1]) { // handle integer values - num, err := strconv.ParseInt(row[1], 10, 64) - if err == nil { - val = num - } - } - awsutil.SetValueAtPath(in.Interface(), path, val) - } -} - -func callWithJSON(op, j string, allowError bool) { - v := reflect.ValueOf(gucumber.World["client"]) - if m := findMethod(v, op); m != nil { - t := m.Type() - in := reflect.New(t.In(0).Elem()) - fillJSON(in, j) - - resps := m.Call([]reflect.Value{in}) - gucumber.World["response"] = resps[0].Interface() - gucumber.World["error"] = resps[1].Interface() - - if !allowError { - err, _ := gucumber.World["error"].(error) - assert.NoError(gucumber.T, err) - } - } else { - assert.Fail(gucumber.T, "failed to find operation "+op) - } -} - -func fillJSON(in reflect.Value, j string) { - d := json.NewDecoder(strings.NewReader(j)) - if err := d.Decode(in.Interface()); err != nil { - panic(err) - } -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/client.go deleted file mode 100644 index e8d9ec269a2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package simpledb provides gucumber integration tests support. -package simpledb - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/simpledb" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@simpledb", func() { - gucumber.World["client"] = simpledb.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/simpledb.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/simpledb.feature deleted file mode 100644 index ddc03d83185..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/simpledb/simpledb.feature +++ /dev/null @@ -1,24 +0,0 @@ -# language: en -@simpledb @sdb -Feature: Amazon SimpleDB - - I want to use Amazon SimpleDB - - Scenario: Making a request - When I call the "CreateDomain" API with: - | DomainName | sample-domain | - Then the request should be successful - And I call the "ListDomains" API - Then the value at "DomainNames" should be a list - And I call the "DeleteDomain" API with: - | DomainName | sample-domain | - Then the request should be successful - - Scenario: Handling errors - When I attempt to call the "CreateDomain" API with: - | DomainName | | - Then I expect the response error code to be "InvalidParameterValue" - And I expect the response error message to include: - """ - DomainName is invalid - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/client.go deleted file mode 100644 index cbf990c0eef..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package sns provides gucumber integration tests support. -package sns - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/sns" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@sns", func() { - gucumber.World["client"] = sns.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/sns.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/sns.feature deleted file mode 100644 index 76f6a16daf2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sns/sns.feature +++ /dev/null @@ -1,14 +0,0 @@ -# language: en -@sns @client -Feature: Amazon Simple Notification Service - - Scenario: Making a request - When I call the "ListTopics" API - Then the value at "Topics" should be a list - - Scenario: Handling errors - When I attempt to call the "Publish" API with: - | Message | hello | - | TopicArn | fake_topic | - Then I expect the response error code to be "InvalidParameter" - And I expect the response error message not be empty diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/client.go deleted file mode 100644 index 884dbbdbd9d..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package sqs provides gucumber integration tests support. -package sqs - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/sqs" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@sqs", func() { - gucumber.World["client"] = sqs.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/sqs.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/sqs.feature deleted file mode 100644 index 1413820c018..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sqs/sqs.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@sqs @client -Feature: Amazon Simple Queue Service - - Scenario: Making a request - When I call the "ListQueues" API - Then the value at "QueueUrls" should be a list - - Scenario: Handling errors - When I attempt to call the "GetQueueUrl" API with: - | QueueName | fake_queue | - Then I expect the response error code to be "AWS.SimpleQueueService.NonExistentQueue" - And I expect the response error message to include: - """ - The specified queue does not exist for this wsdl version. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/client.go deleted file mode 100644 index af5e2aa50c4..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package ssm provides gucumber integration tests support. -package ssm - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/ssm" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@ssm", func() { - gucumber.World["client"] = ssm.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/ssm.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/ssm.feature deleted file mode 100644 index 3e2230ed0b2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/ssm/ssm.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@ssm @client -Feature: Amazon SSM - - Scenario: Making a request - When I call the "ListDocuments" API - Then the value at "DocumentIdentifiers" should be a list - - Scenario: Handling errors - When I attempt to call the "GetDocument" API with: - | Name | 'fake-name' | - Then I expect the response error code to be "ValidationException" - And I expect the response error message to include: - """ - validation error detected - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/client.go deleted file mode 100644 index 44d37317a1b..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package storagegateway provides gucumber integration tests support. -package storagegateway - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/storagegateway" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@storagegateway", func() { - gucumber.World["client"] = storagegateway.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/storagegateway.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/storagegateway.feature deleted file mode 100644 index ef96eed9814..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/storagegateway/storagegateway.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: en -@storagegateway @client -Feature: AWS Storage Gateway - - Scenario: Making a request - When I call the "ListGateways" API - Then the value at "Gateways" should be a list - - Scenario: Handling errors - When I attempt to call the "ListVolumes" API with: - | GatewayARN | fake_gateway | - Then I expect the response error code to be "InvalidParameter" - And I expect the response error message to include: - """ - GatewayARN - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/client.go deleted file mode 100644 index ed61e1b25e2..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package sts provides gucumber integration tests support. -package sts - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/sts" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@sts", func() { - gucumber.World["client"] = sts.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/sts.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/sts.feature deleted file mode 100644 index 9caf1fa020e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/sts/sts.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@sts @client -Feature: AWS STS - - Scenario: Making a request - When I call the "GetSessionToken" API - Then the response should contain a "Credentials" - - Scenario: Handling errors - When I attempt to call the "GetFederationToken" API with: - | Name | temp | - | Policy | | - Then I expect the response error code to be "InvalidParameter" - And I expect the response error message to include: - """ - Policy - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/client.go deleted file mode 100644 index 9322d578835..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package support provides gucumber integration tests support. -package support - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/support" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@support", func() { - gucumber.World["client"] = support.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/support.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/support.feature deleted file mode 100644 index 2f91ff8965a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/support/support.feature +++ /dev/null @@ -1,22 +0,0 @@ -# language: en -@support @client -Feature: AWS Support - - I want to use AWS Support - - Scenario: Making a request - When I call the "DescribeServices" API - Then the value at "services" should be a list - - Scenario: Handling errors - When I attempt to call the "CreateCase" API with: - | subject | subject | - | communicationBody | communication | - | categoryCode | category | - | serviceCode | amazon-dynamodb | - | severityCode | low | - Then I expect the response error code to be "InvalidParameterValueException" - And the error message should contain: - """ - Invalid category code - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/client.go deleted file mode 100644 index 09020a2f30c..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package swf provides gucumber integration tests support. -package swf - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/swf" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@swf", func() { - gucumber.World["client"] = swf.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/swf.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/swf.feature deleted file mode 100644 index 1349c813306..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/swf/swf.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@swf @client -Feature: Amazon Simple Workflow Service - - Scenario: Making a request - When I call the "ListDomains" API with: - | registrationStatus | REGISTERED | - Then the value at "domainInfos" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeDomain" API with: - | name | fake_domain | - Then I expect the response error code to be "UnknownResourceFault" - And I expect the response error message to include: - """ - Unknown domain: fake_domain - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/client.go deleted file mode 100644 index 898f848faf6..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package waf provides gucumber integration tests support. -package waf - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/waf" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@waf", func() { - gucumber.World["client"] = waf.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/waf.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/waf.feature deleted file mode 100644 index bf76fb661a9..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/waf/waf.feature +++ /dev/null @@ -1,20 +0,0 @@ -# language: en -@waf -Feature: AWS WAF - - Scenario: Making a request - When I call the "ListRules" API with JSON: - """ - {"Limit":20} - """ - Then the value at "Rules" should be a list - - Scenario: Handling errors - When I attempt to call the "CreateSqlInjectionMatchSet" API with: - | Name | fake_name | - | ChangeToken | fake_token | - Then I expect the response error code to be "WAFStaleDataException" - And I expect the response error message to include: - """ - The input token is no longer current - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/client.go deleted file mode 100644 index 320fb1adc59..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/client.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build integration - -//Package workspaces provides gucumber integration tests support. -package workspaces - -import ( - "github.com/aws/aws-sdk-go/awstesting/integration/smoke" - "github.com/aws/aws-sdk-go/service/workspaces" - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@workspaces", func() { - gucumber.World["client"] = workspaces.New(smoke.Session) - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/workspaces.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/workspaces.feature deleted file mode 100644 index 09ca8849177..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/integration/smoke/workspaces/workspaces.feature +++ /dev/null @@ -1,18 +0,0 @@ -# language: en -@workspaces @client -Feature: Amazon WorkSpaces - - I want to use Amazon WorkSpaces - - Scenario: Making a request - When I call the "DescribeWorkspaces" API - Then the value at "Workspaces" should be a list - - Scenario: Handling errors - When I attempt to call the "DescribeWorkspaces" API with: - | DirectoryId | fake-id | - Then I expect the response error code to be "ValidationException" - And I expect the response error message to include: - """ - The Directory ID fake-id in the request is invalid. - """ diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/mock/mock.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/mock/mock.go deleted file mode 100644 index 9f180074523..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/mock/mock.go +++ /dev/null @@ -1,43 +0,0 @@ -package mock - -import ( - "net/http" - "net/http/httptest" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/client/metadata" - "github.com/aws/aws-sdk-go/aws/session" -) - -// Session is a mock session which is used to hit the mock server -var Session = session.Must(session.NewSession(&aws.Config{ - DisableSSL: aws.Bool(true), - Endpoint: aws.String(server.URL[7:]), -})) - -// server is the mock server that simply writes a 200 status back to the client -var server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) -})) - -// NewMockClient creates and initializes a client that will connect to the -// mock server -func NewMockClient(cfgs ...*aws.Config) *client.Client { - c := Session.ClientConfig("Mock", cfgs...) - - svc := client.New( - *c.Config, - metadata.ClientInfo{ - ServiceName: "Mock", - SigningRegion: c.SigningRegion, - Endpoint: c.Endpoint, - APIVersion: "2015-12-08", - JSONVersion: "1.1", - TargetPrefix: "MockServer", - }, - c.Handlers, - ) - - return svc -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/benchmarks.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/benchmarks.go deleted file mode 100644 index de136582ec9..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/benchmarks.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build integration - -package performance - -import ( - "errors" - "fmt" - "os" - "reflect" - "runtime" - "strings" - "testing" - - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/awstesting/mock" - "github.com/gucumber/gucumber" -) - -// mapCreateClients allows for the creation of clients -func mapCreateClients() { - clientFns := []func(){} - for _, c := range clients { - clientFns = append(clientFns, func() { c.Call([]reflect.Value{reflect.ValueOf(mock.Session)}) }) - } - - gucumber.World["services"] = clientFns -} - -func buildAnArrayOfClients() { - methods := []reflect.Value{} - params := [][]reflect.Value{} - - for _, c := range clients { - method, param, err := findAndGetMethod(c.Call([]reflect.Value{reflect.ValueOf(mock.Session)})) - if err == nil { - methods = append(methods, method) - params = append(params, param) - } - } - - fns := []func(){} - for i := 0; i < len(methods); i++ { - m := methods[i] - p := params[i] - f := func() { - reqs := m.Call(p) - resp := reqs[0].Interface().(*request.Request).Send() - fmt.Println(resp) - } - fns = append(fns, f) - } - gucumber.World["clientFns"] = fns -} - -// findAndGetMethod will grab the method, params to be passed to the method, and an error. -// The method that is found, is a method that doesn't have any required input -func findAndGetMethod(client interface{}) (reflect.Value, []reflect.Value, error) { - v := reflect.ValueOf(client).Type() - n := v.NumMethod() - -outer: - for i := 0; i < n; i++ { - method := v.Method(i) - if method.Type.NumIn() != 2 || strings.HasSuffix(method.Name, "Request") { - continue - } - param := reflect.New(method.Type.In(1).Elem()) - for j := 0; j < param.Elem().NumField(); j++ { - field := param.Elem().Type().Field(j) - req := field.Tag.Get("required") - - if req == "true" { - continue outer - } - } - - params := []reflect.Value{reflect.ValueOf(client), param} - return method.Func, params, nil - } - - return reflect.Value{}, nil, errors.New("No method found") -} - -// benchmarkTask takes a unique key to write to the logger with the benchmark -// result's data -func benchmarkTask(key string, fns []func(), i1 int) error { - gucumber.World["error"] = nil - memStatStart := &runtime.MemStats{} - runtime.ReadMemStats(memStatStart) - - results := testing.Benchmark(func(b *testing.B) { - for _, f := range fns { - for i := 0; i < i1; i++ { - f() - } - } - }) - - results.N = i1 - memStatEnd := &runtime.MemStats{} - runtime.ReadMemStats(memStatEnd) - l, err := newBenchmarkLogger("stdout") - if err != nil { - return err - } - l.log(key, results) - - toDynamodb := os.Getenv("AWS_TESTING_LOG_RESULTS") == "true" - if toDynamodb { - l, err := newBenchmarkLogger("dynamodb") - if err != nil { - return err - } - l.log(key+"_start_benchmarks", memStatStart) - l.log(key+"_end_benchmarks", memStatEnd) - } - - if memStatStart.Alloc < memStatEnd.Alloc { - return errors.New("Leaked memory") - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/client.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/client.go deleted file mode 100644 index 00c2e8122e4..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/client.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build integration - -//Package performance provides gucumber integration tests support. -package performance - -import ( - "github.com/gucumber/gucumber" -) - -func init() { - gucumber.Before("@performance", func() { - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.feature deleted file mode 100644 index c248329e094..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -@performance @clients -Feature: Client Performance - Background: - Given I have loaded my SDK and its dependencies - And I have a list of services - And I take a snapshot of my resources - - Scenario: Creating and then cleaning up clients doesn't leak resources - When I create and discard 100 clients for each service - Then I should not have leaked any resources - - Scenario: Sending requests doesn't leak resources - When I create a client for each service - And I execute 100 command(s) on each client - And I destroy all the clients - Then I should not have leaked any resources diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.go deleted file mode 100644 index 6baa4444bfd..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/clients.go +++ /dev/null @@ -1,137 +0,0 @@ -// +build integration - -package performance - -import ( - "reflect" - - "github.com/aws/aws-sdk-go/service/acm" - "github.com/aws/aws-sdk-go/service/apigateway" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/aws/aws-sdk-go/service/cloudformation" - "github.com/aws/aws-sdk-go/service/cloudfront" - "github.com/aws/aws-sdk-go/service/cloudhsm" - "github.com/aws/aws-sdk-go/service/cloudsearch" - "github.com/aws/aws-sdk-go/service/cloudsearchdomain" - "github.com/aws/aws-sdk-go/service/cloudtrail" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatchevents" - "github.com/aws/aws-sdk-go/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go/service/codecommit" - "github.com/aws/aws-sdk-go/service/codedeploy" - "github.com/aws/aws-sdk-go/service/codepipeline" - "github.com/aws/aws-sdk-go/service/cognitoidentity" - "github.com/aws/aws-sdk-go/service/cognitosync" - "github.com/aws/aws-sdk-go/service/configservice" - "github.com/aws/aws-sdk-go/service/datapipeline" - "github.com/aws/aws-sdk-go/service/devicefarm" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/aws/aws-sdk-go/service/directoryservice" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/aws/aws-sdk-go/service/dynamodbstreams" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ecr" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/aws/aws-sdk-go/service/efs" - "github.com/aws/aws-sdk-go/service/elasticache" - "github.com/aws/aws-sdk-go/service/elasticbeanstalk" - "github.com/aws/aws-sdk-go/service/elasticsearchservice" - "github.com/aws/aws-sdk-go/service/elastictranscoder" - "github.com/aws/aws-sdk-go/service/elb" - "github.com/aws/aws-sdk-go/service/emr" - "github.com/aws/aws-sdk-go/service/firehose" - "github.com/aws/aws-sdk-go/service/glacier" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/aws/aws-sdk-go/service/inspector" - "github.com/aws/aws-sdk-go/service/iot" - "github.com/aws/aws-sdk-go/service/iotdataplane" - "github.com/aws/aws-sdk-go/service/kinesis" - "github.com/aws/aws-sdk-go/service/kms" - "github.com/aws/aws-sdk-go/service/lambda" - "github.com/aws/aws-sdk-go/service/machinelearning" - "github.com/aws/aws-sdk-go/service/marketplacecommerceanalytics" - "github.com/aws/aws-sdk-go/service/mobileanalytics" - "github.com/aws/aws-sdk-go/service/opsworks" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/aws/aws-sdk-go/service/route53domains" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/ses" - "github.com/aws/aws-sdk-go/service/simpledb" - "github.com/aws/aws-sdk-go/service/sns" - "github.com/aws/aws-sdk-go/service/sqs" - "github.com/aws/aws-sdk-go/service/ssm" - "github.com/aws/aws-sdk-go/service/storagegateway" - "github.com/aws/aws-sdk-go/service/sts" - "github.com/aws/aws-sdk-go/service/support" - "github.com/aws/aws-sdk-go/service/swf" - "github.com/aws/aws-sdk-go/service/waf" - "github.com/aws/aws-sdk-go/service/workspaces" -) - -var clients = []reflect.Value{ - reflect.ValueOf(acm.New), - reflect.ValueOf(apigateway.New), - reflect.ValueOf(autoscaling.New), - reflect.ValueOf(cloudformation.New), - reflect.ValueOf(cloudfront.New), - reflect.ValueOf(cloudhsm.New), - reflect.ValueOf(cloudsearch.New), - reflect.ValueOf(cloudsearchdomain.New), - reflect.ValueOf(cloudtrail.New), - reflect.ValueOf(cloudwatch.New), - reflect.ValueOf(cloudwatchevents.New), - reflect.ValueOf(cloudwatchlogs.New), - reflect.ValueOf(codecommit.New), - reflect.ValueOf(codedeploy.New), - reflect.ValueOf(codepipeline.New), - reflect.ValueOf(cognitoidentity.New), - reflect.ValueOf(cognitosync.New), - reflect.ValueOf(configservice.New), - reflect.ValueOf(datapipeline.New), - reflect.ValueOf(devicefarm.New), - reflect.ValueOf(directconnect.New), - reflect.ValueOf(directoryservice.New), - reflect.ValueOf(dynamodb.New), - reflect.ValueOf(dynamodbstreams.New), - reflect.ValueOf(ec2.New), - reflect.ValueOf(ecr.New), - reflect.ValueOf(ecs.New), - reflect.ValueOf(efs.New), - reflect.ValueOf(elasticache.New), - reflect.ValueOf(elasticbeanstalk.New), - reflect.ValueOf(elasticsearchservice.New), - reflect.ValueOf(elastictranscoder.New), - reflect.ValueOf(elb.New), - reflect.ValueOf(emr.New), - reflect.ValueOf(firehose.New), - reflect.ValueOf(glacier.New), - reflect.ValueOf(iam.New), - reflect.ValueOf(inspector.New), - reflect.ValueOf(iot.New), - reflect.ValueOf(iotdataplane.New), - reflect.ValueOf(kinesis.New), - reflect.ValueOf(kms.New), - reflect.ValueOf(lambda.New), - reflect.ValueOf(machinelearning.New), - reflect.ValueOf(marketplacecommerceanalytics.New), - reflect.ValueOf(mobileanalytics.New), - reflect.ValueOf(opsworks.New), - reflect.ValueOf(rds.New), - reflect.ValueOf(redshift.New), - reflect.ValueOf(route53.New), - reflect.ValueOf(route53domains.New), - reflect.ValueOf(s3.New), - reflect.ValueOf(ses.New), - reflect.ValueOf(simpledb.New), - reflect.ValueOf(sns.New), - reflect.ValueOf(sqs.New), - reflect.ValueOf(ssm.New), - reflect.ValueOf(storagegateway.New), - reflect.ValueOf(sts.New), - reflect.ValueOf(support.New), - reflect.ValueOf(swf.New), - reflect.ValueOf(waf.New), - reflect.ValueOf(workspaces.New), -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/init.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/init.go deleted file mode 100644 index 81596d1c882..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/init.go +++ /dev/null @@ -1,93 +0,0 @@ -// +build integration - -package performance - -import ( - "bytes" - "errors" - "fmt" - "runtime" - - "github.com/gucumber/gucumber" - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/awstesting/mock" - "github.com/aws/aws-sdk-go/service/s3" -) - -func init() { - // Go loads all of its dependecies on compile - gucumber.Given(`^I have loaded my SDK and its dependencies$`, func() { - }) - - // Performance - gucumber.When(`^I create and discard (\d+) clients for each service$`, func(i1 int) { - services := gucumber.World["services"].([]func()) - err := benchmarkTask(fmt.Sprintf("%d_create_and_discard_clients", i1), services, i1) - gucumber.World["error"] = err - }) - - gucumber.Then(`^I should not have leaked any resources$`, func() { - runtime.GC() - err, ok := gucumber.World["error"].(awserr.Error) - assert.False(gucumber.T, ok, "error returned") - assert.NoError(gucumber.T, err) - }) - - gucumber.And(`^I have a list of services$`, func() { - mapCreateClients() - }) - - gucumber.And(`^I take a snapshot of my resources$`, func() { - // Can't take a memory snapshot here, because gucumber does some - // allocation between each instruction leading to unreliable numbers - }) - - gucumber.When(`^I create a client for each service$`, func() { - buildAnArrayOfClients() - }) - - gucumber.And("^I execute (\\d+) command\\(s\\) on each client$", func(i1 int) { - clientFns := gucumber.World["clientFns"].([]func()) - err := benchmarkTask(fmt.Sprintf("%d_commands_on_clients", i1), clientFns, i1) - gucumber.World["error"] = err - }) - - gucumber.And(`^I destroy all the clients$`, func() { - delete(gucumber.World, "clientFns") - runtime.GC() - }) - - gucumber.Given(`^I have a (\d+) byte file$`, func(i1 int) { - gucumber.World["file"] = make([]byte, i1) - }) - - gucumber.When(`^I upload the file$`, func() { - svc := s3.New(mock.Session) - memStatStart := &runtime.MemStats{} - runtime.ReadMemStats(memStatStart) - gucumber.World["start"] = memStatStart - - svc.PutObjectRequest(&s3.PutObjectInput{ - Bucket: aws.String("bucketmesilly"), - Key: aws.String("testKey"), - Body: bytes.NewReader(gucumber.World["file"].([]byte)), - }) - }) - - gucumber.And(`then download the file$`, func() { - svc := s3.New(mock.Session) - svc.GetObjectRequest(&s3.GetObjectInput{ - Bucket: aws.String("bucketmesilly"), - Key: aws.String("testKey"), - }) - memStatEnd := &runtime.MemStats{} - runtime.ReadMemStats(memStatEnd) - memStatStart := gucumber.World["start"].(*runtime.MemStats) - if memStatStart.Alloc < memStatEnd.Alloc { - gucumber.World["error"] = errors.New("Leaked memory") - } - }) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/logging.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/logging.go deleted file mode 100644 index 03c885fd9bf..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/logging.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build integration - -// Package performance contains shared step definitions that are used for performance testing -package performance - -import ( - "errors" - "fmt" - "os" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/awstesting/unit" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" -) - -// benchmarkLogger handles all benchmark logging -type benchmarkLogger struct { - outputer -} - -// logger interface that handles any logging to an output -type logger interface { - log(key string, data map[string]interface{}) error -} - -// init initializes the logger and uses dependency injection for the -// outputer -func newBenchmarkLogger(output string) (*benchmarkLogger, error) { - b := &benchmarkLogger{} - switch output { - case "dynamodb": - region := os.Getenv("AWS_TESTING_REGION") - if region == "" { - return b, errors.New("No region specified. Please export AWS_TESTING_REGION") - } - - table := os.Getenv("AWS_TESTING_DB_TABLE") - if table == "" { - return b, errors.New("No table specified. Please export AWS_TESTING_DB_TABLE") - } - b.outputer = newDynamodbOut(table, region) - case "stdout": - b.outputer = stdout{} - default: - return b, errors.New("Unsupported outputer") - } - return b, nil -} - -type record struct { - Key string - Data interface{} -} - -// log calls the output command and building a data structure -// to pass into its output formatter -func (b benchmarkLogger) log(key, data interface{}) error { - formatData := record{ - Key: fmt.Sprintf("%d-%v", time.Now().Unix(), key.(string)), - Data: data, - } - - return b.output(formatData) -} - -// outputer is a simple interface that'll handle output -// to whatever system like dynamodb or stdout -type outputer interface { - output(record) error -} - -// dyanmodbOut handles simple writes to dynamodb -type dynamodbOut struct { - table string // table to write to in dynamodb - region string - db *dynamodb.DynamoDB // the dynamodb -} - -// init initializes dynamodbOut -func newDynamodbOut(table, region string) *dynamodbOut { - out := dynamodbOut{ - table: table, - region: region, - } - - out.db = dynamodb.New( - unit.Session, - &aws.Config{Region: &out.region}, - ) - return &out -} - -// output just writes to dynamodb -func (out dynamodbOut) output(data record) error { - input := &dynamodb.PutItemInput{ - TableName: aws.String(out.table), - } - - item, err := dynamodbattribute.ConvertToMap(data) - if err != nil { - return err - } - - input.Item = item - _, err = out.db.PutItem(input) - return err -} - -// stdout handles writes to stdout -type stdout struct{} - -// output expects key value data to print to stdout -func (out stdout) output(data record) error { - item, err := dynamodbattribute.ConvertToMap(data.Data) - if err != nil { - return err - } - fmt.Println(item) - return nil -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/streaming.feature b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/streaming.feature deleted file mode 100644 index cd24cb7db8a..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/performance/streaming.feature +++ /dev/null @@ -1,26 +0,0 @@ -# language: en -@performance @streaming -Feature: Streaming transfers consume a fixed amount of memory - - Scenario Outline: Streaming uploads are O(1) in memory usage - Given I have a byte file - And I take a snapshot of my resources - When I upload the file - Then I should not have leaked any resources - - Examples: - | bytes | - | 2097152 | - | 209715200 | - - Scenario Outline: Streaming download are O(1) in memory usage - Given I have a byte file - And I take a snapshot of my resources - When I upload the file - And then download the file - Then I should not have leaked any resources - - Examples: - | bytes | - | 2097152 | - | 209715200 | diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.golang-tip b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.golang-tip deleted file mode 100644 index 70148d532db..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.golang-tip +++ /dev/null @@ -1,42 +0,0 @@ -# Based on docker-library's golang 1.6 alpine and wheezy docker files. -# https://github.com/docker-library/golang/blob/master/1.6/alpine/Dockerfile -# https://github.com/docker-library/golang/blob/master/1.6/wheezy/Dockerfile -FROM buildpack-deps:wheezy-scm - -ENV GOLANG_VERSION tip -ENV GOLANG_SRC_REPO_URL https://go.googlesource.com/go - -ENV GOLANG_BOOTSTRAP_URL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz -ENV GOLANG_BOOTSTRAP_SHA256 ce3140662f45356eb78bc16a88fc7cfb29fb00e18d7c632608245b789b2086d2 -ENV GOLANG_BOOTSTRAP_PATH /usr/local/bootstrap - -# gcc for cgo -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc \ - libc6-dev \ - make \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Setup the Bootstrap -RUN mkdir -p "$GOLANG_BOOTSTRAP_PATH" \ - && curl -fsSL "$GOLANG_BOOTSTRAP_URL" -o golang.tar.gz \ - && echo "$GOLANG_BOOTSTRAP_SHA256 golang.tar.gz" | sha256sum -c - \ - && tar -C "$GOLANG_BOOTSTRAP_PATH" -xzf golang.tar.gz \ - && rm golang.tar.gz - -# Get and build Go tip -RUN export GOROOT_BOOTSTRAP=$GOLANG_BOOTSTRAP_PATH/go \ - && git clone "$GOLANG_SRC_REPO_URL" /usr/local/go \ - && cd /usr/local/go/src \ - && ./make.bash \ - && rm -rf "$GOLANG_BOOTSTRAP_PATH" /usr/local/go/pkg/bootstrap - -# Build Go workspace and environment -ENV GOPATH /go -ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH -RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" \ - && chmod -R 777 "$GOPATH" - -WORKDIR $GOPATH diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.4 b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.4 deleted file mode 100644 index e048ed56767..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.4 +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:12.04 -FROM golang:1.4 - -ADD . /go/src/github.com/aws/aws-sdk-go - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "get-deps", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5 b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5 deleted file mode 100644 index 010381c7939..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5 +++ /dev/null @@ -1,9 +0,0 @@ -FROM ubuntu:12.04 -FROM golang:1.5 - -ADD . /go/src/github.com/aws/aws-sdk-go - -ENV GO15VENDOREXPERIMENT="1" - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5-novendorexp b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5-novendorexp deleted file mode 100644 index 9ec9f169d72..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.5-novendorexp +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:12.04 -FROM golang:1.5 - -ADD . /go/src/github.com/aws/aws-sdk-go - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "get-deps", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.6 b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.6 deleted file mode 100644 index 541a8373565..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.6 +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:12.04 -FROM golang:1.6 - -ADD . /go/src/github.com/aws/aws-sdk-go - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.7 b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.7 deleted file mode 100644 index aed4408a85e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.go1.7 +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:12.04 -FROM golang:1.7 - -ADD . /go/src/github.com/aws/aws-sdk-go - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.gotip b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.gotip deleted file mode 100644 index 9758279f99e..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/sandbox/Dockerfile.test.gotip +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:12.04 -FROM aws-golang:tip - -ADD . /go/src/github.com/aws/aws-sdk-go - -WORKDIR /go/src/github.com/aws/aws-sdk-go -CMD ["make", "unit"] diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/unit/unit.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/unit/unit.go deleted file mode 100644 index 1c6e6054684..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/unit/unit.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package unit performs initialization and validation for unit tests -package unit - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" -) - -// Session is a shared session for unit tests to use. -var Session = session.Must(session.NewSession(aws.NewConfig(). - WithCredentials(credentials.NewStaticCredentials("AKID", "SECRET", "SESSION")). - WithRegion("mock-region"))) diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util.go deleted file mode 100644 index 77c296e99d8..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util.go +++ /dev/null @@ -1,67 +0,0 @@ -package awstesting - -import ( - "io" - - "github.com/aws/aws-sdk-go/private/util" -) - -// ZeroReader is a io.Reader which will always write zeros to the byte slice provided. -type ZeroReader struct{} - -// Read fills the provided byte slice with zeros returning the number of bytes written. -func (r *ZeroReader) Read(b []byte) (int, error) { - for i := 0; i < len(b); i++ { - b[i] = 0 - } - return len(b), nil -} - -// ReadCloser is a io.ReadCloser for unit testing. -// Designed to test for leaks and whether a handle has -// been closed -type ReadCloser struct { - Size int - Closed bool - set bool - FillData func(bool, []byte, int, int) -} - -// Read will call FillData and fill it with whatever data needed. -// Decrements the size until zero, then return io.EOF. -func (r *ReadCloser) Read(b []byte) (int, error) { - if r.Closed { - return 0, io.EOF - } - - delta := len(b) - if delta > r.Size { - delta = r.Size - } - r.Size -= delta - - for i := 0; i < delta; i++ { - b[i] = 'a' - } - - if r.FillData != nil { - r.FillData(r.set, b, r.Size, delta) - } - r.set = true - - if r.Size > 0 { - return delta, nil - } - return delta, io.EOF -} - -// Close sets Closed to true and returns no error -func (r *ReadCloser) Close() error { - r.Closed = true - return nil -} - -// SortedKeys returns a sorted slice of keys of a map. -func SortedKeys(m map[string]interface{}) []string { - return util.SortedKeys(m) -} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util_test.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util_test.go deleted file mode 100644 index 4b03db019d6..00000000000 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/awstesting/util_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package awstesting_test - -import ( - "io" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/awstesting" -) - -func TestReadCloserClose(t *testing.T) { - rc := awstesting.ReadCloser{Size: 1} - err := rc.Close() - - assert.Nil(t, err) - assert.True(t, rc.Closed) - assert.Equal(t, rc.Size, 1) -} - -func TestReadCloserRead(t *testing.T) { - rc := awstesting.ReadCloser{Size: 5} - b := make([]byte, 2) - - n, err := rc.Read(b) - - assert.Nil(t, err) - assert.Equal(t, n, 2) - assert.False(t, rc.Closed) - assert.Equal(t, rc.Size, 3) - - err = rc.Close() - assert.Nil(t, err) - n, err = rc.Read(b) - assert.Equal(t, err, io.EOF) - assert.Equal(t, n, 0) -} - -func TestReadCloserReadAll(t *testing.T) { - rc := awstesting.ReadCloser{Size: 5} - b := make([]byte, 5) - - n, err := rc.Read(b) - - assert.Equal(t, err, io.EOF) - assert.Equal(t, n, 5) - assert.False(t, rc.Closed) - assert.Equal(t, rc.Size, 0) -} diff --git a/Godeps/_workspace/src/github.com/franela/goreq/.gitignore b/Godeps/_workspace/src/github.com/franela/goreq/.gitignore deleted file mode 100644 index 131fc16fbbc..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -src diff --git a/Godeps/_workspace/src/github.com/franela/goreq/.travis.yml b/Godeps/_workspace/src/github.com/franela/goreq/.travis.yml deleted file mode 100644 index d5041b4961f..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go -go: - - 1.5.3 - - tip -notifications: - email: - - ionathan@gmail.com - - marcosnils@gmail.com diff --git a/Godeps/_workspace/src/github.com/franela/goreq/LICENSE b/Godeps/_workspace/src/github.com/franela/goreq/LICENSE deleted file mode 100644 index 068dee1edcc..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Jonathan Leibiusky and Marcos Lilljedahl - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/franela/goreq/Makefile b/Godeps/_workspace/src/github.com/franela/goreq/Makefile deleted file mode 100644 index 0f04d6572df..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -test: - go get -v -d -t ./... - go test -v diff --git a/Godeps/_workspace/src/github.com/franela/goreq/README.md b/Godeps/_workspace/src/github.com/franela/goreq/README.md deleted file mode 100644 index b6a0bfd2e1f..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/README.md +++ /dev/null @@ -1,444 +0,0 @@ -[![Build Status](https://img.shields.io/travis/franela/goreq/master.svg)](https://travis-ci.org/franela/goreq) -[![GoDoc](https://godoc.org/github.com/franela/goreq?status.svg)](https://godoc.org/github.com/franela/goreq) - -GoReq -======= - -Simple and sane HTTP request library for Go language. - - - -**Table of Contents** - -- [Why GoReq?](#user-content-why-goreq) -- [How do I install it?](#user-content-how-do-i-install-it) -- [What can I do with it?](#user-content-what-can-i-do-with-it) - - [Making requests with different methods](#user-content-making-requests-with-different-methods) - - [GET](#user-content-get) - - [Tags](#user-content-tags) - - [POST](#user-content-post) - - [Sending payloads in the Body](#user-content-sending-payloads-in-the-body) - - [Specifiying request headers](#user-content-specifiying-request-headers) - - [Sending Cookies](#cookie-support) - - [Setting timeouts](#user-content-setting-timeouts) - - [Using the Response and Error](#user-content-using-the-response-and-error) - - [Receiving JSON](#user-content-receiving-json) - - [Sending/Receiving Compressed Payloads](#user-content-sendingreceiving-compressed-payloads) - - [Using gzip compression:](#user-content-using-gzip-compression) - - [Using deflate compression:](#user-content-using-deflate-compression) - - [Using compressed responses:](#user-content-using-compressed-responses) - - [Proxy](#proxy) - - [Debugging requests](#debug) - - [Getting raw Request & Response](#getting-raw-request--response) - - [TODO:](#user-content-todo) - - - -Why GoReq? -========== - -Go has very nice native libraries that allows you to do lots of cool things. But sometimes those libraries are too low level, which means that to do a simple thing, like an HTTP Request, it takes some time. And if you want to do something as simple as adding a timeout to a request, you will end up writing several lines of code. - -This is why we think GoReq is useful. Because you can do all your HTTP requests in a very simple and comprehensive way, while enabling you to do more advanced stuff by giving you access to the native API. - -How do I install it? -==================== - -```bash -go get github.com/franela/goreq -``` - -What can I do with it? -====================== - -## Making requests with different methods - -#### GET -```go -res, err := goreq.Request{ Uri: "http://www.google.com" }.Do() -``` - -GoReq default method is GET. - -You can also set value to GET method easily - -```go -type Item struct { - Limit int - Skip int - Fields string -} - -item := Item { - Limit: 3, - Skip: 5, - Fields: "Value", -} - -res, err := goreq.Request{ - Uri: "http://localhost:3000/", - QueryString: item, -}.Do() -``` -The sample above will send `http://localhost:3000/?limit=3&skip=5&fields=Value` - -Alternatively the `url` tag can be used in struct fields to customize encoding properties - -```go -type Item struct { - TheLimit int `url:"the_limit"` - TheSkip string `url:"the_skip,omitempty"` - TheFields string `url:"-"` -} - -item := Item { - TheLimit: 3, - TheSkip: "", - TheFields: "Value", -} - -res, err := goreq.Request{ - Uri: "http://localhost:3000/", - QueryString: item, -}.Do() -``` -The sample above will send `http://localhost:3000/?the_limit=3` - - -QueryString also support url.Values - -```go -item := url.Values{} -item.Set("Limit", 3) -item.Add("Field", "somefield") -item.Add("Field", "someotherfield") - -res, err := goreq.Request{ - Uri: "http://localhost:3000/", - QueryString: item, -}.Do() -``` - -The sample above will send `http://localhost:3000/?limit=3&field=somefield&field=someotherfield` - -### Tags - -Struct field `url` tag is mainly used as the request parameter name. -Tags can be comma separated multiple values, 1st value is for naming and rest has special meanings. - -- special tag for 1st value - - `-`: value is ignored if set this - -- special tag for rest 2nd value - - `omitempty`: zero-value is ignored if set this - - `squash`: the fields of embedded struct is used for parameter - -#### Tag Examples - -```go -type Place struct { - Country string `url:"country"` - City string `url:"city"` - ZipCode string `url:"zipcode,omitempty"` -} - -type Person struct { - Place `url:",squash"` - - FirstName string `url:"first_name"` - LastName string `url:"last_name"` - Age string `url:"age,omitempty"` - Password string `url:"-"` -} - -johnbull := Person{ - Place: Place{ // squash the embedded struct value - Country: "UK", - City: "London", - ZipCode: "SW1", - }, - FirstName: "John", - LastName: "Doe", - Age: "35", - Password: "my-secret", // ignored for parameter -} - -goreq.Request{ - Uri: "http://localhost/", - QueryString: johnbull, -}.Do() -// => `http://localhost/?first_name=John&last_name=Doe&age=35&country=UK&city=London&zip_code=SW1` - - -// age and zipcode will be ignored because of `omitempty` -// but firstname isn't. -samurai := Person{ - Place: Place{ // squash the embedded struct value - Country: "Japan", - City: "Tokyo", - }, - LastName: "Yagyu", -} - -goreq.Request{ - Uri: "http://localhost/", - QueryString: samurai, -}.Do() -// => `http://localhost/?first_name=&last_name=yagyu&country=Japan&city=Tokyo` -``` - - -#### POST - -```go -res, err := goreq.Request{ Method: "POST", Uri: "http://www.google.com" }.Do() -``` - -## Sending payloads in the Body - -You can send ```string```, ```Reader``` or ```interface{}``` in the body. The first two will be sent as text. The last one will be marshalled to JSON, if possible. - -```go -type Item struct { - Id int - Name string -} - -item := Item{ Id: 1111, Name: "foobar" } - -res, err := goreq.Request{ - Method: "POST", - Uri: "http://www.google.com", - Body: item, -}.Do() -``` - -## Specifiying request headers - -We think that most of the times the request headers that you use are: ```Host```, ```Content-Type```, ```Accept``` and ```User-Agent```. This is why we decided to make it very easy to set these headers. - -```go -res, err := goreq.Request{ - Uri: "http://www.google.com", - Host: "foobar.com", - Accept: "application/json", - ContentType: "application/json", - UserAgent: "goreq", -}.Do() -``` - -But sometimes you need to set other headers. You can still do it. - -```go -req := goreq.Request{ Uri: "http://www.google.com" } - -req.AddHeader("X-Custom", "somevalue") - -req.Do() -``` - -Alternatively you can use the `WithHeader` function to keep the syntax short - -```go -res, err = goreq.Request{ Uri: "http://www.google.com" }.WithHeader("X-Custom", "somevalue").Do() -``` - -## Cookie support - -Cookies can be either set at the request level by sending a [CookieJar](http://golang.org/pkg/net/http/cookiejar/) in the `CookieJar` request field -or you can use goreq's one-liner WithCookie method as shown below - -```go -res, err := goreq.Request{ - Uri: "http://www.google.com", -}. -WithCookie(&http.Cookie{Name: "c1", Value: "v1"}). -Do() -``` - -## Setting timeouts - -GoReq supports 2 kind of timeouts. A general connection timeout and a request specific one. By default the connection timeout is of 1 second. There is no default for request timeout, which means it will wait forever. - -You can change the connection timeout doing: - -```go -goreq.SetConnectTimeout(100 * time.Millisecond) -``` - -And specify the request timeout doing: - -```go -res, err := goreq.Request{ - Uri: "http://www.google.com", - Timeout: 500 * time.Millisecond, -}.Do() -``` - -## Using the Response and Error - -GoReq will always return 2 values: a ```Response``` and an ```Error```. -If ```Error``` is not ```nil``` it means that an error happened while doing the request and you shouldn't use the ```Response``` in any way. -You can check what happened by getting the error message: - -```go -fmt.Println(err.Error()) -``` -And to make it easy to know if it was a timeout error, you can ask the error or return it: - -```go -if serr, ok := err.(*goreq.Error); ok { - if serr.Timeout() { - ... - } -} -return err -``` - -If you don't get an error, you can safely use the ```Response```. - -```go -res.Uri // return final URL location of the response (fulfilled after redirect was made) -res.StatusCode // return the status code of the response -res.Body // gives you access to the body -res.Body.ToString() // will return the body as a string -res.Header.Get("Content-Type") // gives you access to all the response headers -``` -Remember that you should **always** close `res.Body` if it's not `nil` - -## Receiving JSON - -GoReq will help you to receive and unmarshal JSON. - -```go -type Item struct { - Id int - Name string -} - -var item Item - -res.Body.FromJsonTo(&item) -``` - -## Sending/Receiving Compressed Payloads -GoReq supports gzip, deflate and zlib compression of requests' body and transparent decompression of responses provided they have a correct `Content-Encoding` header. - -#####Using gzip compression: -```go -res, err := goreq.Request{ - Method: "POST", - Uri: "http://www.google.com", - Body: item, - Compression: goreq.Gzip(), -}.Do() -``` -#####Using deflate/zlib compression: -```go -res, err := goreq.Request{ - Method: "POST", - Uri: "http://www.google.com", - Body: item, - Compression: goreq.Deflate(), -}.Do() -``` -#####Using compressed responses: -If servers replies a correct and matching `Content-Encoding` header (gzip requires `Content-Encoding: gzip` and deflate `Content-Encoding: deflate`) goreq transparently decompresses the response so the previous example should always work: -```go -type Item struct { - Id int - Name string -} -res, err := goreq.Request{ - Method: "POST", - Uri: "http://www.google.com", - Body: item, - Compression: goreq.Gzip(), -}.Do() -var item Item -res.Body.FromJsonTo(&item) -``` -If no `Content-Encoding` header is replied by the server GoReq will return the crude response. - -## Proxy -If you need to use a proxy for your requests GoReq supports the standard `http_proxy` env variable as well as manually setting the proxy for each request - -```go -res, err := goreq.Request{ - Method: "GET", - Proxy: "http://myproxy:myproxyport", - Uri: "http://www.google.com", -}.Do() -``` - -### Proxy basic auth is also supported - -```go -res, err := goreq.Request{ - Method: "GET", - Proxy: "http://user:pass@myproxy:myproxyport", - Uri: "http://www.google.com", -}.Do() -``` - -## Debug -If you need to debug your http requests, it can print the http request detail. - -```go -res, err := goreq.Request{ - Method: "GET", - Uri: "http://www.google.com", - Compression: goreq.Gzip(), - ShowDebug: true, -}.Do() -fmt.Println(res, err) -``` - -and it will print the log: -``` -GET / HTTP/1.1 -Host: www.google.com -Accept: -Accept-Encoding: gzip -Content-Encoding: gzip -Content-Type: -``` - - -### Getting raw Request & Response - -To get the Request: - -```go -req := goreq.Request{ - Host: "foobar.com", -} - -//req.Request will return a new instance of an http.Request so you can safely use it for something else -request, _ := req.NewRequest() - -``` - - -To get the Response: - -```go -res, err := goreq.Request{ - Method: "GET", - Uri: "http://www.google.com", - Compression: goreq.Gzip(), - ShowDebug: true, -}.Do() - -// res.Response will contain the original http.Response structure -fmt.Println(res.Response, err) -``` - - - - -TODO: ------ - -We do have a couple of [issues](https://github.com/franela/goreq/issues) pending we'll be addressing soon. But feel free to -contribute and send us PRs (with tests please :smile:). diff --git a/Godeps/_workspace/src/github.com/franela/goreq/goreq.go b/Godeps/_workspace/src/github.com/franela/goreq/goreq.go deleted file mode 100644 index ffa5ea82200..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/goreq.go +++ /dev/null @@ -1,491 +0,0 @@ -package goreq - -import ( - "bufio" - "bytes" - "compress/gzip" - "compress/zlib" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net" - "net/http" - "net/http/httputil" - "net/url" - "reflect" - "strings" - "time" -) - -type itimeout interface { - Timeout() bool -} -type Request struct { - headers []headerTuple - cookies []*http.Cookie - Method string - Uri string - Body interface{} - QueryString interface{} - Timeout time.Duration - ContentType string - Accept string - Host string - UserAgent string - Insecure bool - MaxRedirects int - RedirectHeaders bool - Proxy string - Compression *compression - BasicAuthUsername string - BasicAuthPassword string - CookieJar http.CookieJar - ShowDebug bool - OnBeforeRequest func(goreq *Request, httpreq *http.Request) -} - -type compression struct { - writer func(buffer io.Writer) (io.WriteCloser, error) - reader func(buffer io.Reader) (io.ReadCloser, error) - ContentEncoding string -} - -type Response struct { - *http.Response - Uri string - Body *Body - req *http.Request -} - -func (r Response) CancelRequest() { - cancelRequest(DefaultTransport, r.req) - -} - -func cancelRequest(transport interface{}, r *http.Request) { - if tp, ok := transport.(transportRequestCanceler); ok { - tp.CancelRequest(r) - } -} - -type headerTuple struct { - name string - value string -} - -type Body struct { - reader io.ReadCloser - compressedReader io.ReadCloser -} - -type Error struct { - timeout bool - Err error -} - -type transportRequestCanceler interface { - CancelRequest(*http.Request) -} - -func (e *Error) Timeout() bool { - return e.timeout -} - -func (e *Error) Error() string { - return e.Err.Error() -} - -func (b *Body) Read(p []byte) (int, error) { - if b.compressedReader != nil { - return b.compressedReader.Read(p) - } - return b.reader.Read(p) -} - -func (b *Body) Close() error { - err := b.reader.Close() - if b.compressedReader != nil { - return b.compressedReader.Close() - } - return err -} - -func (b *Body) FromJsonTo(o interface{}) error { - return json.NewDecoder(b).Decode(o) -} - -func (b *Body) ToString() (string, error) { - body, err := ioutil.ReadAll(b) - if err != nil { - return "", err - } - return string(body), nil -} - -func Gzip() *compression { - reader := func(buffer io.Reader) (io.ReadCloser, error) { - return gzip.NewReader(buffer) - } - writer := func(buffer io.Writer) (io.WriteCloser, error) { - return gzip.NewWriter(buffer), nil - } - return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"} -} - -func Deflate() *compression { - reader := func(buffer io.Reader) (io.ReadCloser, error) { - return zlib.NewReader(buffer) - } - writer := func(buffer io.Writer) (io.WriteCloser, error) { - return zlib.NewWriter(buffer), nil - } - return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"} -} - -func Zlib() *compression { - return Deflate() -} - -func paramParse(query interface{}) (string, error) { - switch query.(type) { - case url.Values: - return query.(url.Values).Encode(), nil - case *url.Values: - return query.(*url.Values).Encode(), nil - default: - var v = &url.Values{} - err := paramParseStruct(v, query) - return v.Encode(), err - } -} - -func paramParseStruct(v *url.Values, query interface{}) error { - var ( - s = reflect.ValueOf(query) - t = reflect.TypeOf(query) - ) - for t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface { - s = s.Elem() - t = s.Type() - } - - if t.Kind() != reflect.Struct { - return errors.New("Can not parse QueryString.") - } - - for i := 0; i < t.NumField(); i++ { - var name string - - field := s.Field(i) - typeField := t.Field(i) - - if !field.CanInterface() { - continue - } - - urlTag := typeField.Tag.Get("url") - if urlTag == "-" { - continue - } - - name, opts := parseTag(urlTag) - - var omitEmpty, squash bool - omitEmpty = opts.Contains("omitempty") - squash = opts.Contains("squash") - - if squash { - err := paramParseStruct(v, field.Interface()) - if err != nil { - return err - } - continue - } - - if urlTag == "" { - name = strings.ToLower(typeField.Name) - } - - if val := fmt.Sprintf("%v", field.Interface()); !(omitEmpty && len(val) == 0) { - v.Add(name, val) - } - } - return nil -} - -func prepareRequestBody(b interface{}) (io.Reader, error) { - switch b.(type) { - case string: - // treat is as text - return strings.NewReader(b.(string)), nil - case io.Reader: - // treat is as text - return b.(io.Reader), nil - case []byte: - //treat as byte array - return bytes.NewReader(b.([]byte)), nil - case nil: - return nil, nil - default: - // try to jsonify it - j, err := json.Marshal(b) - if err == nil { - return bytes.NewReader(j), nil - } - return nil, err - } -} - -var DefaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond} -var DefaultTransport http.RoundTripper = &http.Transport{Dial: DefaultDialer.Dial, Proxy: http.ProxyFromEnvironment} -var DefaultClient = &http.Client{Transport: DefaultTransport} - -var proxyTransport http.RoundTripper -var proxyClient *http.Client - -func SetConnectTimeout(duration time.Duration) { - DefaultDialer.Timeout = duration -} - -func (r *Request) AddHeader(name string, value string) { - if r.headers == nil { - r.headers = []headerTuple{} - } - r.headers = append(r.headers, headerTuple{name: name, value: value}) -} - -func (r Request) WithHeader(name string, value string) Request { - r.AddHeader(name, value) - return r -} - -func (r *Request) AddCookie(c *http.Cookie) { - r.cookies = append(r.cookies, c) -} - -func (r Request) WithCookie(c *http.Cookie) Request { - r.AddCookie(c) - return r - -} - -func (r Request) Do() (*Response, error) { - var client = DefaultClient - var transport = DefaultTransport - var resUri string - var redirectFailed bool - - r.Method = valueOrDefault(r.Method, "GET") - - // use a client with a cookie jar if necessary. We create a new client not - // to modify the default one. - if r.CookieJar != nil { - client = &http.Client{ - Transport: transport, - Jar: r.CookieJar, - } - } - - if r.Proxy != "" { - proxyUrl, err := url.Parse(r.Proxy) - if err != nil { - // proxy address is in a wrong format - return nil, &Error{Err: err} - } - - //If jar is specified new client needs to be built - if proxyTransport == nil || client.Jar != nil { - proxyTransport = &http.Transport{Dial: DefaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)} - proxyClient = &http.Client{Transport: proxyTransport, Jar: client.Jar} - } else if proxyTransport, ok := proxyTransport.(*http.Transport); ok { - proxyTransport.Proxy = http.ProxyURL(proxyUrl) - } - transport = proxyTransport - client = proxyClient - } - - client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - - if len(via) > r.MaxRedirects { - redirectFailed = true - return errors.New("Error redirecting. MaxRedirects reached") - } - - resUri = req.URL.String() - - //By default Golang will not redirect request headers - // https://code.google.com/p/go/issues/detail?id=4800&q=request%20header - if r.RedirectHeaders { - for key, val := range via[0].Header { - req.Header[key] = val - } - } - return nil - } - - if transport, ok := transport.(*http.Transport); ok { - if r.Insecure { - if transport.TLSClientConfig != nil { - transport.TLSClientConfig.InsecureSkipVerify = true - } else { - transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } - } else if transport.TLSClientConfig != nil { - // the default TLS client (when transport.TLSClientConfig==nil) is - // already set to verify, so do nothing in that case - transport.TLSClientConfig.InsecureSkipVerify = false - } - } - - req, err := r.NewRequest() - - if err != nil { - // we couldn't parse the URL. - return nil, &Error{Err: err} - } - - timeout := false - if r.Timeout > 0 { - client.Timeout = r.Timeout - } - - if r.ShowDebug { - dump, err := httputil.DumpRequest(req, true) - if err != nil { - log.Println(err) - } - log.Println(string(dump)) - } - - if r.OnBeforeRequest != nil { - r.OnBeforeRequest(&r, req) - } - res, err := client.Do(req) - - if err != nil { - if !timeout { - if t, ok := err.(itimeout); ok { - timeout = t.Timeout() - } - if ue, ok := err.(*url.Error); ok { - if t, ok := ue.Err.(itimeout); ok { - timeout = t.Timeout() - } - } - } - - var response *Response - //If redirect fails we still want to return response data - if redirectFailed { - if res != nil { - response = &Response{res, resUri, &Body{reader: res.Body}, req} - } else { - response = &Response{res, resUri, nil, req} - } - } - - //If redirect fails and we haven't set a redirect count we shouldn't return an error - if redirectFailed && r.MaxRedirects == 0 { - return response, nil - } - - return response, &Error{timeout: timeout, Err: err} - } - - if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) { - compressedReader, err := r.Compression.reader(res.Body) - if err != nil { - return nil, &Error{Err: err} - } - return &Response{res, resUri, &Body{reader: res.Body, compressedReader: compressedReader}, req}, nil - } - - return &Response{res, resUri, &Body{reader: res.Body}, req}, nil -} - -func (r Request) addHeaders(headersMap http.Header) { - if len(r.UserAgent) > 0 { - headersMap.Add("User-Agent", r.UserAgent) - } - if r.Accept != "" { - headersMap.Add("Accept", r.Accept) - } - if r.ContentType != "" { - headersMap.Add("Content-Type", r.ContentType) - } -} - -func (r Request) NewRequest() (*http.Request, error) { - - b, e := prepareRequestBody(r.Body) - if e != nil { - // there was a problem marshaling the body - return nil, &Error{Err: e} - } - - if r.QueryString != nil { - param, e := paramParse(r.QueryString) - if e != nil { - return nil, &Error{Err: e} - } - r.Uri = r.Uri + "?" + param - } - - var bodyReader io.Reader - if b != nil && r.Compression != nil { - buffer := bytes.NewBuffer([]byte{}) - readBuffer := bufio.NewReader(b) - writer, err := r.Compression.writer(buffer) - if err != nil { - return nil, &Error{Err: err} - } - _, e = readBuffer.WriteTo(writer) - writer.Close() - if e != nil { - return nil, &Error{Err: e} - } - bodyReader = buffer - } else { - bodyReader = b - } - - req, err := http.NewRequest(r.Method, r.Uri, bodyReader) - if err != nil { - return nil, err - } - // add headers to the request - req.Host = r.Host - - r.addHeaders(req.Header) - if r.Compression != nil { - req.Header.Add("Content-Encoding", r.Compression.ContentEncoding) - req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding) - } - if r.headers != nil { - for _, header := range r.headers { - req.Header.Add(header.name, header.value) - } - } - - //use basic auth if required - if r.BasicAuthUsername != "" { - req.SetBasicAuth(r.BasicAuthUsername, r.BasicAuthPassword) - } - - for _, c := range r.cookies { - req.AddCookie(c) - } - return req, nil -} - -// Return value if nonempty, def otherwise. -func valueOrDefault(value, def string) string { - if value != "" { - return value - } - return def -} diff --git a/Godeps/_workspace/src/github.com/franela/goreq/tags.go b/Godeps/_workspace/src/github.com/franela/goreq/tags.go deleted file mode 100644 index ffe8789d394..00000000000 --- a/Godeps/_workspace/src/github.com/franela/goreq/tags.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package goreq - -import ( - "strings" - "unicode" -) - -// tagOptions is the string following a comma in a struct field's "json" -// tag, or the empty string. It does not include the leading comma. -type tagOptions string - -// parseTag splits a struct field's json tag into its name and -// comma-separated options. -func parseTag(tag string) (string, tagOptions) { - if idx := strings.Index(tag, ","); idx != -1 { - return tag[:idx], tagOptions(tag[idx+1:]) - } - return tag, tagOptions("") -} - -// Contains reports whether a comma-separated list of options -// contains a particular substr flag. substr must be surrounded by a -// string boundary or commas. -func (o tagOptions) Contains(optionName string) bool { - if len(o) == 0 { - return false - } - s := string(o) - for s != "" { - var next string - i := strings.Index(s, ",") - if i >= 0 { - s, next = s[:i], s[i+1:] - } - if s == optionName { - return true - } - s = next - } - return false -} - -func isValidTag(s string) bool { - if s == "" { - return false - } - for _, c := range s { - switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): - // Backslash and quote chars are reserved, but - // otherwise any punctuation chars are allowed - // in a tag name. - default: - if !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } - } - } - return true -} diff --git a/Godeps/_workspace/src/github.com/go-ini/ini/ini.go b/Godeps/_workspace/src/github.com/go-ini/ini/ini.go deleted file mode 100644 index 1fee789a1d6..00000000000 --- a/Godeps/_workspace/src/github.com/go-ini/ini/ini.go +++ /dev/null @@ -1,1226 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini provides INI file read and write functionality in Go. -package ini - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "os" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -const ( - DEFAULT_SECTION = "DEFAULT" - // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - - _VERSION = "1.6.0" -) - -func Version() string { - return _VERSION -} - -var ( - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - - // Write spaces around "=" to look better. - PrettyFormat = true -) - -func init() { - if runtime.GOOS == "windows" { - LineBreak = "\r\n" - } -} - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} - -// dataSource is a interface that returns file content. -type dataSource interface { - ReadCloser() (io.ReadCloser, error) -} - -type sourceFile struct { - name string -} - -func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { - return os.Open(s.name) -} - -type bytesReadCloser struct { - reader io.Reader -} - -func (rc *bytesReadCloser) Read(p []byte) (n int, err error) { - return rc.reader.Read(p) -} - -func (rc *bytesReadCloser) Close() error { - return nil -} - -type sourceData struct { - data []byte -} - -func (s *sourceData) ReadCloser() (io.ReadCloser, error) { - return &bytesReadCloser{bytes.NewReader(s.data)}, nil -} - -// ____ __. -// | |/ _|____ ___.__. -// | <_/ __ < | | -// | | \ ___/\___ | -// |____|__ \___ > ____| -// \/ \/\/ - -// Key represents a key under a section. -type Key struct { - s *Section - Comment string - name string - value string - isAutoIncr bool -} - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// String returns string representation of value. -func (k *Key) String() string { - val := k.value - if strings.Index(val, "%") == -1 { - return val - } - - for i := 0; i < _DEPTH_VALUES; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := strings.TrimLeft(vr, "%(") - noption = strings.TrimRight(noption, ")s") - - // Search in the same section. - nk, err := k.s.GetKey(noption) - if err != nil { - // Search again in default section. - nk, _ = k.s.f.Section("").GetKey(noption) - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// Validate accepts a validate function which can -// return modifed result as key value. -func (k *Key) Validate(fn func(string) string) string { - return fn(k.String()) -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - return strconv.Atoi(k.String()) -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 10, 64) -} - -// Uint returns uint type valued. -func (k *Key) Uint() (uint, error) { - u, e := strconv.ParseUint(k.String(), 10, 64) - return uint(u), e -} - -// Uint64 returns uint64 type value. -func (k *Key) Uint64() (uint64, error) { - return strconv.ParseUint(k.String(), 10, 64) -} - -// Duration returns time.Duration type value. -func (k *Key) Duration() (time.Duration, error) { - return time.ParseDuration(k.String()) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustUint always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint(defaultVal ...uint) uint { - val, err := k.Uint() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustUint64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint64(defaultVal ...uint64) uint64 { - val, err := k.Uint64() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustDuration always returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration { - val, err := k.Duration() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint(defaultVal uint, candidates []uint) uint { - val := k.MustUint() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 { - val := k.MustUint64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string devide by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - vals := strings.Split(str, delim) - for i := range vals { - vals[i] = strings.TrimSpace(vals[i]) - } - return vals -} - -// Float64s returns list of float64 devide by given delimiter. -func (k *Key) Float64s(delim string) []float64 { - strs := k.Strings(delim) - vals := make([]float64, len(strs)) - for i := range strs { - vals[i], _ = strconv.ParseFloat(strs[i], 64) - } - return vals -} - -// Ints returns list of int devide by given delimiter. -func (k *Key) Ints(delim string) []int { - strs := k.Strings(delim) - vals := make([]int, len(strs)) - for i := range strs { - vals[i], _ = strconv.Atoi(strs[i]) - } - return vals -} - -// Int64s returns list of int64 devide by given delimiter. -func (k *Key) Int64s(delim string) []int64 { - strs := k.Strings(delim) - vals := make([]int64, len(strs)) - for i := range strs { - vals[i], _ = strconv.ParseInt(strs[i], 10, 64) - } - return vals -} - -// Uints returns list of uint devide by given delimiter. -func (k *Key) Uints(delim string) []uint { - strs := k.Strings(delim) - vals := make([]uint, len(strs)) - for i := range strs { - u, _ := strconv.ParseUint(strs[i], 10, 64) - vals[i] = uint(u) - } - return vals -} - -// Uint64s returns list of uint64 devide by given delimiter. -func (k *Key) Uint64s(delim string) []uint64 { - strs := k.Strings(delim) - vals := make([]uint64, len(strs)) - for i := range strs { - vals[i], _ = strconv.ParseUint(strs[i], 10, 64) - } - return vals -} - -// TimesFormat parses with given format and returns list of time.Time devide by given delimiter. -func (k *Key) TimesFormat(format, delim string) []time.Time { - strs := k.Strings(delim) - vals := make([]time.Time, len(strs)) - for i := range strs { - vals[i], _ = time.Parse(format, strs[i]) - } - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time devide by given delimiter. -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - k.value = v -} - -// _________ __ .__ -// / _____/ ____ _____/ |_|__| ____ ____ -// \_____ \_/ __ \_/ ___\ __\ |/ _ \ / \ -// / \ ___/\ \___| | | ( <_> ) | \ -// /_______ /\___ >\___ >__| |__|\____/|___| / -// \/ \/ \/ \/ - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string -} - -func newSection(f *File, name string) *Section { - return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)} -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - s.keys[name].value = val - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = &Key{s, "", name, val, false} - s.keysHash[name] = val - return s.keys[name], nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? - if s.f.BlockMode { - s.f.lock.RLock() - } - key := s.keys[name] - if s.f.BlockMode { - s.f.lock.RUnlock() - } - - if key == nil { - // Check if it is a child-section. - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - return sec.GetKey(name) - } else { - break - } - } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) - } - return key, nil -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := map[string]string{} - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - return - } - } -} - -// ___________.__.__ -// \_ _____/|__| | ____ -// | __) | | | _/ __ \ -// | \ | | |_\ ___/ -// \___ / |__|____/\___ > -// \/ \/ - -// File represents a combination of a or more INI file(s) in memory. -type File struct { - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - // Make sure data is safe in multiple goroutines. - lock sync.RWMutex - - // Allow combination of multiple data sources. - dataSources []dataSource - // Actual data is stored here. - sections map[string]*Section - - // To keep data in order. - sectionList []string - - NameMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - } -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) - } -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -func Load(source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources) - return f, f.Reload() -} - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("error when getting section: section '%s' not exists", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // Note: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - sections := make([]*Section, len(f.sectionList)) - for i := range f.sectionList { - sections[i] = f.Section(f.sectionList[i]) - } - return sections -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func cutComment(str string) string { - i := strings.Index(str, "#") - if i == -1 { - return str - } - return str[:i] -} - -func checkMultipleLines(buf *bufio.Reader, line, val, valQuote string) (string, error) { - isEnd := false - for { - next, err := buf.ReadString('\n') - if err != nil { - if err != io.EOF { - return "", err - } - isEnd = true - } - pos := strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - break - } - val += next - if isEnd { - return "", fmt.Errorf("error parsing line: missing closing key quote from '%s' to '%s'", line, next) - } - } - return val, nil -} - -func checkContinuationLines(buf *bufio.Reader, val string) (string, bool, error) { - isEnd := false - for { - valLen := len(val) - if valLen == 0 || val[valLen-1] != '\\' { - break - } - val = val[:valLen-1] - - next, err := buf.ReadString('\n') - if err != nil { - if err != io.EOF { - return "", isEnd, err - } - isEnd = true - } - - next = strings.TrimSpace(next) - if len(next) == 0 { - break - } - val += next - } - return val, isEnd, nil -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) error { - buf := bufio.NewReader(reader) - - // Handle BOM-UTF8. - // http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding - mask, err := buf.Peek(3) - if err == nil && len(mask) >= 3 && mask[0] == 239 && mask[1] == 187 && mask[2] == 191 { - buf.Read(mask) - } - - count := 1 - comments := "" - isEnd := false - - section, err := f.NewSection(DEFAULT_SECTION) - if err != nil { - return err - } - - for { - line, err := buf.ReadString('\n') - line = strings.TrimSpace(line) - length := len(line) - - // Check error and ignore io.EOF just for a moment. - if err != nil { - if err != io.EOF { - return fmt.Errorf("error reading next line: %v", err) - } - // The last line of file could be an empty line. - if length == 0 { - break - } - isEnd = true - } - - // Skip empty lines. - if length == 0 { - continue - } - - switch { - case line[0] == '#' || line[0] == ';': // Comments. - if len(comments) == 0 { - comments = line - } else { - comments += LineBreak + line - } - continue - case line[0] == '[' && line[length-1] == ']': // New sction. - section, err = f.NewSection(strings.TrimSpace(line[1 : length-1])) - if err != nil { - return err - } - - if len(comments) > 0 { - section.Comment = comments - comments = "" - } - // Reset counter. - count = 1 - continue - } - - // Other possibilities. - var ( - i int - keyQuote string - kname string - valQuote string - val string - ) - - // Key name surrounded by quotes. - if line[0] == '"' { - if length > 6 && line[0:3] == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - if len(keyQuote) > 0 { - qLen := len(keyQuote) - pos := strings.Index(line[qLen:], keyQuote) - if pos == -1 { - return fmt.Errorf("error parsing line: missing closing key quote: %s", line) - } - pos = pos + qLen - i = strings.IndexAny(line[pos:], "=:") - if i < 0 { - return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line) - } else if i == pos { - return fmt.Errorf("error parsing line: key is empty: %s", line) - } - i = i + pos - kname = line[qLen:pos] // Just keep spaces inside quotes. - } else { - i = strings.IndexAny(line, "=:") - if i < 0 { - return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line) - } else if i == 0 { - return fmt.Errorf("error parsing line: key is empty: %s", line) - } - kname = strings.TrimSpace(line[0:i]) - } - - isAutoIncr := false - // Auto increment. - if kname == "-" { - isAutoIncr = true - kname = "#" + fmt.Sprint(count) - count++ - } - - lineRight := strings.TrimSpace(line[i+1:]) - lineRightLength := len(lineRight) - firstChar := "" - if lineRightLength >= 2 { - firstChar = lineRight[0:1] - } - if firstChar == "`" { - valQuote = "`" - } else if firstChar == `"` { - if lineRightLength >= 3 && lineRight[0:3] == `"""` { - valQuote = `"""` - } else { - valQuote = `"` - } - } else if firstChar == `'` { - valQuote = `'` - } - - if len(valQuote) > 0 { - qLen := len(valQuote) - pos := strings.LastIndex(lineRight[qLen:], valQuote) - // For multiple-line value check. - if pos == -1 { - if valQuote == `"` || valQuote == `'` { - return fmt.Errorf("error parsing line: single quote does not allow multiple-line value: %s", line) - } - - val = lineRight[qLen:] + "\n" - val, err = checkMultipleLines(buf, line, val, valQuote) - if err != nil { - return err - } - } else { - val = lineRight[qLen : pos+qLen] - } - } else { - val = strings.TrimSpace(cutComment(lineRight)) - val, isEnd, err = checkContinuationLines(buf, val) - if err != nil { - return err - } - } - - k, err := section.NewKey(kname, val) - if err != nil { - return err - } - k.isAutoIncr = isAutoIncr - if len(comments) > 0 { - k.Comment = comments - comments = "" - } - - if isEnd { - break - } - } - return nil -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -// WriteToIndent writes file content into io.Writer with given value indention. -func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { - equalSign := "=" - if PrettyFormat { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } - if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil { - return 0, err - } - } - - if i > 0 { - if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return 0, err - } - } else { - // Write nothing if default section is empty. - if len(sec.keyList) == 0 { - continue - } - } - - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } - if _, err = buf.WriteString(key.Comment + LineBreak); err != nil { - return 0, err - } - } - - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncr: - kname = "-" - case strings.Contains(kname, "`") || strings.Contains(kname, `"`): - kname = `"""` + kname + `"""` - case strings.Contains(kname, `=`) || strings.Contains(kname, `:`): - kname = "`" + kname + "`" - } - - val := key.value - // In case key value contains "\n", "`" or "\"". - if strings.Contains(val, "\n") || strings.Contains(val, "`") || strings.Contains(val, `"`) || - strings.Contains(val, "#") { - val = `"""` + val + `"""` - } - if _, err = buf.WriteString(kname + equalSign + val + LineBreak); err != nil { - return 0, err - } - } - - // Put a line between sections. - if _, err = buf.WriteString(LineBreak); err != nil { - return 0, err - } - } - - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename afte done. - tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp" - defer os.Remove(tmpPath) - - fw, err := os.Create(tmpPath) - if err != nil { - return err - } - - if _, err = f.WriteToIndent(fw, indent); err != nil { - fw.Close() - return err - } - fw.Close() - - // Remove old file and rename the new one. - os.Remove(filename) - return os.Rename(tmpPath, filename) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/Godeps/_workspace/src/github.com/go-ini/ini/ini_test.go b/Godeps/_workspace/src/github.com/go-ini/ini/ini_test.go deleted file mode 100644 index 82ff36dd236..00000000000 --- a/Godeps/_workspace/src/github.com/go-ini/ini/ini_test.go +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "fmt" - "strings" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Version(t *testing.T) { - Convey("Get version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} - -const _CONF_DATA = ` -; Package name -NAME = ini -; Package version -VERSION = v1 -; Package import path -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -# Information about package author -# Bio can be written in multiple lines. -[author] -NAME = Unknwon # Succeeding comment -E-MAIL = fake@localhost -GITHUB = https://github.com/%(NAME)s -BIO = """Gopher. -Coding addict. -Good man. -""" # Succeeding comment - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -UNUSED_KEY = should be deleted - -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values - -[types] -STRING = str -BOOL = true -BOOL_FALSE = false -FLOAT64 = 1.25 -INT = 10 -TIME = 2015-01-01T20:17:05Z -DURATION = 2h45m -UINT = 3 - -[array] -STRINGS = en, zh, de -FLOAT64S = 1.1, 2.2, 3.3 -INTS = 1, 2, 3 -UINTS = 1, 2, 3 -TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z - -[note] -empty_lines = next line is empty\ - -[advance] -value with quotes = "some value" -value quote2 again = 'some value' -true = """"2+3=5"""" -"1+1=2" = true -"""6+1=7""" = true -"""` + "`" + `5+5` + "`" + `""" = 10 -""""6+6"""" = 12 -` + "`" + `7-2=4` + "`" + ` = false -ADDRESS = ` + "`" + `404 road, -NotFound, State, 50000` + "`" + ` - -two_lines = how about \ - continuation lines? -lots_of_lines = 1 \ - 2 \ - 3 \ - 4 \ -` - -func Test_Load(t *testing.T) { - Convey("Load from data sources", t, func() { - - Convey("Load with empty data", func() { - So(Empty(), ShouldNotBeNil) - }) - - Convey("Load with multiple data sources", func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - }) - }) - - Convey("Bad load process", t, func() { - - Convey("Load from invalid data sources", func() { - _, err := Load(_CONF_DATA) - So(err, ShouldNotBeNil) - - _, err = Load("testdata/404.ini") - So(err, ShouldNotBeNil) - - _, err = Load(1) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(""), 1) - So(err, ShouldNotBeNil) - }) - - Convey("Load with empty section name", func() { - _, err := Load([]byte("[]")) - So(err, ShouldNotBeNil) - }) - - Convey("Load with bad keys", func() { - _, err := Load([]byte(`"""name`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`"""name"""`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`""=1`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`=`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`name`)) - So(err, ShouldNotBeNil) - }) - - Convey("Load with bad values", func() { - _, err := Load([]byte(`name="""Unknwon`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`key = "value`)) - So(err, ShouldNotBeNil) - }) - }) -} - -func Test_Values(t *testing.T) { - Convey("Test getting and setting values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - Convey("Get values in default section", func() { - sec := cfg.Section("") - So(sec, ShouldNotBeNil) - So(sec.Key("NAME").Value(), ShouldEqual, "ini") - So(sec.Key("NAME").String(), ShouldEqual, "ini") - So(sec.Key("NAME").Validate(func(in string) string { - return in - }), ShouldEqual, "ini") - So(sec.Key("NAME").Comment, ShouldEqual, "; Package name") - So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1") - }) - - Convey("Get values in non-default section", func() { - sec := cfg.Section("author") - So(sec, ShouldNotBeNil) - So(sec.Key("NAME").String(), ShouldEqual, "Unknwon") - So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon") - - sec = cfg.Section("package") - So(sec, ShouldNotBeNil) - So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") - }) - - Convey("Get auto-increment key names", func() { - keys := cfg.Section("features").Keys() - for i, k := range keys { - So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1)) - } - }) - - Convey("Get overwrite value", func() { - So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") - }) - - Convey("Get sections", func() { - sections := cfg.Sections() - for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "advance"} { - So(sections[i].Name(), ShouldEqual, name) - } - }) - - Convey("Get parent section value", func() { - So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") - }) - - Convey("Get multiple line value", func() { - So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n") - }) - - Convey("Get values with type", func() { - sec := cfg.Section("types") - v1, err := sec.Key("BOOL").Bool() - So(err, ShouldBeNil) - So(v1, ShouldBeTrue) - - v1, err = sec.Key("BOOL_FALSE").Bool() - So(err, ShouldBeNil) - So(v1, ShouldBeFalse) - - v2, err := sec.Key("FLOAT64").Float64() - So(err, ShouldBeNil) - So(v2, ShouldEqual, 1.25) - - v3, err := sec.Key("INT").Int() - So(err, ShouldBeNil) - So(v3, ShouldEqual, 10) - - v4, err := sec.Key("INT").Int64() - So(err, ShouldBeNil) - So(v4, ShouldEqual, 10) - - v5, err := sec.Key("UINT").Uint() - So(err, ShouldBeNil) - So(v5, ShouldEqual, 3) - - v6, err := sec.Key("UINT").Uint64() - So(err, ShouldBeNil) - So(v6, ShouldEqual, 3) - - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - v7, err := sec.Key("TIME").Time() - So(err, ShouldBeNil) - So(v7.String(), ShouldEqual, t.String()) - - Convey("Must get values with type", func() { - So(sec.Key("STRING").MustString("404"), ShouldEqual, "str") - So(sec.Key("BOOL").MustBool(), ShouldBeTrue) - So(sec.Key("FLOAT64").MustFloat64(), ShouldEqual, 1.25) - So(sec.Key("INT").MustInt(), ShouldEqual, 10) - So(sec.Key("INT").MustInt64(), ShouldEqual, 10) - So(sec.Key("UINT").MustUint(), ShouldEqual, 3) - So(sec.Key("UINT").MustUint64(), ShouldEqual, 3) - So(sec.Key("TIME").MustTime().String(), ShouldEqual, t.String()) - - dur, err := time.ParseDuration("2h45m") - So(err, ShouldBeNil) - So(sec.Key("DURATION").MustDuration().Seconds(), ShouldEqual, dur.Seconds()) - - Convey("Must get values with default value", func() { - So(sec.Key("STRING_404").MustString("404"), ShouldEqual, "404") - So(sec.Key("BOOL_404").MustBool(true), ShouldBeTrue) - So(sec.Key("FLOAT64_404").MustFloat64(2.5), ShouldEqual, 2.5) - So(sec.Key("INT_404").MustInt(15), ShouldEqual, 15) - So(sec.Key("INT_404").MustInt64(15), ShouldEqual, 15) - So(sec.Key("UINT_404").MustUint(6), ShouldEqual, 6) - So(sec.Key("UINT_404").MustUint64(6), ShouldEqual, 6) - - t, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME_404").MustTime(t).String(), ShouldEqual, t.String()) - - So(sec.Key("DURATION_404").MustDuration(dur).Seconds(), ShouldEqual, dur.Seconds()) - }) - }) - }) - - Convey("Get value with candidates", func() { - sec := cfg.Section("types") - So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str") - So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25) - So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("INT").InInt64(0, []int64{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("UINT").InUint(0, []uint{3, 6, 9}), ShouldEqual, 3) - So(sec.Key("UINT").InUint64(0, []uint64{3, 6, 9}), ShouldEqual, 3) - - zt, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z") - So(err, ShouldBeNil) - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME").InTime(zt, []time.Time{t, time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String()) - - Convey("Get value with candidates and default value", func() { - So(sec.Key("STRING_404").In("str", []string{"str", "arr", "types"}), ShouldEqual, "str") - So(sec.Key("FLOAT64_404").InFloat64(1.25, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25) - So(sec.Key("INT_404").InInt(10, []int{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("INT64_404").InInt64(10, []int64{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("UINT_404").InUint(3, []uint{3, 6, 9}), ShouldEqual, 3) - So(sec.Key("UINT_404").InUint64(3, []uint64{3, 6, 9}), ShouldEqual, 3) - So(sec.Key("TIME_404").InTime(t, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String()) - }) - }) - - Convey("Get values in range", func() { - sec := cfg.Section("types") - So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25) - So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10) - So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10) - - minT, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z") - So(err, ShouldBeNil) - midT, err := time.Parse(time.RFC3339, "2013-01-01T01:00:00Z") - So(err, ShouldBeNil) - maxT, err := time.Parse(time.RFC3339, "9999-01-01T01:00:00Z") - So(err, ShouldBeNil) - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME").RangeTime(t, minT, maxT).String(), ShouldEqual, t.String()) - - Convey("Get value in range with default value", func() { - So(sec.Key("FLOAT64").RangeFloat64(5, 0, 1), ShouldEqual, 5) - So(sec.Key("INT").RangeInt(7, 0, 5), ShouldEqual, 7) - So(sec.Key("INT").RangeInt64(7, 0, 5), ShouldEqual, 7) - So(sec.Key("TIME").RangeTime(t, minT, midT).String(), ShouldEqual, t.String()) - }) - }) - - Convey("Get values into slice", func() { - sec := cfg.Section("array") - So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de") - So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0) - - vals1 := sec.Key("FLOAT64S").Float64s(",") - for i, v := range []float64{1.1, 2.2, 3.3} { - So(vals1[i], ShouldEqual, v) - } - - vals2 := sec.Key("INTS").Ints(",") - for i, v := range []int{1, 2, 3} { - So(vals2[i], ShouldEqual, v) - } - - vals3 := sec.Key("INTS").Int64s(",") - for i, v := range []int64{1, 2, 3} { - So(vals3[i], ShouldEqual, v) - } - - vals4 := sec.Key("UINTS").Uints(",") - for i, v := range []uint{1, 2, 3} { - So(vals4[i], ShouldEqual, v) - } - - vals5 := sec.Key("UINTS").Uint64s(",") - for i, v := range []uint64{1, 2, 3} { - So(vals5[i], ShouldEqual, v) - } - - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - vals6 := sec.Key("TIMES").Times(",") - for i, v := range []time.Time{t, t, t} { - So(vals6[i].String(), ShouldEqual, v.String()) - } - }) - - Convey("Get key hash", func() { - cfg.Section("").KeysHash() - }) - - Convey("Set key value", func() { - k := cfg.Section("author").Key("NAME") - k.SetValue("无闻") - So(k.String(), ShouldEqual, "无闻") - }) - - Convey("Get key strings", func() { - So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,BOOL_FALSE,FLOAT64,INT,TIME,DURATION,UINT") - }) - - Convey("Delete a key", func() { - cfg.Section("package.sub").DeleteKey("UNUSED_KEY") - _, err := cfg.Section("package.sub").GetKey("UNUSED_KEY") - So(err, ShouldNotBeNil) - }) - - Convey("Get section strings", func() { - So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,advance") - }) - - Convey("Delete a section", func() { - cfg.DeleteSection("") - So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION) - }) - - Convey("Create new sections", func() { - cfg.NewSections("test", "test2") - _, err := cfg.GetSection("test") - So(err, ShouldBeNil) - _, err = cfg.GetSection("test2") - So(err, ShouldBeNil) - }) - }) - - Convey("Test getting and setting bad values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - Convey("Create new key with empty name", func() { - k, err := cfg.Section("").NewKey("", "") - So(err, ShouldNotBeNil) - So(k, ShouldBeNil) - }) - - Convey("Create new section with empty name", func() { - s, err := cfg.NewSection("") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - }) - - Convey("Create new sections with empty name", func() { - So(cfg.NewSections(""), ShouldNotBeNil) - }) - - Convey("Get section that not exists", func() { - s, err := cfg.GetSection("404") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - - s = cfg.Section("404") - So(s, ShouldNotBeNil) - }) - }) -} - -func Test_File_Append(t *testing.T) { - Convey("Append data sources", t, func() { - cfg, err := Load([]byte("")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(cfg.Append([]byte(""), []byte("")), ShouldBeNil) - - Convey("Append bad data sources", func() { - So(cfg.Append(1), ShouldNotBeNil) - So(cfg.Append([]byte(""), 1), ShouldNotBeNil) - }) - }) -} - -func Test_File_SaveTo(t *testing.T) { - Convey("Save file", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.Section("").Key("NAME").Comment = "Package name" - cfg.Section("author").Comment = `Information about package author -# Bio can be written in multiple lines.` - cfg.Section("advanced").Key("val w/ pound").SetValue("my#password") - So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil) - - cfg.Section("author").Key("NAME").Comment = "This is author name" - So(cfg.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil) - }) -} - -func Benchmark_Key_Value(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} - -func Benchmark_Key_String(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_Value_NonBlock(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - c.BlockMode = false - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} - -func Benchmark_Key_String_NonBlock(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - c.BlockMode = false - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_SetValue(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").SetValue("10") - } -} diff --git a/Godeps/_workspace/src/github.com/go-ini/ini/struct_test.go b/Godeps/_workspace/src/github.com/go-ini/ini/struct_test.go deleted file mode 100644 index d865ad78eb7..00000000000 --- a/Godeps/_workspace/src/github.com/go-ini/ini/struct_test.go +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "strings" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -type testNested struct { - Cities []string `delim:"|"` - Visits []time.Time - Note string - Unused int `ini:"-"` -} - -type testEmbeded struct { - GPA float64 -} - -type testStruct struct { - Name string `ini:"NAME"` - Age int - Male bool - Money float64 - Born time.Time - Time time.Duration `ini:"Duration"` - Others testNested - *testEmbeded `ini:"grade"` - Unused int `ini:"-"` - Unsigned uint -} - -const _CONF_DATA_STRUCT = ` -NAME = Unknwon -Age = 21 -Male = true -Money = 1.25 -Born = 1993-10-07T20:17:05Z -Duration = 2h45m -Unsigned = 3 - -[Others] -Cities = HangZhou|Boston -Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z -Note = Hello world! - -[grade] -GPA = 2.8 - -[foo.bar] -Here = there -When = then -` - -type unsupport struct { - Byte byte -} - -type unsupport2 struct { - Others struct { - Cities byte - } -} - -type unsupport3 struct { - Cities byte -} - -type unsupport4 struct { - *unsupport3 `ini:"Others"` -} - -type defaultValue struct { - Name string - Age int - Male bool - Money float64 - Born time.Time - Cities []string -} - -type fooBar struct { - Here, When string -} - -const _INVALID_DATA_CONF_STRUCT = ` -Name = -Age = age -Male = 123 -Money = money -Born = nil -Cities = -` - -func Test_Struct(t *testing.T) { - Convey("Map to struct", t, func() { - Convey("Map file to struct", func() { - ts := new(testStruct) - So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) - - So(ts.Name, ShouldEqual, "Unknwon") - So(ts.Age, ShouldEqual, 21) - So(ts.Male, ShouldBeTrue) - So(ts.Money, ShouldEqual, 1.25) - So(ts.Unsigned, ShouldEqual, 3) - - t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") - So(err, ShouldBeNil) - So(ts.Born.String(), ShouldEqual, t.String()) - - dur, err := time.ParseDuration("2h45m") - So(err, ShouldBeNil) - So(ts.Time.Seconds(), ShouldEqual, dur.Seconds()) - - So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston") - So(ts.Others.Visits[0].String(), ShouldEqual, t.String()) - So(ts.Others.Note, ShouldEqual, "Hello world!") - So(ts.testEmbeded.GPA, ShouldEqual, 2.8) - }) - - Convey("Map section to struct", func() { - foobar := new(fooBar) - f, err := Load([]byte(_CONF_DATA_STRUCT)) - So(err, ShouldBeNil) - - So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil) - So(foobar.Here, ShouldEqual, "there") - So(foobar.When, ShouldEqual, "then") - }) - - Convey("Map to non-pointer struct", func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(cfg.MapTo(testStruct{}), ShouldNotBeNil) - }) - - Convey("Map to unsupported type", func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.NameMapper = func(raw string) string { - if raw == "Byte" { - return "NAME" - } - return raw - } - So(cfg.MapTo(&unsupport{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil) - }) - - Convey("Map from invalid data source", func() { - So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil) - }) - - Convey("Map to wrong types and gain default values", func() { - cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT)) - So(err, ShouldBeNil) - - t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") - So(err, ShouldBeNil) - dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}} - So(cfg.MapTo(dv), ShouldBeNil) - So(dv.Name, ShouldEqual, "Joe") - So(dv.Age, ShouldEqual, 10) - So(dv.Male, ShouldBeTrue) - So(dv.Money, ShouldEqual, 1.25) - So(dv.Born.String(), ShouldEqual, t.String()) - So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston") - }) - }) - - Convey("Reflect from struct", t, func() { - type Embeded struct { - Dates []time.Time `delim:"|"` - Places []string - None []int - } - type Author struct { - Name string `ini:"NAME"` - Male bool - Age int - GPA float64 - NeverMind string `ini:"-"` - *Embeded `ini:"infos"` - } - a := &Author{"Unknwon", true, 21, 2.8, "", - &Embeded{ - []time.Time{time.Now(), time.Now()}, - []string{"HangZhou", "Boston"}, - []int{}, - }} - cfg := Empty() - So(ReflectFrom(cfg, a), ShouldBeNil) - cfg.SaveTo("testdata/conf_reflect.ini") - - Convey("Reflect from non-point struct", func() { - So(ReflectFrom(cfg, Author{}), ShouldNotBeNil) - }) - }) -} - -type testMapper struct { - PackageName string -} - -func Test_NameGetter(t *testing.T) { - Convey("Test name mappers", t, func() { - So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil) - - cfg, err := Load([]byte("PACKAGE_NAME=ini")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.NameMapper = AllCapsUnderscore - tg := new(testMapper) - So(cfg.MapTo(tg), ShouldBeNil) - So(tg.PackageName, ShouldEqual, "ini") - }) -} diff --git a/Godeps/_workspace/src/github.com/go-ini/ini/testdata/conf.ini b/Godeps/_workspace/src/github.com/go-ini/ini/testdata/conf.ini deleted file mode 100644 index 2ed0ac1d3ac..00000000000 --- a/Godeps/_workspace/src/github.com/go-ini/ini/testdata/conf.ini +++ /dev/null @@ -1,2 +0,0 @@ -[author] -E-MAIL = u@gogs.io \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gopherjs/gopherjs/LICENSE b/Godeps/_workspace/src/github.com/gopherjs/gopherjs/LICENSE deleted file mode 100644 index d496fef1092..00000000000 --- a/Godeps/_workspace/src/github.com/gopherjs/gopherjs/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013 Richard Musiol. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/LICENSE b/Godeps/_workspace/src/github.com/grafana/grafana-cli/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 {yyyy} {name of copyright owner} - - 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. diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/command_line.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/command_line.go deleted file mode 100644 index edbdc03d7c2..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/command_line.go +++ /dev/null @@ -1,35 +0,0 @@ -package commands - -import ( - "github.com/codegangsta/cli" -) - -type CommandLine interface { - ShowHelp() - ShowVersion() - Application() *cli.App - Args() cli.Args - Bool(name string) bool - Int(name string) int - String(name string) string - StringSlice(name string) []string - GlobalString(name string) string - FlagNames() (names []string) - Generic(name string) interface{} -} - -type contextCommandLine struct { - *cli.Context -} - -func (c *contextCommandLine) ShowHelp() { - cli.ShowCommandHelp(c.Context, c.Command.Name) -} - -func (c *contextCommandLine) ShowVersion() { - cli.ShowVersion(c.Context) -} - -func (c *contextCommandLine) Application() *cli.App { - return c.App -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commands.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commands.go deleted file mode 100644 index e72a1e45ff6..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commands.go +++ /dev/null @@ -1,48 +0,0 @@ -package commands - -import ( - "github.com/codegangsta/cli" - "github.com/grafana/grafana-cli/pkg/log" -) - -func runCommand(command func(commandLine CommandLine) error) func(context *cli.Context) { - return func(context *cli.Context) { - - cmd := &contextCommandLine{context} - if err := command(cmd); err != nil { - log.Errorf("%v\n\n", err) - - cmd.ShowHelp() - } else { - log.Info("Restart grafana after installing plugins . \n") - } - } -} - -var Commands = []cli.Command{ - { - Name: "install", - Usage: "installs stuff", - Action: runCommand(installCommand), - }, { - Name: "list-remote", - Usage: "list remote available plugins", - Action: runCommand(listremoteCommand), - }, { - Name: "upgrade", - Usage: "upgrades one plugin", - Action: runCommand(upgradeCommand), - }, { - Name: "upgrade-all", - Usage: "upgrades all your installed plugins", - Action: runCommand(upgradeAllCommand), - }, { - Name: "ls", - Usage: "list all installed plugins", - Action: runCommand(lsCommand), - }, { - Name: "remove", - Usage: "removes stuff", - Action: runCommand(removeCommand), - }, -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_commandLine.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_commandLine.go deleted file mode 100644 index 4a070b5a192..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_commandLine.go +++ /dev/null @@ -1,95 +0,0 @@ -package commandstest - -import ( - "github.com/codegangsta/cli" -) - -type FakeFlagger struct { - Data map[string]interface{} -} - -type FakeCommandLine struct { - LocalFlags, GlobalFlags *FakeFlagger - HelpShown, VersionShown bool - CliArgs []string -} - -func (ff FakeFlagger) String(key string) string { - if value, ok := ff.Data[key]; ok { - return value.(string) - } - return "" -} - -func (ff FakeFlagger) StringSlice(key string) []string { - if value, ok := ff.Data[key]; ok { - return value.([]string) - } - return []string{} -} - -func (ff FakeFlagger) Int(key string) int { - if value, ok := ff.Data[key]; ok { - return value.(int) - } - return 0 -} - -func (ff FakeFlagger) Bool(key string) bool { - if value, ok := ff.Data[key]; ok { - return value.(bool) - } - return false -} - -func (fcli *FakeCommandLine) String(key string) string { - return fcli.LocalFlags.String(key) -} - -func (fcli *FakeCommandLine) StringSlice(key string) []string { - return fcli.LocalFlags.StringSlice(key) -} - -func (fcli *FakeCommandLine) Int(key string) int { - return fcli.LocalFlags.Int(key) -} - -func (fcli *FakeCommandLine) Bool(key string) bool { - if fcli.LocalFlags == nil { - return false - } - return fcli.LocalFlags.Bool(key) -} - -func (fcli *FakeCommandLine) GlobalString(key string) string { - return fcli.GlobalFlags.String(key) -} - -func (fcli *FakeCommandLine) Generic(name string) interface{} { - return fcli.LocalFlags.Data[name] -} - -func (fcli *FakeCommandLine) FlagNames() []string { - flagNames := []string{} - for key := range fcli.LocalFlags.Data { - flagNames = append(flagNames, key) - } - - return flagNames -} - -func (fcli *FakeCommandLine) ShowHelp() { - fcli.HelpShown = true -} - -func (fcli *FakeCommandLine) Application() *cli.App { - return cli.NewApp() -} - -func (fcli *FakeCommandLine) Args() cli.Args { - return fcli.CliArgs -} - -func (fcli *FakeCommandLine) ShowVersion() { - fcli.VersionShown = true -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_ioutil.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_ioutil.go deleted file mode 100644 index e6715a5ea77..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/commandstest/fake_ioutil.go +++ /dev/null @@ -1,55 +0,0 @@ -package commandstest - -import ( - "os" - "time" -) - -type FakeIoUtil struct { - FakeReadDir []os.FileInfo - FakeIsDirectory bool -} - -func (util *FakeIoUtil) Stat(path string) (os.FileInfo, error) { - return FakeFileInfo{IsDirectory: util.FakeIsDirectory}, nil -} - -func (util *FakeIoUtil) RemoveAll(path string) error { - return nil -} - -func (util *FakeIoUtil) ReadDir(path string) ([]os.FileInfo, error) { - return util.FakeReadDir, nil -} - -func (i *FakeIoUtil) ReadFile(filename string) ([]byte, error) { - return make([]byte, 0), nil -} - -type FakeFileInfo struct { - IsDirectory bool -} - -func (ffi FakeFileInfo) IsDir() bool { - return ffi.IsDirectory -} - -func (ffi FakeFileInfo) Size() int64 { - return 1 -} - -func (ffi FakeFileInfo) Mode() os.FileMode { - return 0777 -} - -func (ffi FakeFileInfo) Name() string { - return "" -} - -func (ffi FakeFileInfo) ModTime() time.Time { - return time.Time{} -} - -func (ffi FakeFileInfo) Sys() interface{} { - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/install_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/install_command.go deleted file mode 100644 index f8807d66108..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/install_command.go +++ /dev/null @@ -1,146 +0,0 @@ -package commands - -import ( - "archive/zip" - "bytes" - "errors" - "github.com/grafana/grafana-cli/pkg/log" - m "github.com/grafana/grafana-cli/pkg/models" - services "github.com/grafana/grafana-cli/pkg/services" - "io" - "io/ioutil" - "net/http" - "os" - "path" - "regexp" -) - -func validateInput(c CommandLine, pluginFolder string) error { - arg := c.Args().First() - if arg == "" { - return errors.New("please specify plugin to install") - } - - pluginDir := c.GlobalString("path") - if pluginDir == "" { - return errors.New("missing path flag") - } - - fileinfo, err := os.Stat(pluginDir) - if err != nil && !fileinfo.IsDir() { - return errors.New("path is not a directory") - } - - return nil -} - -func installCommand(c CommandLine) error { - pluginFolder := c.GlobalString("path") - if err := validateInput(c, pluginFolder); err != nil { - return err - } - - pluginToInstall := c.Args().First() - version := c.Args().Get(1) - - log.Infof("version: %v\n", version) - - return InstallPlugin(pluginToInstall, pluginFolder, version) -} - -func InstallPlugin(pluginName, pluginFolder, version string) error { - plugin, err := services.GetPlugin(pluginName) - if err != nil { - return err - } - - v, err := SelectVersion(plugin, version) - if err != nil { - return err - } - - url := v.Url - commit := v.Commit - - downloadURL := url + "/archive/" + commit + ".zip" - - log.Infof("installing %v @ %v\n", plugin.Id, version) - log.Infof("from url: %v\n", downloadURL) - log.Infof("on commit: %v\n", commit) - log.Infof("into: %v\n", pluginFolder) - - err = downloadFile(plugin.Id, pluginFolder, downloadURL) - if err == nil { - log.Info("Installed %s successfully ✔\n", plugin.Id) - } - - res := services.ReadPlugin(pluginFolder, pluginName) - - for _, v := range res.Dependency.Plugins { - log.Infof("Depends on %s install!\n", v.Id) - - //Todo: uncomment this code once the repo is more correct. - //InstallPlugin(v.Id, pluginFolder, "") - } - - return err -} - -func SelectVersion(plugin m.Plugin, version string) (m.Version, error) { - if version == "" { - return plugin.Versions[0], nil - } - - for _, v := range plugin.Versions { - if v.Version == version { - return v, nil - } - } - - return m.Version{}, errors.New("Could not find the version your looking for") -} - -func RemoveGitBuildFromname(pluginname, filename string) string { - r := regexp.MustCompile("^[a-zA-Z0-9_.-]*/") - return r.ReplaceAllString(filename, pluginname+"/") -} - -func downloadFile(pluginName, filepath, url string) (err error) { - resp, err := http.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - r, err := zip.NewReader(bytes.NewReader(body), resp.ContentLength) - if err != nil { - return err - } - for _, zf := range r.File { - newfile := path.Join(filepath, RemoveGitBuildFromname(pluginName, zf.Name)) - - if zf.FileInfo().IsDir() { - os.Mkdir(newfile, 0777) - } else { - dst, err := os.Create(newfile) - if err != nil { - log.Errorf("%v", err) - } - defer dst.Close() - src, err := zf.Open() - if err != nil { - log.Errorf("%v", err) - } - defer src.Close() - - io.Copy(dst, src) - } - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/listremote_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/listremote_command.go deleted file mode 100644 index 9dc1af204c9..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/listremote_command.go +++ /dev/null @@ -1,20 +0,0 @@ -package commands - -import ( - "github.com/grafana/grafana-cli/pkg/log" - "github.com/grafana/grafana-cli/pkg/services" -) - -func listremoteCommand(c CommandLine) error { - plugin, err := services.ListAllPlugins() - - if err != nil { - return err - } - - for _, i := range plugin.Plugins { - log.Infof("id: %v version:\n", i.Id) - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/ls_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/ls_command.go deleted file mode 100644 index 7965f74de27..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/ls_command.go +++ /dev/null @@ -1,49 +0,0 @@ -package commands - -import ( - "errors" - "github.com/grafana/grafana-cli/pkg/log" - m "github.com/grafana/grafana-cli/pkg/models" - s "github.com/grafana/grafana-cli/pkg/services" -) - -var getPlugins func(path string) []m.InstalledPlugin - -var GetStat m.IoUtil - -func init() { - getPlugins = s.GetLocalPlugins - GetStat = s.IoUtil -} - -func validateCommand(pluginDir string) error { - if pluginDir == "" { - return errors.New("missing path flag") - } - - log.Info("plugindir: " + pluginDir + "\n") - pluginDirInfo, err := GetStat.Stat(pluginDir) - - if err != nil { - return errors.New("missing path flag") - } - - if pluginDirInfo.IsDir() == false { - return errors.New("plugin path is not a directory") - } - - return nil -} - -func lsCommand(c CommandLine) error { - pluginDir := c.GlobalString("path") - if err := validateCommand(pluginDir); err != nil { - return err - } - - for _, plugin := range getPlugins(pluginDir) { - log.Infof("plugin: %s @ %s \n", plugin.Name, plugin.Info.Version) - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/remove_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/remove_command.go deleted file mode 100644 index 4a25d04404e..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/remove_command.go +++ /dev/null @@ -1,35 +0,0 @@ -package commands - -import ( - "errors" - "github.com/grafana/grafana-cli/pkg/log" - m "github.com/grafana/grafana-cli/pkg/models" - services "github.com/grafana/grafana-cli/pkg/services" -) - -var getPluginss func(path string) []m.InstalledPlugin = services.GetLocalPlugins -var removePlugin func(pluginPath, id string) error = services.RemoveInstalledPlugin - -func removeCommand(c CommandLine) error { - pluginPath := c.GlobalString("path") - localPlugins := getPluginss(pluginPath) - - log.Info("remove!\n") - - plugin := c.Args().First() - log.Info("plugin: " + plugin + "\n") - if plugin == "" { - return errors.New("Missing which plugin parameter") - } - - log.Infof("plugins : \n%v\n", localPlugins) - - for _, p := range localPlugins { - log.Infof("is %s == %s ? %v", p.Id, c.Args().First(), p.Id == c.Args().First()) - if p.Id == c.Args().First() { - removePlugin(pluginPath, p.Id) - } - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_all_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_all_command.go deleted file mode 100644 index 97454769e62..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_all_command.go +++ /dev/null @@ -1,61 +0,0 @@ -package commands - -import ( - "github.com/grafana/grafana-cli/pkg/log" - m "github.com/grafana/grafana-cli/pkg/models" - services "github.com/grafana/grafana-cli/pkg/services" - "github.com/hashicorp/go-version" -) - -func ShouldUpgrade(installed string, remote m.Plugin) bool { - installedVersion, err1 := version.NewVersion(installed) - - if err1 != nil { - return false - } - - for _, v := range remote.Versions { - remoteVersion, err2 := version.NewVersion(v.Version) - - if err2 == nil { - if installedVersion.LessThan(remoteVersion) { - return true - } - } - } - - return false -} - -func upgradeAllCommand(c CommandLine) error { - pluginDir := c.GlobalString("path") - - localPlugins := services.GetLocalPlugins(pluginDir) - - remotePlugins, err := services.ListAllPlugins() - - if err != nil { - return err - } - - pluginsToUpgrade := make([]m.InstalledPlugin, 0) - - for _, localPlugin := range localPlugins { - for _, remotePlugin := range remotePlugins.Plugins { - if localPlugin.Id == remotePlugin.Id { - if ShouldUpgrade(localPlugin.Info.Version, remotePlugin) { - pluginsToUpgrade = append(pluginsToUpgrade, localPlugin) - } - } - } - } - - for _, p := range pluginsToUpgrade { - log.Infof("lets upgrade %v \n", p) - - services.RemoveInstalledPlugin(pluginDir, p.Id) - InstallPlugin(p.Id, pluginDir, "") - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_command.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_command.go deleted file mode 100644 index df9581b3dc3..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/commands/upgrade_command.go +++ /dev/null @@ -1,9 +0,0 @@ -package commands - -import ( - "errors" -) - -func upgradeCommand(c CommandLine) error { - return errors.New("Not yet Implemented") -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/log/log.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/log/log.go deleted file mode 100644 index c8222d60c81..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/log/log.go +++ /dev/null @@ -1,49 +0,0 @@ -package log - -import ( - "fmt" -) - -var ( - debugmode = false -) - -func Debug(args ...interface{}) { - if debugmode { - fmt.Print(args...) - } -} - -func Debugf(fmtString string, args ...interface{}) { - if debugmode { - fmt.Printf(fmtString, args...) - } -} - -func Error(args ...interface{}) { - fmt.Print(args...) -} - -func Errorf(fmtString string, args ...interface{}) { - fmt.Printf(fmtString, args...) -} - -func Info(args ...interface{}) { - fmt.Print(args...) -} - -func Infof(fmtString string, args ...interface{}) { - fmt.Printf(fmtString, args...) -} - -func Warn(args ...interface{}) { - fmt.Print(args...) -} - -func Warnf(fmtString string, args ...interface{}) { - fmt.Printf(fmtString, args...) -} - -func SetDebug(value bool) { - debugmode = value -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/models/model.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/models/model.go deleted file mode 100644 index 3a39c5fbe65..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/models/model.go +++ /dev/null @@ -1,48 +0,0 @@ -package models - -import ( - "os" -) - -type InstalledPlugin struct { - Id string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - - Info PluginInfo `json:"info"` - Dependency Dependency `json:"dependencies"` -} - -type Dependency struct { - GrafanaVersion string `json:"grafanaVersion"` - Plugins []Plugin `json:"plugins"` -} - -type PluginInfo struct { - Version string `json:"version"` - Updated string `json:"updated"` -} - -type Plugin struct { - Id string `json:"id"` - Category string `json:"category"` - Versions []Version `json:"versions"` -} - -type Version struct { - Commit string `json:"commit"` - Url string `json:"url"` - Version string `json:"version"` -} - -type PluginRepo struct { - Plugins []Plugin `json:"plugins"` - Version string `json:"version"` -} - -type IoUtil interface { - Stat(path string) (os.FileInfo, error) - RemoveAll(path string) error - ReadDir(path string) ([]os.FileInfo, error) - ReadFile(filename string) ([]byte, error) -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/io_util.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/io_util.go deleted file mode 100644 index 5a7cd187575..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/io_util.go +++ /dev/null @@ -1,28 +0,0 @@ -package services - -import ( - m "github.com/grafana/grafana-cli/pkg/models" - "io/ioutil" - "os" -) - -var IoUtil m.IoUtil = IoUtilImp{} - -type IoUtilImp struct { -} - -func (i IoUtilImp) Stat(path string) (os.FileInfo, error) { - return os.Stat(path) -} - -func (i IoUtilImp) RemoveAll(path string) error { - return os.RemoveAll(path) -} - -func (i IoUtilImp) ReadDir(path string) ([]os.FileInfo, error) { - return ioutil.ReadDir(path) -} - -func (i IoUtilImp) ReadFile(filename string) ([]byte, error) { - return ioutil.ReadFile(filename) -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/services.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/services.go deleted file mode 100644 index 9e5b65f5e84..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/services/services.go +++ /dev/null @@ -1,70 +0,0 @@ -package services - -import ( - "encoding/json" - "errors" - "github.com/franela/goreq" - m "github.com/grafana/grafana-cli/pkg/models" - "path" -) - -var IoHelper m.IoUtil = IoUtilImp{} - -func ListAllPlugins() (m.PluginRepo, error) { - res, _ := goreq.Request{Uri: "https://raw.githubusercontent.com/grafana/grafana-plugin-repository/master/repo.json"}.Do() - - var resp m.PluginRepo - err := res.Body.FromJsonTo(&resp) - if err != nil { - return m.PluginRepo{}, errors.New("Could not load plugin data") - } - - return resp, nil -} - -func ReadPlugin(pluginDir, pluginName string) m.InstalledPlugin { - pluginDataPath := path.Join(pluginDir, pluginName, "plugin.json") - pluginData, _ := IoHelper.ReadFile(pluginDataPath) - - res := m.InstalledPlugin{} - json.Unmarshal(pluginData, &res) - - if res.Info.Version == "" { - res.Info.Version = "0.0.0" - } - - if res.Id == "" { - res.Id = res.Name - } - - return res -} - -func GetLocalPlugins(pluginDir string) []m.InstalledPlugin { - result := make([]m.InstalledPlugin, 0) - files, _ := IoHelper.ReadDir(pluginDir) - for _, f := range files { - res := ReadPlugin(pluginDir, f.Name()) - result = append(result, res) - } - - return result -} - -func RemoveInstalledPlugin(pluginPath, id string) error { - return IoHelper.RemoveAll(path.Join(pluginPath, id)) -} - -func GetPlugin(id string) (m.Plugin, error) { - resp, err := ListAllPlugins() - if err != nil { - } - - for _, i := range resp.Plugins { - if i.Id == id { - return i, nil - } - } - - return m.Plugin{}, errors.New("could not find plugin named \"" + id + "\"") -} diff --git a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/version/version.go b/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/version/version.go deleted file mode 100644 index 2a0eddf9859..00000000000 --- a/Godeps/_workspace/src/github.com/grafana/grafana-cli/pkg/version/version.go +++ /dev/null @@ -1,5 +0,0 @@ -package version - -var ( - Version = "0.0.2" -) diff --git a/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE b/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE deleted file mode 100644 index 63cef79ba6f..00000000000 --- a/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2016 Errplane Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md b/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md deleted file mode 100644 index f0794abc112..00000000000 --- a/Godeps/_workspace/src/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md +++ /dev/null @@ -1,27 +0,0 @@ -# List -- bootstrap 3.3.5 [MIT LICENSE](https://github.com/twbs/bootstrap/blob/master/LICENSE) -- collectd.org [ISC LICENSE](https://github.com/collectd/go-collectd/blob/master/LICENSE) -- github.com/armon/go-metrics [MIT LICENSE](https://github.com/armon/go-metrics/blob/master/LICENSE) -- github.com/BurntSushi/toml [WTFPL LICENSE](https://github.com/BurntSushi/toml/blob/master/COPYING) -- github.com/bmizerany/pat [MIT LICENSE](https://github.com/bmizerany/pat#license) -- github.com/boltdb/bolt [MIT LICENSE](https://github.com/boltdb/bolt/blob/master/LICENSE) -- github.com/dgryski/go-bits [MIT LICENSE](https://github.com/dgryski/go-bits/blob/master/LICENSE) -- github.com/dgryski/go-bitstream [MIT LICENSE](https://github.com/dgryski/go-bitstream/blob/master/LICENSE) -- github.com/gogo/protobuf/proto [BSD LICENSE](https://github.com/gogo/protobuf/blob/master/LICENSE) -- github.com/davecgh/go-spew/spew [ISC LICENSE](https://github.com/davecgh/go-spew/blob/master/LICENSE) -- github.com/golang/snappy [BSD LICENSE](https://github.com/golang/snappy/blob/master/LICENSE) -- github.com/hashicorp/go-msgpack [BSD LICENSE](https://github.com/hashicorp/go-msgpack/blob/master/LICENSE) -- github.com/hashicorp/raft [MPL LICENSE](https://github.com/hashicorp/raft/blob/master/LICENSE) -- github.com/hashicorp/raft-boltdb [MOZILLA PUBLIC LICENSE](https://github.com/hashicorp/raft-boltdb/blob/master/LICENSE) -- github.com/influxdata/usage-client [MIT LICENSE](https://github.com/influxdata/usage-client/blob/master/LICENSE.txt) -- github.com/jwilder/encoding [MIT LICENSE](https://github.com/jwilder/encoding/blob/master/LICENSE) -- github.com/kimor79/gollectd [BSD LICENSE](https://github.com/kimor79/gollectd/blob/master/LICENSE) -- github.com/paulbellamy/ratecounter [MIT LICENSE](https://github.com/paulbellamy/ratecounter/blob/master/LICENSE) -- github.com/peterh/liner [MIT LICENSE](https://github.com/peterh/liner/blob/master/COPYING) -- github.com/rakyll/statik [APACHE LICENSE](https://github.com/rakyll/statik/blob/master/LICENSE) -- glyphicons [LICENSE](http://glyphicons.com/license/) -- golang.org/x/crypto [BSD LICENSE](https://github.com/golang/crypto/blob/master/LICENSE) -- golang.org/x/tools [BSD LICENSE](https://github.com/golang/tools/blob/master/LICENSE) -- gopkg.in/fatih/pool.v2 [MIT LICENSE](https://github.com/fatih/pool/blob/v2.0.0/LICENSE) -- jquery 2.1.4 [MIT LICENSE](https://github.com/jquery/jquery/blob/master/LICENSE.txt) -- react 0.13.3 [BSD LICENSE](https://github.com/facebook/react/blob/master/LICENSE) diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/assertions.goconvey b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/assertions.goconvey deleted file mode 100644 index 8a7f1b6671a..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/assertions.goconvey +++ /dev/null @@ -1,3 +0,0 @@ -#ignore --timeout=1s --coverpkg=github.com/smartystreets/goconvey/convey/assertions,github.com/smartystreets/goconvey/convey/assertions/oglematchers \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/doc.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/doc.go deleted file mode 100644 index 7bbd628eef2..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/doc.go +++ /dev/null @@ -1,43 +0,0 @@ -// Package assertions contains the implementations for all assertions which -// are referenced in the convey package for use with the So(...) method. -package assertions - -// This function is not used by the goconvey library. It's actually a convenience method -// for running assertions on arbitrary arguments outside of any testing context, like for -// application logging. It allows you to perform assertion-like behavior (and get nicely -// formatted messages detailing discrepancies) but without the probram blowing up or panicking. -// All that is required is to import this package and call `So` with one of the assertions -// exported by this package as the second parameter. -// The first return parameter is a boolean indicating if the assertion was true. The second -// return parameter is the well-formatted message showing why an assertion was incorrect, or -// blank if the assertion was correct. -// -// Example: -// -// if ok, message := So(x, ShouldBeGreaterThan, y); !ok { -// log.Println(message) -// } -// -func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) { - serializer = noop - - if result := so(actual, assert, expected...); len(result) == 0 { - return true, result - } else { - return false, result - } -} - -// so is like So, except that it only returns the string message, which is blank if the -// assertion passed. Used to facilitate testing. -func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string { - return assert(actual, expected...) -} - -// assertion is an alias for a function with a signature that the So() -// function can handle. Any future or custom assertions should conform to this -// method signature. The return value should be an empty string if the assertion -// passes and a well-formed failure message if not. -type assertion func(actual interface{}, expected ...interface{}) string - -//////////////////////////////////////////////////////////////////////////// diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/init.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/init.go deleted file mode 100644 index 753bcc7daa7..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/init.go +++ /dev/null @@ -1,6 +0,0 @@ -package assertions - -var ( - serializer Serializer = newSerializer() - noop Serializer = new(noopSerializer) -) diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/.gitignore b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/.gitignore deleted file mode 100644 index dd8fc7468f4..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.6 -6.out -_obj/ -_test/ -_testmain.go diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/oglematchers.goconvey b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/oglematchers.goconvey deleted file mode 100644 index 79982854b53..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/assertions/oglematchers/oglematchers.goconvey +++ /dev/null @@ -1,2 +0,0 @@ -#ignore --timeout=1s diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/focused_execution_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/focused_execution_test.go deleted file mode 100644 index 294e32fa17e..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/focused_execution_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package convey - -import "testing" - -func TestFocusOnlyAtTopLevel(t *testing.T) { - output := prepare() - - FocusConvey("hi", t, func() { - output += "done" - }) - - expectEqual(t, "done", output) -} - -func TestFocus(t *testing.T) { - output := prepare() - - FocusConvey("hi", t, func() { - output += "1" - - Convey("bye", func() { - output += "2" - }) - }) - - expectEqual(t, "1", output) -} - -func TestNestedFocus(t *testing.T) { - output := prepare() - - FocusConvey("hi", t, func() { - output += "1" - - Convey("This shouldn't run", func() { - output += "boink!" - }) - - FocusConvey("This should run", func() { - output += "2" - - FocusConvey("The should run too", func() { - output += "3" - - }) - - Convey("The should NOT run", func() { - output += "blah blah blah!" - }) - }) - }) - - expectEqual(t, "123", output) -} - -func TestForgotTopLevelFocus(t *testing.T) { - output := prepare() - - Convey("1", t, func() { - output += "1" - - FocusConvey("This will be run because the top-level lacks Focus", func() { - output += "2" - }) - - Convey("3", func() { - output += "3" - }) - }) - - expectEqual(t, "1213", output) -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/isolated_execution_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/isolated_execution_test.go deleted file mode 100644 index 7e22b3caa53..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/isolated_execution_test.go +++ /dev/null @@ -1,774 +0,0 @@ -package convey - -import ( - "strconv" - "testing" - "time" -) - -func TestSingleScope(t *testing.T) { - output := prepare() - - Convey("hi", t, func() { - output += "done" - }) - - expectEqual(t, "done", output) -} - -func TestSingleScopeWithMultipleConveys(t *testing.T) { - output := prepare() - - Convey("1", t, func() { - output += "1" - }) - - Convey("2", t, func() { - output += "2" - }) - - expectEqual(t, "12", output) -} - -func TestNestedScopes(t *testing.T) { - output := prepare() - - Convey("a", t, func() { - output += "a " - - Convey("bb", func() { - output += "bb " - - Convey("ccc", func() { - output += "ccc | " - }) - }) - }) - - expectEqual(t, "a bb ccc | ", output) -} - -func TestNestedScopesWithIsolatedExecution(t *testing.T) { - output := prepare() - - Convey("a", t, func() { - output += "a " - - Convey("aa", func() { - output += "aa " - - Convey("aaa", func() { - output += "aaa | " - }) - - Convey("aaa1", func() { - output += "aaa1 | " - }) - }) - - Convey("ab", func() { - output += "ab " - - Convey("abb", func() { - output += "abb | " - }) - }) - }) - - expectEqual(t, "a aa aaa | a aa aaa1 | a ab abb | ", output) -} - -func TestSingleScopeWithConveyAndNestedReset(t *testing.T) { - output := prepare() - - Convey("1", t, func() { - output += "1" - - Reset(func() { - output += "a" - }) - }) - - expectEqual(t, "1a", output) -} - -func TestPanicingReset(t *testing.T) { - output := prepare() - - Convey("1", t, func() { - output += "1" - - Reset(func() { - panic("nooo") - }) - - Convey("runs since the reset hasn't yet", func() { - output += "a" - }) - - Convey("but this doesnt", func() { - output += "nope" - }) - }) - - expectEqual(t, "1a", output) -} - -func TestSingleScopeWithMultipleRegistrationsAndReset(t *testing.T) { - output := prepare() - - Convey("reset after each nested convey", t, func() { - Convey("first output", func() { - output += "1" - }) - - Convey("second output", func() { - output += "2" - }) - - Reset(func() { - output += "a" - }) - }) - - expectEqual(t, "1a2a", output) -} - -func TestSingleScopeWithMultipleRegistrationsAndMultipleResets(t *testing.T) { - output := prepare() - - Convey("each reset is run at end of each nested convey", t, func() { - Convey("1", func() { - output += "1" - }) - - Convey("2", func() { - output += "2" - }) - - Reset(func() { - output += "a" - }) - - Reset(func() { - output += "b" - }) - }) - - expectEqual(t, "1ab2ab", output) -} - -func Test_Failure_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) { - output := prepare() - - Convey("This step fails", t, func() { - So(1, ShouldEqual, 2) - - Convey("this should NOT be executed", func() { - output += "a" - }) - }) - - expectEqual(t, "", output) -} - -func Test_Panic_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) { - output := prepare() - - Convey("This step panics", t, func() { - Convey("this happens, because the panic didn't happen yet", func() { - output += "1" - }) - - output += "a" - - Convey("this should NOT be executed", func() { - output += "2" - }) - - output += "b" - - panic("Hi") - - output += "nope" - }) - - expectEqual(t, "1ab", output) -} - -func Test_Panic_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) { - output := prepare() - - Convey("This is the parent", t, func() { - Convey("This step panics", func() { - panic("Hi") - output += "1" - }) - - Convey("This sibling should execute", func() { - output += "2" - }) - }) - - expectEqual(t, "2", output) -} - -func Test_Failure_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) { - output := prepare() - - Convey("This is the parent", t, func() { - Convey("This step fails", func() { - So(1, ShouldEqual, 2) - output += "1" - }) - - Convey("This sibling should execute", func() { - output += "2" - }) - }) - - expectEqual(t, "2", output) -} - -func TestResetsAreAlwaysExecutedAfterScope_Panics(t *testing.T) { - output := prepare() - - Convey("This is the parent", t, func() { - Convey("This step panics", func() { - panic("Hi") - output += "1" - }) - - Convey("This sibling step does not panic", func() { - output += "a" - - Reset(func() { - output += "b" - }) - }) - - Reset(func() { - output += "2" - }) - }) - - expectEqual(t, "2ab2", output) -} - -func TestResetsAreAlwaysExecutedAfterScope_Failures(t *testing.T) { - output := prepare() - - Convey("This is the parent", t, func() { - Convey("This step fails", func() { - So(1, ShouldEqual, 2) - output += "1" - }) - - Convey("This sibling step does not fail", func() { - output += "a" - - Reset(func() { - output += "b" - }) - }) - - Reset(func() { - output += "2" - }) - }) - - expectEqual(t, "2ab2", output) -} - -func TestSkipTopLevel(t *testing.T) { - output := prepare() - - SkipConvey("hi", t, func() { - output += "This shouldn't be executed!" - }) - - expectEqual(t, "", output) -} - -func TestSkipNestedLevel(t *testing.T) { - output := prepare() - - Convey("hi", t, func() { - output += "yes" - - SkipConvey("bye", func() { - output += "no" - }) - }) - - expectEqual(t, "yes", output) -} - -func TestSkipNestedLevelSkipsAllChildLevels(t *testing.T) { - output := prepare() - - Convey("hi", t, func() { - output += "yes" - - SkipConvey("bye", func() { - output += "no" - - Convey("byebye", func() { - output += "no-no" - }) - }) - }) - - expectEqual(t, "yes", output) -} - -func TestIterativeConveys(t *testing.T) { - output := prepare() - - Convey("Test", t, func() { - for x := 0; x < 10; x++ { - y := strconv.Itoa(x) - - Convey(y, func() { - output += y - }) - } - }) - - expectEqual(t, "0123456789", output) -} - -func TestClosureVariables(t *testing.T) { - output := prepare() - - i := 0 - - Convey("A", t, func() { - i = i + 1 - j := i - - output += "A" + strconv.Itoa(i) + " " - - Convey("B", func() { - k := j - j = j + 1 - - output += "B" + strconv.Itoa(k) + " " - - Convey("C", func() { - output += "C" + strconv.Itoa(k) + strconv.Itoa(j) + " " - }) - - Convey("D", func() { - output += "D" + strconv.Itoa(k) + strconv.Itoa(j) + " " - }) - }) - - Convey("C", func() { - output += "C" + strconv.Itoa(j) + " " - }) - }) - - output += "D" + strconv.Itoa(i) + " " - - expectEqual(t, "A1 B1 C12 A2 B2 D23 A3 C3 D3 ", output) -} - -func TestClosureVariablesWithReset(t *testing.T) { - output := prepare() - - i := 0 - - Convey("A", t, func() { - i = i + 1 - j := i - - output += "A" + strconv.Itoa(i) + " " - - Reset(func() { - output += "R" + strconv.Itoa(i) + strconv.Itoa(j) + " " - }) - - Convey("B", func() { - output += "B" + strconv.Itoa(j) + " " - }) - - Convey("C", func() { - output += "C" + strconv.Itoa(j) + " " - }) - }) - - output += "D" + strconv.Itoa(i) + " " - - expectEqual(t, "A1 B1 R11 A2 C2 R22 D2 ", output) -} - -func TestWrappedSimple(t *testing.T) { - prepare() - output := resetTestString{""} - - Convey("A", t, func() { - func() { - output.output += "A " - - Convey("B", func() { - output.output += "B " - - Convey("C", func() { - output.output += "C " - }) - - }) - - Convey("D", func() { - output.output += "D " - }) - }() - }) - - expectEqual(t, "A B C A D ", output.output) -} - -type resetTestString struct { - output string -} - -func addReset(o *resetTestString, f func()) func() { - return func() { - Reset(func() { - o.output += "R " - }) - - f() - } -} - -func TestWrappedReset(t *testing.T) { - prepare() - output := resetTestString{""} - - Convey("A", t, addReset(&output, func() { - output.output += "A " - - Convey("B", func() { - output.output += "B " - }) - - Convey("C", func() { - output.output += "C " - }) - })) - - expectEqual(t, "A B R A C R ", output.output) -} - -func TestWrappedReset2(t *testing.T) { - prepare() - output := resetTestString{""} - - Convey("A", t, func() { - Reset(func() { - output.output += "R " - }) - - func() { - output.output += "A " - - Convey("B", func() { - output.output += "B " - - Convey("C", func() { - output.output += "C " - }) - }) - - Convey("D", func() { - output.output += "D " - }) - }() - }) - - expectEqual(t, "A B C R A D R ", output.output) -} - -func TestInfiniteLoopWithTrailingFail(t *testing.T) { - done := make(chan int) - - go func() { - Convey("This fails", t, func() { - Convey("and this is run", func() { - So(true, ShouldEqual, true) - }) - - /* And this prevents the whole block to be marked as run */ - So(false, ShouldEqual, true) - }) - - done <- 1 - }() - - select { - case <-done: - return - case <-time.After(1 * time.Millisecond): - t.Fail() - } -} - -func TestOutermostResetInvokedForGrandchildren(t *testing.T) { - output := prepare() - - Convey("A", t, func() { - output += "A " - - Reset(func() { - output += "rA " - }) - - Convey("B", func() { - output += "B " - - Reset(func() { - output += "rB " - }) - - Convey("C", func() { - output += "C " - - Reset(func() { - output += "rC " - }) - }) - - Convey("D", func() { - output += "D " - - Reset(func() { - output += "rD " - }) - }) - }) - }) - - expectEqual(t, "A B C rC rB rA A B D rD rB rA ", output) -} - -func TestFailureOption(t *testing.T) { - output := prepare() - - Convey("A", t, FailureHalts, func() { - output += "A " - So(true, ShouldEqual, true) - output += "B " - So(false, ShouldEqual, true) - output += "C " - }) - - expectEqual(t, "A B ", output) -} - -func TestFailureOption2(t *testing.T) { - output := prepare() - - Convey("A", t, func() { - output += "A " - So(true, ShouldEqual, true) - output += "B " - So(false, ShouldEqual, true) - output += "C " - }) - - expectEqual(t, "A B ", output) -} - -func TestFailureOption3(t *testing.T) { - output := prepare() - - Convey("A", t, FailureContinues, func() { - output += "A " - So(true, ShouldEqual, true) - output += "B " - So(false, ShouldEqual, true) - output += "C " - }) - - expectEqual(t, "A B C ", output) -} - -func TestFailureOptionInherit(t *testing.T) { - output := prepare() - - Convey("A", t, FailureContinues, func() { - output += "A1 " - So(false, ShouldEqual, true) - output += "A2 " - - Convey("B", func() { - output += "B1 " - So(true, ShouldEqual, true) - output += "B2 " - So(false, ShouldEqual, true) - output += "B3 " - }) - }) - - expectEqual(t, "A1 A2 B1 B2 B3 ", output) -} - -func TestFailureOptionInherit2(t *testing.T) { - output := prepare() - - Convey("A", t, FailureHalts, func() { - output += "A1 " - So(false, ShouldEqual, true) - output += "A2 " - - Convey("B", func() { - output += "A1 " - So(true, ShouldEqual, true) - output += "A2 " - So(false, ShouldEqual, true) - output += "A3 " - }) - }) - - expectEqual(t, "A1 ", output) -} - -func TestFailureOptionInherit3(t *testing.T) { - output := prepare() - - Convey("A", t, FailureHalts, func() { - output += "A1 " - So(true, ShouldEqual, true) - output += "A2 " - - Convey("B", func() { - output += "B1 " - So(true, ShouldEqual, true) - output += "B2 " - So(false, ShouldEqual, true) - output += "B3 " - }) - }) - - expectEqual(t, "A1 A2 B1 B2 ", output) -} - -func TestFailureOptionNestedOverride(t *testing.T) { - output := prepare() - - Convey("A", t, FailureContinues, func() { - output += "A " - So(false, ShouldEqual, true) - output += "B " - - Convey("C", FailureHalts, func() { - output += "C " - So(true, ShouldEqual, true) - output += "D " - So(false, ShouldEqual, true) - output += "E " - }) - }) - - expectEqual(t, "A B C D ", output) -} - -func TestFailureOptionNestedOverride2(t *testing.T) { - output := prepare() - - Convey("A", t, FailureHalts, func() { - output += "A " - So(true, ShouldEqual, true) - output += "B " - - Convey("C", FailureContinues, func() { - output += "C " - So(true, ShouldEqual, true) - output += "D " - So(false, ShouldEqual, true) - output += "E " - }) - }) - - expectEqual(t, "A B C D E ", output) -} - -func TestMultipleInvocationInheritance(t *testing.T) { - output := prepare() - - Convey("A", t, FailureHalts, func() { - output += "A1 " - So(true, ShouldEqual, true) - output += "A2 " - - Convey("B", FailureContinues, func() { - output += "B1 " - So(true, ShouldEqual, true) - output += "B2 " - So(false, ShouldEqual, true) - output += "B3 " - }) - - Convey("C", func() { - output += "C1 " - So(true, ShouldEqual, true) - output += "C2 " - So(false, ShouldEqual, true) - output += "C3 " - }) - }) - - expectEqual(t, "A1 A2 B1 B2 B3 A1 A2 C1 C2 ", output) -} - -func TestMultipleInvocationInheritance2(t *testing.T) { - output := prepare() - - Convey("A", t, FailureContinues, func() { - output += "A1 " - So(true, ShouldEqual, true) - output += "A2 " - So(false, ShouldEqual, true) - output += "A3 " - - Convey("B", FailureHalts, func() { - output += "B1 " - So(true, ShouldEqual, true) - output += "B2 " - So(false, ShouldEqual, true) - output += "B3 " - }) - - Convey("C", func() { - output += "C1 " - So(true, ShouldEqual, true) - output += "C2 " - So(false, ShouldEqual, true) - output += "C3 " - }) - }) - - expectEqual(t, "A1 A2 A3 B1 B2 A1 A2 A3 C1 C2 C3 ", output) -} - -func TestSetDefaultFailureMode(t *testing.T) { - output := prepare() - - SetDefaultFailureMode(FailureContinues) // the default is normally FailureHalts - defer SetDefaultFailureMode(FailureHalts) - - Convey("A", t, func() { - output += "A1 " - So(true, ShouldBeFalse) - output += "A2 " - }) - - expectEqual(t, "A1 A2 ", output) -} - -func prepare() string { - testReporter = newNilReporter() - return "" -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/dot_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/dot_test.go deleted file mode 100644 index a8d20d46f08..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/dot_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package reporting - -import ( - "errors" - "testing" -) - -func TestDotReporterAssertionPrinting(t *testing.T) { - monochrome() - file := newMemoryFile() - printer := NewPrinter(file) - reporter := NewDotReporter(printer) - - reporter.Report(NewSuccessReport()) - reporter.Report(NewFailureReport("failed")) - reporter.Report(NewErrorReport(errors.New("error"))) - reporter.Report(NewSkipReport()) - - expected := dotSuccess + dotFailure + dotError + dotSkip - - if file.buffer != expected { - t.Errorf("\nExpected: '%s'\nActual: '%s'", expected, file.buffer) - } -} - -func TestDotReporterOnlyReportsAssertions(t *testing.T) { - monochrome() - file := newMemoryFile() - printer := NewPrinter(file) - reporter := NewDotReporter(printer) - - reporter.BeginStory(nil) - reporter.Enter(nil) - reporter.Exit() - reporter.EndStory() - - if file.buffer != "" { - t.Errorf("\nExpected: '(blank)'\nActual: '%s'", file.buffer) - } -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go deleted file mode 100644 index fda189458e5..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package reporting - -import "testing" - -func TestReporterReceivesSuccessfulReport(t *testing.T) { - reporter := NewGoTestReporter() - test := new(fakeTest) - reporter.BeginStory(NewStoryReport(test)) - reporter.Report(NewSuccessReport()) - - if test.failed { - t.Errorf("Should have have marked test as failed--the report reflected success.") - } -} - -func TestReporterReceivesFailureReport(t *testing.T) { - reporter := NewGoTestReporter() - test := new(fakeTest) - reporter.BeginStory(NewStoryReport(test)) - reporter.Report(NewFailureReport("This is a failure.")) - - if !test.failed { - t.Errorf("Test should have been marked as failed (but it wasn't).") - } -} - -func TestReporterReceivesErrorReport(t *testing.T) { - reporter := NewGoTestReporter() - test := new(fakeTest) - reporter.BeginStory(NewStoryReport(test)) - reporter.Report(NewErrorReport("This is an error.")) - - if !test.failed { - t.Errorf("Test should have been marked as failed (but it wasn't).") - } -} - -func TestReporterIsResetAtTheEndOfTheStory(t *testing.T) { - defer catch(t) - reporter := NewGoTestReporter() - test := new(fakeTest) - reporter.BeginStory(NewStoryReport(test)) - reporter.EndStory() - - reporter.Report(NewSuccessReport()) -} - -func TestReporterNoopMethods(t *testing.T) { - reporter := NewGoTestReporter() - reporter.Enter(NewScopeReport("title")) - reporter.Exit() -} - -func catch(t *testing.T) { - if r := recover(); r != nil { - t.Log("Getting to this point means we've passed (because we caught a panic appropriately).") - } -} - -type fakeTest struct { - failed bool -} - -func (self *fakeTest) Fail() { - self.failed = true -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/printer_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/printer_test.go deleted file mode 100644 index 94202d5ac97..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/printer_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package reporting - -import "testing" - -func TestPrint(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "Hello, World!" - - printer.Print(expected) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintFormat(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - template := "Hi, %s" - name := "Ralph" - expected := "Hi, Ralph" - - printer.Print(template, name) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintPreservesEncodedStrings(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "= -> %3D" - printer.Print(expected) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintln(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "Hello, World!" - - printer.Println(expected) - - if file.buffer != expected+"\n" { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintlnFormat(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - template := "Hi, %s" - name := "Ralph" - expected := "Hi, Ralph\n" - - printer.Println(template, name) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintlnPreservesEncodedStrings(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "= -> %3D" - printer.Println(expected) - - if file.buffer != expected+"\n" { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintIndented(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const message = "Hello, World!\nGoodbye, World!" - const expected = " Hello, World!\n Goodbye, World!" - - printer.Indent() - printer.Print(message) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintDedented(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "Hello, World!\nGoodbye, World!" - - printer.Indent() - printer.Dedent() - printer.Print(expected) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintlnIndented(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const message = "Hello, World!\nGoodbye, World!" - const expected = " Hello, World!\n Goodbye, World!\n" - - printer.Indent() - printer.Println(message) - - if file.buffer != expected { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestPrintlnDedented(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - const expected = "Hello, World!\nGoodbye, World!" - - printer.Indent() - printer.Dedent() - printer.Println(expected) - - if file.buffer != expected+"\n" { - t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) - } -} - -func TestDedentTooFarShouldNotPanic(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Error("Should not have panicked!") - } - }() - file := newMemoryFile() - printer := NewPrinter(file) - - printer.Dedent() - - t.Log("Getting to this point without panicking means we passed.") -} - -func TestInsert(t *testing.T) { - file := newMemoryFile() - printer := NewPrinter(file) - - printer.Indent() - printer.Print("Hi") - printer.Insert(" there") - printer.Dedent() - - expected := " Hi there" - if file.buffer != expected { - t.Errorf("Should have written '%s' but instead wrote '%s'.", expected, file.buffer) - } -} - -////////////////// memoryFile //////////////////// - -type memoryFile struct { - buffer string -} - -func (self *memoryFile) Write(p []byte) (n int, err error) { - self.buffer += string(p) - return len(p), nil -} - -func (self *memoryFile) String() string { - return self.buffer -} - -func newMemoryFile() *memoryFile { - return new(memoryFile) -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/problems_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/problems_test.go deleted file mode 100644 index 92f0ca35cca..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/problems_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package reporting - -import ( - "strings" - "testing" -) - -func TestNoopProblemReporterActions(t *testing.T) { - file, reporter := setup() - reporter.BeginStory(nil) - reporter.Enter(nil) - reporter.Exit() - expected := "" - actual := file.String() - if expected != actual { - t.Errorf("Expected: '(blank)'\nActual: '%s'", actual) - } -} - -func TestReporterPrintsFailuresAndErrorsAtTheEndOfTheStory(t *testing.T) { - file, reporter := setup() - reporter.Report(NewFailureReport("failed")) - reporter.Report(NewErrorReport("error")) - reporter.Report(NewSuccessReport()) - reporter.EndStory() - - result := file.String() - if !strings.Contains(result, "Errors:\n") { - t.Errorf("Expected errors, found none.") - } - if !strings.Contains(result, "Failures:\n") { - t.Errorf("Expected failures, found none.") - } - - // Each stack trace looks like: `* /path/to/file.go`, so look for `* `. - // With go 1.4+ there is a line in some stack traces that looks like this: - // `testing.(*M).Run(0x2082d60a0, 0x25b7c0)` - // So we can't just look for "*" anymore. - problemCount := strings.Count(result, "* ") - if problemCount != 2 { - t.Errorf("Expected one failure and one error (total of 2 '*' characters). Got %d", problemCount) - } -} - -func setup() (file *memoryFile, reporter *problem) { - monochrome() - file = newMemoryFile() - printer := NewPrinter(file) - reporter = NewProblemReporter(printer) - return -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go deleted file mode 100644 index 4e5caf63b2b..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package reporting - -import ( - "runtime" - "testing" -) - -func TestEachNestedReporterReceivesTheCallFromTheContainingReporter(t *testing.T) { - fake1 := newFakeReporter() - fake2 := newFakeReporter() - reporter := NewReporters(fake1, fake2) - - reporter.BeginStory(nil) - assertTrue(t, fake1.begun) - assertTrue(t, fake2.begun) - - reporter.Enter(NewScopeReport("scope")) - assertTrue(t, fake1.entered) - assertTrue(t, fake2.entered) - - reporter.Report(NewSuccessReport()) - assertTrue(t, fake1.reported) - assertTrue(t, fake2.reported) - - reporter.Exit() - assertTrue(t, fake1.exited) - assertTrue(t, fake2.exited) - - reporter.EndStory() - assertTrue(t, fake1.ended) - assertTrue(t, fake2.ended) - - content := []byte("hi") - written, err := reporter.Write(content) - assertTrue(t, fake1.written) - assertTrue(t, fake2.written) - assertEqual(t, written, len(content)) - assertNil(t, err) - -} - -func assertTrue(t *testing.T, value bool) { - if !value { - _, _, line, _ := runtime.Caller(1) - t.Errorf("Value should have been true (but was false). See line %d", line) - } -} - -func assertEqual(t *testing.T, expected, actual int) { - if actual != expected { - _, _, line, _ := runtime.Caller(1) - t.Errorf("Value should have been %d (but was %d). See line %d", expected, actual, line) - } -} - -func assertNil(t *testing.T, err error) { - if err != nil { - _, _, line, _ := runtime.Caller(1) - t.Errorf("Error should have been (but wasn't). See line %d", err, line) - } -} - -type fakeReporter struct { - begun bool - entered bool - reported bool - exited bool - ended bool - written bool -} - -func newFakeReporter() *fakeReporter { - return &fakeReporter{} -} - -func (self *fakeReporter) BeginStory(story *StoryReport) { - self.begun = true -} -func (self *fakeReporter) Enter(scope *ScopeReport) { - self.entered = true -} -func (self *fakeReporter) Report(report *AssertionResult) { - self.reported = true -} -func (self *fakeReporter) Exit() { - self.exited = true -} -func (self *fakeReporter) EndStory() { - self.ended = true -} -func (self *fakeReporter) Write(content []byte) (int, error) { - self.written = true - return len(content), nil -} diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go deleted file mode 100644 index 69125c3cf44..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go +++ /dev/null @@ -1,317 +0,0 @@ -package convey - -import ( - "fmt" - "net/http" - "net/http/httptest" - "path" - "runtime" - "strconv" - "strings" - "testing" - - "github.com/smartystreets/goconvey/convey/reporting" -) - -func TestSingleScopeReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - So(1, ShouldEqual, 1) - }) - - expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory()) -} - -func TestNestedScopeReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - Convey("B", func() { - So(1, ShouldEqual, 1) - }) - }) - - expectEqual(t, "Begin|A|B|Success|Exit|Exit|End", myReporter.wholeStory()) -} - -func TestFailureReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - So(1, ShouldBeNil) - }) - - expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory()) -} - -func TestFirstFailureEndsScopeExecution(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - So(1, ShouldBeNil) - So(nil, ShouldBeNil) - }) - - expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory()) -} - -func TestComparisonFailureDeserializedAndReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - So("hi", ShouldEqual, "bye") - }) - - expectEqual(t, "Begin|A|Failure(bye/hi)|Exit|End", myReporter.wholeStory()) -} - -func TestNestedFailureReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - Convey("B", func() { - So(2, ShouldBeNil) - }) - }) - - expectEqual(t, "Begin|A|B|Failure|Exit|Exit|End", myReporter.wholeStory()) -} - -func TestSuccessAndFailureReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - So(nil, ShouldBeNil) - So(1, ShouldBeNil) - }) - - expectEqual(t, "Begin|A|Success|Failure|Exit|End", myReporter.wholeStory()) -} - -func TestIncompleteActionReportedAsSkipped(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - Convey("B", nil) - }) - - expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory()) -} - -func TestSkippedConveyReportedAsSkipped(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - SkipConvey("B", func() { - So(1, ShouldEqual, 1) - }) - }) - - expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory()) -} - -func TestMultipleSkipsAreReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - Convey("0", func() { - So(nil, ShouldBeNil) - }) - - SkipConvey("1", func() {}) - SkipConvey("2", func() {}) - - Convey("3", nil) - Convey("4", nil) - - Convey("5", func() { - So(nil, ShouldBeNil) - }) - }) - - expected := "Begin" + - "|A|0|Success|Exit|Exit" + - "|A|1|Skipped|Exit|Exit" + - "|A|2|Skipped|Exit|Exit" + - "|A|3|Skipped|Exit|Exit" + - "|A|4|Skipped|Exit|Exit" + - "|A|5|Success|Exit|Exit" + - "|End" - - expectEqual(t, expected, myReporter.wholeStory()) -} - -func TestSkippedAssertionIsNotReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - SkipSo(1, ShouldEqual, 1) - }) - - expectEqual(t, "Begin|A|Skipped|Exit|End", myReporter.wholeStory()) -} - -func TestMultipleSkippedAssertionsAreNotReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - SkipSo(1, ShouldEqual, 1) - So(1, ShouldEqual, 1) - SkipSo(1, ShouldEqual, 1) - }) - - expectEqual(t, "Begin|A|Skipped|Success|Skipped|Exit|End", myReporter.wholeStory()) -} - -func TestErrorByManualPanicReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - panic("Gopher alert!") - }) - - expectEqual(t, "Begin|A|Error|Exit|End", myReporter.wholeStory()) -} - -func TestIterativeConveysReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - for x := 0; x < 3; x++ { - Convey(strconv.Itoa(x), func() { - So(x, ShouldEqual, x) - }) - } - }) - - expectEqual(t, "Begin|A|0|Success|Exit|Exit|A|1|Success|Exit|Exit|A|2|Success|Exit|Exit|End", myReporter.wholeStory()) -} - -func TestNestedIterativeConveysReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func() { - for x := 0; x < 3; x++ { - Convey(strconv.Itoa(x), func() { - for y := 0; y < 3; y++ { - Convey("< "+strconv.Itoa(y), func() { - So(x, ShouldBeLessThan, y) - }) - } - }) - } - }) - - expectEqual(t, ("Begin|" + - "A|0|< 0|Failure|Exit|Exit|Exit|" + - "A|0|< 1|Success|Exit|Exit|Exit|" + - "A|0|< 2|Success|Exit|Exit|Exit|" + - "A|1|< 0|Failure|Exit|Exit|Exit|" + - "A|1|< 1|Failure|Exit|Exit|Exit|" + - "A|1|< 2|Success|Exit|Exit|Exit|" + - "A|2|< 0|Failure|Exit|Exit|Exit|" + - "A|2|< 1|Failure|Exit|Exit|Exit|" + - "A|2|< 2|Failure|Exit|Exit|Exit|" + - "End"), myReporter.wholeStory()) -} - -func TestEmbeddedAssertionReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - Convey("A", test, func(c C) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.So(r.FormValue("msg"), ShouldEqual, "ping") - })) - http.DefaultClient.Get(ts.URL + "?msg=ping") - }) - - expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory()) -} - -func TestEmbeddedContextHelperReported(t *testing.T) { - myReporter, test := setupFakeReporter() - - helper := func(c C) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Convey("Embedded", func() { - So(r.FormValue("msg"), ShouldEqual, "ping") - }) - }) - } - - Convey("A", test, func(c C) { - ts := httptest.NewServer(helper(c)) - http.DefaultClient.Get(ts.URL + "?msg=ping") - }) - - expectEqual(t, "Begin|A|Embedded|Success|Exit|Exit|End", myReporter.wholeStory()) -} - -func expectEqual(t *testing.T, expected interface{}, actual interface{}) { - if expected != actual { - _, file, line, _ := runtime.Caller(1) - t.Errorf("Expected '%v' to be '%v' but it wasn't. See '%s' at line %d.", - actual, expected, path.Base(file), line) - } -} - -func setupFakeReporter() (*fakeReporter, *fakeGoTest) { - myReporter := new(fakeReporter) - myReporter.calls = []string{} - testReporter = myReporter - return myReporter, new(fakeGoTest) -} - -type fakeReporter struct { - calls []string -} - -func (self *fakeReporter) BeginStory(story *reporting.StoryReport) { - self.calls = append(self.calls, "Begin") -} - -func (self *fakeReporter) Enter(scope *reporting.ScopeReport) { - self.calls = append(self.calls, scope.Title) -} - -func (self *fakeReporter) Report(report *reporting.AssertionResult) { - if report.Error != nil { - self.calls = append(self.calls, "Error") - } else if report.Failure != "" { - message := "Failure" - if report.Expected != "" || report.Actual != "" { - message += fmt.Sprintf("(%s/%s)", report.Expected, report.Actual) - } - self.calls = append(self.calls, message) - } else if report.Skipped { - self.calls = append(self.calls, "Skipped") - } else { - self.calls = append(self.calls, "Success") - } -} - -func (self *fakeReporter) Exit() { - self.calls = append(self.calls, "Exit") -} - -func (self *fakeReporter) EndStory() { - self.calls = append(self.calls, "End") -} - -func (self *fakeReporter) Write(content []byte) (int, error) { - return len(content), nil // no-op -} - -func (self *fakeReporter) wholeStory() string { - return strings.Join(self.calls, "|") -} - -//////////////////////////////// - -type fakeGoTest struct{} - -func (self *fakeGoTest) Fail() {} -func (self *fakeGoTest) Fatalf(format string, args ...interface{}) {} - -var test t = new(fakeGoTest) diff --git a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/story_conventions_test.go b/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/story_conventions_test.go deleted file mode 100644 index ff3a4e6e7a0..00000000000 --- a/Godeps/_workspace/src/github.com/smartystreets/goconvey/convey/story_conventions_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package convey - -import ( - "reflect" - "testing" -) - -func expectPanic(t *testing.T, f string) interface{} { - r := recover() - if r != nil { - if cp, ok := r.(*conveyErr); ok { - if cp.fmt != f { - t.Error("Incorrect panic message.") - } - } else { - t.Errorf("Incorrect panic type. %s", reflect.TypeOf(r)) - } - } else { - t.Error("Expected panic but none occured") - } - return r -} - -func TestMissingTopLevelGoTestReferenceCausesPanic(t *testing.T) { - output := map[string]bool{} - - defer expectEqual(t, false, output["good"]) - defer expectPanic(t, missingGoTest) - - Convey("Hi", func() { - output["bad"] = true // this shouldn't happen - }) -} - -func requireGoTestReference(t *testing.T) { - err := recover() - if err == nil { - t.Error("We should have recovered a panic here (because of a missing *testing.T reference)!") - } else { - expectEqual(t, missingGoTest, err) - } -} - -func TestMissingTopLevelGoTestReferenceAfterGoodExample(t *testing.T) { - output := map[string]bool{} - - defer func() { - expectEqual(t, true, output["good"]) - expectEqual(t, false, output["bad"]) - }() - defer expectPanic(t, missingGoTest) - - Convey("Good example", t, func() { - output["good"] = true - }) - - Convey("Bad example", func() { - output["bad"] = true // shouldn't happen - }) -} - -func TestExtraReferencePanics(t *testing.T) { - output := map[string]bool{} - - defer expectEqual(t, false, output["bad"]) - defer expectPanic(t, extraGoTest) - - Convey("Good example", t, func() { - Convey("Bad example - passing in *testing.T a second time!", t, func() { - output["bad"] = true // shouldn't happen - }) - }) -} - -func TestParseRegistrationMissingRequiredElements(t *testing.T) { - defer expectPanic(t, parseError) - - Convey() -} - -func TestParseRegistration_MissingNameString(t *testing.T) { - defer expectPanic(t, parseError) - - Convey(func() {}) -} - -func TestParseRegistration_MissingActionFunc(t *testing.T) { - defer expectPanic(t, parseError) - - Convey("Hi there", 12345) -} - -func TestFailureModeNoContext(t *testing.T) { - Convey("Foo", t, func() { - done := make(chan int, 1) - go func() { - defer func() { done <- 1 }() - defer expectPanic(t, noStackContext) - So(len("I have no context"), ShouldBeGreaterThan, 0) - }() - <-done - }) -} - -func TestFailureModeDuplicateSuite(t *testing.T) { - Convey("cool", t, func() { - defer expectPanic(t, multipleIdenticalConvey) - - Convey("dup", nil) - Convey("dup", nil) - }) -} - -func TestFailureModeIndeterminentSuiteNames(t *testing.T) { - defer expectPanic(t, differentConveySituations) - - name := "bob" - Convey("cool", t, func() { - for i := 0; i < 3; i++ { - Convey(name, func() {}) - name += "bob" - } - }) -} - -func TestFailureModeNestedIndeterminentSuiteNames(t *testing.T) { - defer expectPanic(t, differentConveySituations) - - name := "bob" - Convey("cool", t, func() { - Convey("inner", func() { - for i := 0; i < 3; i++ { - Convey(name, func() {}) - name += "bob" - } - }) - }) -} - -func TestFailureModeParameterButMissing(t *testing.T) { - defer expectPanic(t, parseError) - - prepare() - - Convey("Foobar", t, FailureHalts) -} - -func TestFailureModeParameterWithAction(t *testing.T) { - prepare() - - Convey("Foobar", t, FailureHalts, func() {}) -} - -func TestExtraConveyParameters(t *testing.T) { - defer expectPanic(t, parseError) - - prepare() - - Convey("Foobar", t, FailureHalts, func() {}, "This is not supposed to be here") -} - -func TestExtraConveyParameters2(t *testing.T) { - defer expectPanic(t, parseError) - - prepare() - - Convey("Foobar", t, func() {}, "This is not supposed to be here") -} - -func TestExtraConveyParameters3(t *testing.T) { - defer expectPanic(t, parseError) - - output := prepare() - - Convey("A", t, func() { - output += "A " - - Convey("B", func() { - output += "B " - }, "This is not supposed to be here") - }) - - expectEqual(t, "A ", output) -} diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/.gitignore b/Godeps/_workspace/src/gopkg.in/ini.v1/.gitignore deleted file mode 100644 index 53b64215a3b..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -testdata/conf_out.ini -ini.sublime-project -ini.sublime-workspace diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/README.md b/Godeps/_workspace/src/gopkg.in/ini.v1/README.md deleted file mode 100644 index 6d771819656..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/README.md +++ /dev/null @@ -1,378 +0,0 @@ -ini [![Build Status](https://drone.io/github.com/go-ini/ini/status.png)](https://drone.io/github.com/go-ini/ini/latest) [![](http://gocover.io/_badge/github.com/go-ini/ini)](http://gocover.io/github.com/go-ini/ini) -=== - -Package ini provides INI file read and write functionality in Go. - -[简体中文](README_ZH.md) - -## Feature - -- Load multiple data sources(`[]byte` or file) with overwrites. -- Read with recursion values. -- Read with parent-child sections. -- Read with auto-increment key names. -- Read with multiple-line values. -- Read with tons of helper methods. -- Read and convert values to Go types. -- Read and **WRITE** comments of sections and keys. -- Manipulate sections, keys and comments with ease. -- Keep sections and keys in order as you parse and save. - -## Installation - - go get gopkg.in/ini.v1 - -## Getting Started - -### Loading from data sources - -A **Data Source** is either raw data in type `[]byte` or a file name with type `string` and you can load **as many as** data sources you want. Passing other types will simply return an error. - -```go -cfg, err := ini.Load([]byte("raw data"), "filename") -``` - -Or start with an empty object: - -```go -cfg := ini.Empty() -``` - -When you cannot decide how many data sources to load at the beginning, you still able to **Append()** them later. - -```go -err := cfg.Append("other file", []byte("other raw data")) -``` - -### Working with sections - -To get a section, you would need to: - -```go -section, err := cfg.GetSection("section name") -``` - -For a shortcut for default section, just give an empty string as name: - -```go -section, err := cfg.GetSection("") -``` - -When you're pretty sure the section exists, following code could make your life easier: - -```go -section := cfg.Section("") -``` - -What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you. - -To create a new section: - -```go -err := cfg.NewSection("new section") -``` - -To get a list of sections or section names: - -```go -sections := cfg.Sections() -names := cfg.SectionStrings() -``` - -### Working with keys - -To get a key under a section: - -```go -key, err := cfg.Section("").GetKey("key name") -``` - -Same rule applies to key operations: - -```go -key := cfg.Section("").Key("key name") -``` - -To create a new key: - -```go -err := cfg.Section("").NewKey("name", "value") -``` - -To get a list of keys or key names: - -```go -keys := cfg.Section().Keys() -names := cfg.Section().KeyStrings() -``` - -To get a clone hash of keys and corresponding values: - -```go -hash := cfg.GetSection("").KeysHash() -``` - -### Working with values - -To get a string value: - -```go -val := cfg.Section("").Key("key name").String() -``` - -To get value with types: - -```go -// For boolean values: -// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On -// false when value is: 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off -v, err = cfg.Section("").Key("BOOL").Bool() -v, err = cfg.Section("").Key("FLOAT64").Float64() -v, err = cfg.Section("").Key("INT").Int() -v, err = cfg.Section("").Key("INT64").Int64() -v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339) -v, err = cfg.Section("").Key("TIME").Time() // RFC3339 - -v = cfg.Section("").Key("BOOL").MustBool() -v = cfg.Section("").Key("FLOAT64").MustFloat64() -v = cfg.Section("").Key("INT").MustInt() -v = cfg.Section("").Key("INT64").MustInt64() -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339) -v = cfg.Section("").Key("TIME").MustTime() // RFC3339 - -// Methods start with Must also accept one argument for default value -// when key not found or fail to parse value to given type. -// Except method MustString, which you have to pass a default value. - -v = cfg.Seciont("").Key("String").MustString("default") -v = cfg.Section("").Key("BOOL").MustBool(true) -v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25) -v = cfg.Section("").Key("INT").MustInt(10) -v = cfg.Section("").Key("INT64").MustInt64(99) -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now()) -v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339 -``` - -What if my value is three-line long? - -```ini -[advance] -ADDRESS = """404 road, -NotFound, State, 5000 -Earth""" -``` - -Not a problem! - -```go -cfg.Section("advance").Key("ADDRESS").String() - -/* --- start --- -404 road, -NotFound, State, 5000 -Earth ------- end --- */ -``` - -That's all? Hmm, no. - -#### Helper methods of working with values - -To get value with given candidates: - -```go -v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"}) -v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75}) -v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30}) -v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30}) -v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3}) -v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339 -``` - -Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates. - -To validate value in a given range: - -```go -vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2) -vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20) -vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20) -vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) -vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339 -``` - -To auto-split value into slice: - -```go -vals = cfg.Section("").Key("STRINGS").Strings(",") -vals = cfg.Section("").Key("FLOAT64S").Float64s(",") -vals = cfg.Section("").Key("INTS").Ints(",") -vals = cfg.Section("").Key("INT64S").Int64s(",") -vals = cfg.Section("").Key("TIMES").Times(",") -``` - -## Advanced Usage - -### Recursive Values - -For all value of keys, there is a special syntax `%()s`, where `` is the key name in same section or default section, and `%()s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions. - -```ini -NAME = ini - -[author] -NAME = Unknwon -GITHUB = https://github.com/%(NAME)s - -[package] -FULL_NAME = github.com/go-ini/%(NAME)s -``` - -```go -cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon -cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini -``` - -### Parent-child Sections - -You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section. - -```ini -NAME = ini -VERSION = v1 -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -``` - -```go -cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1 -``` - -### Auto-increment Key Names - -If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter. - -```ini -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values -``` - -```go -cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"} -``` - -### Map To Struct - -Want more objective way to play with INI? Cool. - -```ini -Name = Unknwon -age = 21 -Male = true -Born = 1993-01-01T20:17:05Z - -[Note] -Content = Hi is a good man! -Cities = HangZhou, Boston -``` - -```go -type Note struct { - Content string - Cities []string -} - -type Person struct { - Name string - Age int `ini:"age"` - Male bool - Born time.Time - Note - Created time.Time `ini:"-"` -} - -func main() { - cfg, err := ini.Load("path/to/ini") - // ... - p := new(Person) - err = cfg.MapTo(p) - // ... - - // Things can be simpler. - err = ini.MapTo(p, "path/to/ini") - // ... - - // Just map a section? Fine. - n := new(Note) - err = cfg.Section("Note").MapTo(n) - // ... -} -``` - -Can I have default value for field? Absolutely. - -Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type. - -```go -// ... -p := &Person{ - Name: "Joe", -} -// ... -``` - -#### Name Mapper - -To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual secion and key name. - -There are 2 built-in name mappers: - -- `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key. -- `TitleUnderscore`: it converts to format `title_underscore` then match section or key. - -To use them: - -```go -type Info struct{ - PackageName string -} - -func main() { - err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini")) - // ... - - cfg, err := ini.Load("PACKAGE_NAME=ini") - // ... - info := new(Info) - cfg.NameMapper = ini.AllCapsUnderscore - err = cfg.MapTo(info) - // ... -} -``` - -## Getting Help - -- [API Documentation](https://gowalker.org/gopkg.in/ini.v1) -- [File An Issue](https://github.com/go-ini/ini/issues/new) - -## FAQs - -### What does `BlockMode` field do? - -By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster. - -### Why another INI library? - -Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster. - -To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path) - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/README_ZH.md b/Godeps/_workspace/src/gopkg.in/ini.v1/README_ZH.md deleted file mode 100644 index c455cb67297..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/README_ZH.md +++ /dev/null @@ -1,367 +0,0 @@ -本包提供了 Go 语言中读写 INI 文件的功能。 - -## 功能特性 - -- 支持覆盖加载多个数据源(`[]byte` 或文件) -- 支持递归读取键值 -- 支持读取父子分区 -- 支持读取自增键名 -- 支持读取多行的键值 -- 支持大量辅助方法 -- 支持在读取时直接转换为 Go 语言类型 -- 支持读取和 **写入** 分区和键的注释 -- 轻松操作分区、键值和注释 -- 在保存文件时分区和键值会保持原有的顺序 - -## 下载安装 - - go get gopkg.in/ini.v1 - -## 开始使用 - -### 从数据源加载 - -一个 **数据源** 可以是 `[]byte` 类型的原始数据,或 `string` 类型的文件路径。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。 - -```go -cfg, err := ini.Load([]byte("raw data"), "filename") -``` - -或者从一个空白的文件开始: - -```go -cfg := ini.Empty() -``` - -当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。 - -```go -err := cfg.Append("other file", []byte("other raw data")) -``` - -### 操作分区(Section) - -获取指定分区: - -```go -section, err := cfg.GetSection("section name") -``` - -如果您想要获取默认分区,则可以用空字符串代替分区名: - -```go -section, err := cfg.GetSection("") -``` - -当您非常确定某个分区是存在的,可以使用以下简便方法: - -```go -section := cfg.Section("") -``` - -如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。 - -创建一个分区: - -```go -err := cfg.NewSection("new section") -``` - -获取所有分区对象或名称: - -```go -sections := cfg.Sections() -names := cfg.SectionStrings() -``` - -### 操作键(Key) - -获取某个分区下的键: - -```go -key, err := cfg.Section("").GetKey("key name") -``` - -和分区一样,您也可以直接获取键而忽略错误处理: - -```go -key := cfg.Section("").Key("key name") -``` - -创建一个新的键: - -```go -err := cfg.Section("").NewKey("name", "value") -``` - -获取分区下的所有键或键名: - -```go -keys := cfg.Section().Keys() -names := cfg.Section().KeyStrings() -``` - -获取分区下的所有键值对的克隆: - -```go -hash := cfg.GetSection("").KeysHash() -``` - -### 操作键值(Value) - -获取一个类型为字符串(string)的值: - -```go -val := cfg.Section("").Key("key name").String() -``` - -获取其它类型的值: - -```go -// 布尔值的规则: -// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On -// false 当值为:0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off -v, err = cfg.Section("").Key("BOOL").Bool() -v, err = cfg.Section("").Key("FLOAT64").Float64() -v, err = cfg.Section("").Key("INT").Int() -v, err = cfg.Section("").Key("INT64").Int64() -v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339) -v, err = cfg.Section("").Key("TIME").Time() // RFC3339 - -v = cfg.Section("").Key("BOOL").MustBool() -v = cfg.Section("").Key("FLOAT64").MustFloat64() -v = cfg.Section("").Key("INT").MustInt() -v = cfg.Section("").Key("INT64").MustInt64() -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339) -v = cfg.Section("").Key("TIME").MustTime() // RFC3339 - -// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值, -// 当键不存在或者转换失败时,则会直接返回该默认值。 -// 但是,MustString 方法必须传递一个默认值。 - -v = cfg.Seciont("").Key("String").MustString("default") -v = cfg.Section("").Key("BOOL").MustBool(true) -v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25) -v = cfg.Section("").Key("INT").MustInt(10) -v = cfg.Section("").Key("INT64").MustInt64(99) -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now()) -v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339 -``` - -如果我的值有好多行怎么办? - -```ini -[advance] -ADDRESS = """404 road, -NotFound, State, 5000 -Earth""" -``` - -嗯哼?小 case! - -```go -cfg.Section("advance").Key("ADDRESS").String() - -/* --- start --- -404 road, -NotFound, State, 5000 -Earth ------- end --- */ -``` - -这就是全部了?哈哈,当然不是。 - -#### 操作键值的辅助方法 - -获取键值时设定候选值: - -```go -v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"}) -v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75}) -v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30}) -v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30}) -v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3}) -v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339 -``` - -如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。 - -验证获取的值是否在指定范围内: - -```go -vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2) -vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20) -vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20) -vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) -vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339 -``` - -自动分割键值为切片(slice): - -```go -vals = cfg.Section("").Key("STRINGS").Strings(",") -vals = cfg.Section("").Key("FLOAT64S").Float64s(",") -vals = cfg.Section("").Key("INTS").Ints(",") -vals = cfg.Section("").Key("INT64S").Int64s(",") -vals = cfg.Section("").Key("TIMES").Times(",") -``` - -### 高级用法 - -#### 递归读取键值 - -在获取所有键值的过程中,特殊语法 `%()s` 会被应用,其中 `` 可以是相同分区或者默认分区下的键名。字符串 `%()s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。 - -```ini -NAME = ini - -[author] -NAME = Unknwon -GITHUB = https://github.com/%(NAME)s - -[package] -FULL_NAME = github.com/go-ini/%(NAME)s -``` - -```go -cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon -cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini -``` - -#### 读取父子分区 - -您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。 - -```ini -NAME = ini -VERSION = v1 -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -``` - -```go -cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1 -``` - -#### 读取自增键名 - -如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。 - -```ini -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values -``` - -```go -cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"} -``` - -### 映射到结构 - -想要使用更加面向对象的方式玩转 INI 吗?好主意。 - -```ini -Name = Unknwon -age = 21 -Male = true -Born = 1993-01-01T20:17:05Z - -[Note] -Content = Hi is a good man! -Cities = HangZhou, Boston -``` - -```go -type Note struct { - Content string - Cities []string -} - -type Person struct { - Name string - Age int `ini:"age"` - Male bool - Born time.Time - Note - Created time.Time `ini:"-"` -} - -func main() { - cfg, err := ini.Load("path/to/ini") - // ... - p := new(Person) - err = cfg.MapTo(p) - // ... - - // 一切竟可以如此的简单。 - err = ini.MapTo(p, "path/to/ini") - // ... - - // 嗯哼?只需要映射一个分区吗? - n := new(Note) - err = cfg.Section("Note").MapTo(n) - // ... -} -``` - -结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。 - -```go -// ... -p := &Person{ - Name: "Joe", -} -// ... -``` - -#### 名称映射器(Name Mapper) - -为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。 - -目前有 2 款内置的映射器: - -- `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。 -- `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。 - -使用方法: - -```go -type Info struct{ - PackageName string -} - -func main() { - err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini")) - // ... - - cfg, err := ini.Load("PACKAGE_NAME=ini") - // ... - info := new(Info) - cfg.NameMapper = ini.AllCapsUnderscore - err = cfg.MapTo(info) - // ... -} -``` - -## 获取帮助 - -- [API 文档](https://gowalker.org/gopkg.in/ini.v1) -- [创建工单](https://github.com/go-ini/ini/issues/new) - -## 常见问题 - -### 字段 `BlockMode` 是什么? - -默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。 - -### 为什么要写另一个 INI 解析库? - -许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。 - -为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了) diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/ini.go b/Godeps/_workspace/src/gopkg.in/ini.v1/ini.go deleted file mode 100644 index 6674baf0b00..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/ini.go +++ /dev/null @@ -1,1017 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini provides INI file read and write functionality in Go. -package ini - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "os" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -const ( - DEFAULT_SECTION = "DEFAULT" - // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - - _VERSION = "1.2.6" -) - -func Version() string { - return _VERSION -} - -var ( - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - - // Write spaces around "=" to look better. - PrettyFormat = true -) - -func init() { - if runtime.GOOS == "windows" { - LineBreak = "\r\n" - } -} - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} - -// dataSource is a interface that returns file content. -type dataSource interface { - Reader() (io.Reader, error) -} - -type sourceFile struct { - name string -} - -func (s sourceFile) Reader() (io.Reader, error) { - return os.Open(s.name) -} - -type sourceData struct { - data []byte -} - -func (s *sourceData) Reader() (io.Reader, error) { - return bytes.NewReader(s.data), nil -} - -// ____ __. -// | |/ _|____ ___.__. -// | <_/ __ < | | -// | | \ ___/\___ | -// |____|__ \___ > ____| -// \/ \/\/ - -// Key represents a key under a section. -type Key struct { - s *Section - Comment string - name string - value string - isAutoIncr bool -} - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// String returns string representation of value. -func (k *Key) String() string { - val := k.value - if strings.Index(val, "%") == -1 { - return val - } - - for i := 0; i < _DEPTH_VALUES; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := strings.TrimLeft(vr, "%(") - noption = strings.TrimRight(noption, ")s") - - // Search in the same section. - nk, err := k.s.GetKey(noption) - if err != nil { - // Search again in default section. - nk, _ = k.s.f.Section("").GetKey(noption) - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - return strconv.Atoi(k.String()) -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 10, 64) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string devide by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - vals := strings.Split(str, delim) - for i := range vals { - vals[i] = strings.TrimSpace(vals[i]) - } - return vals -} - -// Float64s returns list of float64 devide by given delimiter. -func (k *Key) Float64s(delim string) []float64 { - strs := k.Strings(delim) - vals := make([]float64, len(strs)) - for i := range strs { - vals[i], _ = strconv.ParseFloat(strs[i], 64) - } - return vals -} - -// Ints returns list of int devide by given delimiter. -func (k *Key) Ints(delim string) []int { - strs := k.Strings(delim) - vals := make([]int, len(strs)) - for i := range strs { - vals[i], _ = strconv.Atoi(strs[i]) - } - return vals -} - -// Int64s returns list of int64 devide by given delimiter. -func (k *Key) Int64s(delim string) []int64 { - strs := k.Strings(delim) - vals := make([]int64, len(strs)) - for i := range strs { - vals[i], _ = strconv.ParseInt(strs[i], 10, 64) - } - return vals -} - -// TimesFormat parses with given format and returns list of time.Time devide by given delimiter. -func (k *Key) TimesFormat(format, delim string) []time.Time { - strs := k.Strings(delim) - vals := make([]time.Time, len(strs)) - for i := range strs { - vals[i], _ = time.Parse(format, strs[i]) - } - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time devide by given delimiter. -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - k.value = v -} - -// _________ __ .__ -// / _____/ ____ _____/ |_|__| ____ ____ -// \_____ \_/ __ \_/ ___\ __\ |/ _ \ / \ -// / \ ___/\ \___| | | ( <_> ) | \ -// /_______ /\___ >\___ >__| |__|\____/|___| / -// \/ \/ \/ \/ - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string -} - -func newSection(f *File, name string) *Section { - return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)} -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - s.keys[name].value = val - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = &Key{s, "", name, val, false} - s.keysHash[name] = val - return s.keys[name], nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - key := s.keys[name] - if key == nil { - // Check if it is a child-section. - if i := strings.LastIndex(s.name, "."); i > -1 { - return s.f.Section(s.name[:i]).GetKey(name) - } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) - } - return key, nil -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := map[string]string{} - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - return - } - } -} - -// ___________.__.__ -// \_ _____/|__| | ____ -// | __) | | | _/ __ \ -// | \ | | |_\ ___/ -// \___ / |__|____/\___ > -// \/ \/ - -// File represents a combination of a or more INI file(s) in memory. -type File struct { - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - // Make sure data is safe in multiple goroutines. - lock sync.RWMutex - - // Allow combination of multiple data sources. - dataSources []dataSource - // Actual data is stored here. - sections map[string]*Section - - // To keep data in order. - sectionList []string - - NameMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - } -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) - } -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -func Load(source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources) - return f, f.Reload() -} - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("error when getting section: section '%s' not exists", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - sections := make([]*Section, len(f.sectionList)) - for i := range f.sectionList { - sections[i] = f.Section(f.sectionList[i]) - } - return sections -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func cutComment(str string) string { - i := strings.Index(str, "#") - if i == -1 { - return str - } - return str[:i] -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) error { - buf := bufio.NewReader(reader) - - // Handle BOM-UTF8. - // http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding - mask, err := buf.Peek(3) - if err == nil && len(mask) >= 3 && mask[0] == 239 && mask[1] == 187 && mask[2] == 191 { - buf.Read(mask) - } - - count := 1 - comments := "" - isEnd := false - - section, err := f.NewSection(DEFAULT_SECTION) - if err != nil { - return err - } - - for { - line, err := buf.ReadString('\n') - line = strings.TrimSpace(line) - length := len(line) - - // Check error and ignore io.EOF just for a moment. - if err != nil { - if err != io.EOF { - return fmt.Errorf("error reading next line: %v", err) - } - // The last line of file could be an empty line. - if length == 0 { - break - } - isEnd = true - } - - // Skip empty lines. - if length == 0 { - continue - } - - switch { - case line[0] == '#' || line[0] == ';': // Comments. - if len(comments) == 0 { - comments = line - } else { - comments += LineBreak + line - } - continue - case line[0] == '[' && line[length-1] == ']': // New sction. - name := strings.TrimSpace(line[1 : length-1]) - section, err = f.NewSection(name) - if err != nil { - return err - } - - if len(comments) > 0 { - section.Comment = comments - comments = "" - } - // Reset counter. - count = 1 - continue - } - - // Other possibilities. - var ( - i int - keyQuote string - kname string - valQuote string - val string - ) - - // Key name surrounded by quotes. - if line[0] == '"' { - if length > 6 && line[0:3] == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - if len(keyQuote) > 0 { - qLen := len(keyQuote) - pos := strings.Index(line[qLen:], keyQuote) - if pos == -1 { - return fmt.Errorf("error parsing line: missing closing key quote: %s", line) - } - pos = pos + qLen - i = strings.IndexAny(line[pos:], "=:") - if i < 0 { - return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line) - } else if i == pos { - return fmt.Errorf("error parsing line: key is empty: %s", line) - } - i = i + pos - kname = line[qLen:pos] // Just keep spaces inside quotes. - } else { - i = strings.IndexAny(line, "=:") - if i < 0 { - return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line) - } else if i == 0 { - return fmt.Errorf("error parsing line: key is empty: %s", line) - } - kname = strings.TrimSpace(line[0:i]) - } - - isAutoIncr := false - // Auto increment. - if kname == "-" { - isAutoIncr = true - kname = "#" + fmt.Sprint(count) - count++ - } - - lineRight := strings.TrimSpace(line[i+1:]) - lineRightLength := len(lineRight) - firstChar := "" - if lineRightLength >= 2 { - firstChar = lineRight[0:1] - } - if firstChar == "`" { - valQuote = "`" - } else if lineRightLength >= 6 && lineRight[0:3] == `"""` { - valQuote = `"""` - } - if len(valQuote) > 0 { - qLen := len(valQuote) - pos := strings.LastIndex(lineRight[qLen:], valQuote) - // For multiple lines value. - if pos == -1 { - isEnd := false - val = lineRight[qLen:] + "\n" - for { - next, err := buf.ReadString('\n') - if err != nil { - if err != io.EOF { - return err - } - isEnd = true - } - pos = strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - break - } - val += next - if isEnd { - return fmt.Errorf("error parsing line: missing closing key quote from '%s' to '%s'", line, next) - } - } - } else { - val = lineRight[qLen : pos+qLen] - } - } else { - val = strings.TrimSpace(cutComment(lineRight[0:])) - } - - k, err := section.NewKey(kname, val) - if err != nil { - return err - } - k.isAutoIncr = isAutoIncr - if len(comments) > 0 { - k.Comment = comments - comments = "" - } - - if isEnd { - break - } - } - return nil -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() error { - for _, s := range f.dataSources { - r, err := s.Reader() - if err != nil { - return err - } - if err = f.parse(r); err != nil { - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -// SaveTo writes content to filesystem. -func (f *File) SaveTo(filename string) (err error) { - equalSign := "=" - if PrettyFormat { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } - if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil { - return err - } - } - - if i > 0 { - if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return err - } - } else { - // Write nothing if default section is empty. - if len(sec.keyList) == 0 { - continue - } - } - - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } - if _, err = buf.WriteString(key.Comment + LineBreak); err != nil { - return err - } - } - - switch { - case key.isAutoIncr: - kname = "-" - case strings.Contains(kname, "`") || strings.Contains(kname, `"`): - kname = `"""` + kname + `"""` - case strings.Contains(kname, `=`) || strings.Contains(kname, `:`): - kname = "`" + kname + "`" - } - - val := key.value - // In case key value contains "\n", "`" or "\"". - if strings.Contains(val, "\n") || strings.Contains(val, "`") || strings.Contains(val, `"`) { - val = `"""` + val + `"""` - } - if _, err = buf.WriteString(kname + equalSign + val + LineBreak); err != nil { - return err - } - } - - // Put a line between sections. - if _, err = buf.WriteString(LineBreak); err != nil { - return err - } - } - - fw, err := os.Create(filename) - if err != nil { - return err - } - if _, err = buf.WriteTo(fw); err != nil { - return err - } - return fw.Close() -} diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/ini_test.go b/Godeps/_workspace/src/gopkg.in/ini.v1/ini_test.go deleted file mode 100644 index c6daf81ca52..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/ini_test.go +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "fmt" - "strings" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Version(t *testing.T) { - Convey("Get version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} - -const _CONF_DATA = ` -; Package name -NAME = ini -; Package version -VERSION = v1 -; Package import path -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -# Information about package author -# Bio can be written in multiple lines. -[author] -NAME = Unknwon # Succeeding comment -E-MAIL = fake@localhost -GITHUB = https://github.com/%(NAME)s -BIO = """Gopher. -Coding addict. -Good man. -""" # Succeeding comment - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -UNUSED_KEY = should be deleted - -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values - -[types] -STRING = str -BOOL = true -BOOL_FALSE = false -FLOAT64 = 1.25 -INT = 10 -TIME = 2015-01-01T20:17:05Z - -[array] -STRINGS = en, zh, de -FLOAT64S = 1.1, 2.2, 3.3 -INTS = 1, 2, 3 -TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z - -[note] - -[advance] -true = """"2+3=5"""" -"1+1=2" = true -"""6+1=7""" = true -"""` + "`" + `5+5` + "`" + `""" = 10 -""""6+6"""" = 12 -` + "`" + `7-2=4` + "`" + ` = false -ADDRESS = ` + "`" + `404 road, -NotFound, State, 50000` + "`" - -func Test_Load(t *testing.T) { - Convey("Load from data sources", t, func() { - - Convey("Load with empty data", func() { - So(Empty(), ShouldNotBeNil) - }) - - Convey("Load with multiple data sources", func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - }) - }) - - Convey("Bad load process", t, func() { - - Convey("Load from invalid data sources", func() { - _, err := Load(_CONF_DATA) - So(err, ShouldNotBeNil) - - _, err = Load("testdata/404.ini") - So(err, ShouldNotBeNil) - - _, err = Load(1) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(""), 1) - So(err, ShouldNotBeNil) - }) - - Convey("Load with empty section name", func() { - _, err := Load([]byte("[]")) - So(err, ShouldNotBeNil) - }) - - Convey("Load with bad keys", func() { - _, err := Load([]byte(`"""name`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`"""name"""`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`""=1`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`=`)) - So(err, ShouldNotBeNil) - - _, err = Load([]byte(`name`)) - So(err, ShouldNotBeNil) - }) - - Convey("Load with bad values", func() { - _, err := Load([]byte(`name="""Unknwon`)) - So(err, ShouldNotBeNil) - }) - }) -} - -func Test_Values(t *testing.T) { - Convey("Test getting and setting values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - Convey("Get values in default section", func() { - sec := cfg.Section("") - So(sec, ShouldNotBeNil) - So(sec.Key("NAME").Value(), ShouldEqual, "ini") - So(sec.Key("NAME").String(), ShouldEqual, "ini") - So(sec.Key("NAME").Comment, ShouldEqual, "; Package name") - So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1") - }) - - Convey("Get values in non-default section", func() { - sec := cfg.Section("author") - So(sec, ShouldNotBeNil) - So(sec.Key("NAME").String(), ShouldEqual, "Unknwon") - So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon") - - sec = cfg.Section("package") - So(sec, ShouldNotBeNil) - So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") - }) - - Convey("Get auto-increment key names", func() { - keys := cfg.Section("features").Keys() - for i, k := range keys { - So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1)) - } - }) - - Convey("Get overwrite value", func() { - So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") - }) - - Convey("Get sections", func() { - sections := cfg.Sections() - for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "advance"} { - So(sections[i].Name(), ShouldEqual, name) - } - }) - - Convey("Get parent section value", func() { - So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1") - }) - - Convey("Get multiple line value", func() { - So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n") - }) - - Convey("Get values with type", func() { - sec := cfg.Section("types") - v1, err := sec.Key("BOOL").Bool() - So(err, ShouldBeNil) - So(v1, ShouldBeTrue) - - v1, err = sec.Key("BOOL_FALSE").Bool() - So(err, ShouldBeNil) - So(v1, ShouldBeFalse) - - v2, err := sec.Key("FLOAT64").Float64() - So(err, ShouldBeNil) - So(v2, ShouldEqual, 1.25) - - v3, err := sec.Key("INT").Int() - So(err, ShouldBeNil) - So(v3, ShouldEqual, 10) - - v4, err := sec.Key("INT").Int64() - So(err, ShouldBeNil) - So(v4, ShouldEqual, 10) - - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - v5, err := sec.Key("TIME").Time() - So(err, ShouldBeNil) - So(v5.String(), ShouldEqual, t.String()) - - Convey("Must get values with type", func() { - So(sec.Key("STRING").MustString("404"), ShouldEqual, "str") - So(sec.Key("BOOL").MustBool(), ShouldBeTrue) - So(sec.Key("FLOAT64").MustFloat64(), ShouldEqual, 1.25) - So(sec.Key("INT").MustInt(), ShouldEqual, 10) - So(sec.Key("INT").MustInt64(), ShouldEqual, 10) - So(sec.Key("TIME").MustTime().String(), ShouldEqual, t.String()) - - Convey("Must get values with default value", func() { - So(sec.Key("STRING_404").MustString("404"), ShouldEqual, "404") - So(sec.Key("BOOL_404").MustBool(true), ShouldBeTrue) - So(sec.Key("FLOAT64_404").MustFloat64(2.5), ShouldEqual, 2.5) - So(sec.Key("INT_404").MustInt(15), ShouldEqual, 15) - So(sec.Key("INT_404").MustInt64(15), ShouldEqual, 15) - - t, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME_404").MustTime(t).String(), ShouldEqual, t.String()) - }) - }) - }) - - Convey("Get value with candidates", func() { - sec := cfg.Section("types") - So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str") - So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25) - So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("INT").InInt64(0, []int64{10, 20, 30}), ShouldEqual, 10) - - zt, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z") - So(err, ShouldBeNil) - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME").InTime(zt, []time.Time{t, time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String()) - - Convey("Get value with candidates and default value", func() { - So(sec.Key("STRING_404").In("str", []string{"str", "arr", "types"}), ShouldEqual, "str") - So(sec.Key("FLOAT64_404").InFloat64(1.25, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25) - So(sec.Key("INT_404").InInt(10, []int{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("INT64_404").InInt64(10, []int64{10, 20, 30}), ShouldEqual, 10) - So(sec.Key("TIME_404").InTime(t, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String()) - }) - }) - - Convey("Get values in range", func() { - sec := cfg.Section("types") - So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25) - So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10) - So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10) - - minT, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z") - So(err, ShouldBeNil) - midT, err := time.Parse(time.RFC3339, "2013-01-01T01:00:00Z") - So(err, ShouldBeNil) - maxT, err := time.Parse(time.RFC3339, "9999-01-01T01:00:00Z") - So(err, ShouldBeNil) - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - So(sec.Key("TIME").RangeTime(t, minT, maxT).String(), ShouldEqual, t.String()) - - Convey("Get value in range with default value", func() { - So(sec.Key("FLOAT64").RangeFloat64(5, 0, 1), ShouldEqual, 5) - So(sec.Key("INT").RangeInt(7, 0, 5), ShouldEqual, 7) - So(sec.Key("INT").RangeInt64(7, 0, 5), ShouldEqual, 7) - So(sec.Key("TIME").RangeTime(t, minT, midT).String(), ShouldEqual, t.String()) - }) - }) - - Convey("Get values into slice", func() { - sec := cfg.Section("array") - So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de") - So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0) - - vals1 := sec.Key("FLOAT64S").Float64s(",") - for i, v := range []float64{1.1, 2.2, 3.3} { - So(vals1[i], ShouldEqual, v) - } - - vals2 := sec.Key("INTS").Ints(",") - for i, v := range []int{1, 2, 3} { - So(vals2[i], ShouldEqual, v) - } - - vals3 := sec.Key("INTS").Int64s(",") - for i, v := range []int64{1, 2, 3} { - So(vals3[i], ShouldEqual, v) - } - - t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z") - So(err, ShouldBeNil) - vals4 := sec.Key("TIMES").Times(",") - for i, v := range []time.Time{t, t, t} { - So(vals4[i].String(), ShouldEqual, v.String()) - } - }) - - Convey("Get key hash", func() { - cfg.Section("").KeysHash() - }) - - Convey("Set key value", func() { - k := cfg.Section("author").Key("NAME") - k.SetValue("无闻") - So(k.String(), ShouldEqual, "无闻") - }) - - Convey("Get key strings", func() { - So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,BOOL_FALSE,FLOAT64,INT,TIME") - }) - - Convey("Delete a key", func() { - cfg.Section("package.sub").DeleteKey("UNUSED_KEY") - _, err := cfg.Section("package.sub").GetKey("UNUSED_KEY") - So(err, ShouldNotBeNil) - }) - - Convey("Get section strings", func() { - So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,advance") - }) - - Convey("Delete a section", func() { - cfg.DeleteSection("") - So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION) - }) - - Convey("Create new sections", func() { - cfg.NewSections("test", "test2") - _, err := cfg.GetSection("test") - So(err, ShouldBeNil) - _, err = cfg.GetSection("test2") - So(err, ShouldBeNil) - }) - }) - - Convey("Test getting and setting bad values", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - Convey("Create new key with empty name", func() { - k, err := cfg.Section("").NewKey("", "") - So(err, ShouldNotBeNil) - So(k, ShouldBeNil) - }) - - Convey("Create new section with empty name", func() { - s, err := cfg.NewSection("") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - }) - - Convey("Create new sections with empty name", func() { - So(cfg.NewSections(""), ShouldNotBeNil) - }) - - Convey("Get section that not exists", func() { - s, err := cfg.GetSection("404") - So(err, ShouldNotBeNil) - So(s, ShouldBeNil) - - s = cfg.Section("404") - So(s, ShouldNotBeNil) - }) - }) -} - -func Test_File_Append(t *testing.T) { - Convey("Append data sources", t, func() { - cfg, err := Load([]byte("")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(cfg.Append([]byte(""), []byte("")), ShouldBeNil) - - Convey("Append bad data sources", func() { - So(cfg.Append(1), ShouldNotBeNil) - So(cfg.Append([]byte(""), 1), ShouldNotBeNil) - }) - }) -} - -func Test_File_SaveTo(t *testing.T) { - Convey("Save file", t, func() { - cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini") - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.Section("").Key("NAME").Comment = "Package name" - cfg.Section("author").Comment = `Information about package author -# Bio can be written in multiple lines.` - So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil) - }) -} - -func Benchmark_Key_Value(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} - -func Benchmark_Key_String(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_Value_NonBlock(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - c.BlockMode = false - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").Value() - } -} - -func Benchmark_Key_String_NonBlock(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - c.BlockMode = false - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").String() - } -} - -func Benchmark_Key_SetValue(b *testing.B) { - c, _ := Load([]byte(_CONF_DATA)) - for i := 0; i < b.N; i++ { - c.Section("").Key("NAME").SetValue("10") - } -} diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/struct.go b/Godeps/_workspace/src/gopkg.in/ini.v1/struct.go deleted file mode 100644 index 09ea816442b..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/struct.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "errors" - "fmt" - "reflect" - "time" - "unicode" -) - -// NameMapper represents a ini tag name mapper. -type NameMapper func(string) string - -// Built-in name getters. -var ( - // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. - AllCapsUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - } - newstr = append(newstr, unicode.ToUpper(chr)) - } - return string(newstr) - } - // TitleUnderscore converts to format title_underscore. - TitleUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - chr -= ('A' - 'a') - } - newstr = append(newstr, chr) - } - return string(newstr) - } -) - -func (s *Section) parseFieldName(raw, actual string) string { - if len(actual) > 0 { - return actual - } - if s.f.NameMapper != nil { - return s.f.NameMapper(raw) - } - return raw -} - -func parseDelim(actual string) string { - if len(actual) > 0 { - return actual - } - return "," -} - -var reflectTime = reflect.TypeOf(time.Now()).Kind() - -// setWithProperType sets proper value to field based on its type, -// but it does not return error for failing parsing, -// because we want to use default value that is already assigned to strcut. -func setWithProperType(kind reflect.Kind, key *Key, field reflect.Value, delim string) error { - switch kind { - case reflect.String: - if len(key.String()) == 0 { - return nil - } - field.SetString(key.String()) - case reflect.Bool: - boolVal, err := key.Bool() - if err != nil { - return nil - } - field.SetBool(boolVal) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - intVal, err := key.Int64() - if err != nil { - return nil - } - field.SetInt(intVal) - case reflect.Float64: - floatVal, err := key.Float64() - if err != nil { - return nil - } - field.SetFloat(floatVal) - case reflectTime: - timeVal, err := key.Time() - if err != nil { - return nil - } - field.Set(reflect.ValueOf(timeVal)) - case reflect.Slice: - vals := key.Strings(delim) - numVals := len(vals) - if numVals == 0 { - return nil - } - - sliceOf := field.Type().Elem().Kind() - - var times []time.Time - if sliceOf == reflectTime { - times = key.Times(delim) - } - - slice := reflect.MakeSlice(field.Type(), numVals, numVals) - for i := 0; i < numVals; i++ { - switch sliceOf { - case reflectTime: - slice.Index(i).Set(reflect.ValueOf(times[i])) - default: - slice.Index(i).Set(reflect.ValueOf(vals[i])) - } - } - field.Set(slice) - default: - return fmt.Errorf("unsupported type '%s'", kind) - } - return nil -} - -func (s *Section) mapTo(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - fieldName := s.parseFieldName(tpField.Name, tag) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - if tpField.Type.Kind() == reflect.Struct { - if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - continue - } - } else if tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous { - field.Set(reflect.New(tpField.Type.Elem())) - if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - continue - } - } - - if key, err := s.GetKey(fieldName); err == nil { - if err = setWithProperType(tpField.Type.Kind(), key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - } - } - return nil -} - -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val) -} - -// MapTo maps file to given struct. -func (f *File) MapTo(v interface{}) error { - return f.Section("").MapTo(v) -} - -// MapTo maps data sources to given struct with name mapper. -func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.MapTo(v) -} - -// MapTo maps data sources to given struct. -func MapTo(v, source interface{}, others ...interface{}) error { - return MapToWithMapper(v, nil, source, others...) -} diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/struct_test.go b/Godeps/_workspace/src/gopkg.in/ini.v1/struct_test.go deleted file mode 100644 index f6fad196ae0..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/struct_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "strings" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -type testNested struct { - Cities []string `delim:"|"` - Visits []time.Time - Note string - Unused int `ini:"-"` -} - -type testEmbeded struct { - GPA float64 -} - -type testStruct struct { - Name string `ini:"NAME"` - Age int - Male bool - Money float64 - Born time.Time - Others testNested - *testEmbeded `ini:"grade"` - Unused int `ini:"-"` -} - -const _CONF_DATA_STRUCT = ` -NAME = Unknwon -Age = 21 -Male = true -Money = 1.25 -Born = 1993-10-07T20:17:05Z - -[Others] -Cities = HangZhou|Boston -Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z -Note = Hello world! - -[grade] -GPA = 2.8 -` - -type unsupport struct { - Byte byte -} - -type unsupport2 struct { - Others struct { - Cities byte - } -} - -type unsupport3 struct { - Cities byte -} - -type unsupport4 struct { - *unsupport3 `ini:"Others"` -} - -type defaultValue struct { - Name string - Age int - Male bool - Money float64 - Born time.Time - Cities []string -} - -const _INVALID_DATA_CONF_STRUCT = ` -Name = -Age = age -Male = 123 -Money = money -Born = nil -Cities = -` - -func Test_Struct(t *testing.T) { - Convey("Map file to struct", t, func() { - ts := new(testStruct) - So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil) - - So(ts.Name, ShouldEqual, "Unknwon") - So(ts.Age, ShouldEqual, 21) - So(ts.Male, ShouldBeTrue) - So(ts.Money, ShouldEqual, 1.25) - - t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") - So(err, ShouldBeNil) - So(ts.Born.String(), ShouldEqual, t.String()) - - So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston") - So(ts.Others.Visits[0].String(), ShouldEqual, t.String()) - So(ts.Others.Note, ShouldEqual, "Hello world!") - So(ts.testEmbeded.GPA, ShouldEqual, 2.8) - }) - - Convey("Map to non-pointer struct", t, func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - So(cfg.MapTo(testStruct{}), ShouldNotBeNil) - }) - - Convey("Map to unsupported type", t, func() { - cfg, err := Load([]byte(_CONF_DATA_STRUCT)) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.NameMapper = func(raw string) string { - if raw == "Byte" { - return "NAME" - } - return raw - } - So(cfg.MapTo(&unsupport{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil) - So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil) - }) - - Convey("Map from invalid data source", t, func() { - So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil) - }) - - Convey("Map to wrong types and gain default values", t, func() { - cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT)) - So(err, ShouldBeNil) - - t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z") - So(err, ShouldBeNil) - dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}} - So(cfg.MapTo(dv), ShouldBeNil) - So(dv.Name, ShouldEqual, "Joe") - So(dv.Age, ShouldEqual, 10) - So(dv.Male, ShouldBeTrue) - So(dv.Money, ShouldEqual, 1.25) - So(dv.Born.String(), ShouldEqual, t.String()) - So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston") - }) -} - -type testMapper struct { - PackageName string -} - -func Test_NameGetter(t *testing.T) { - Convey("Test name mappers", t, func() { - So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil) - - cfg, err := Load([]byte("PACKAGE_NAME=ini")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - - cfg.NameMapper = AllCapsUnderscore - tg := new(testMapper) - So(cfg.MapTo(tg), ShouldBeNil) - So(tg.PackageName, ShouldEqual, "ini") - }) -} diff --git a/Godeps/_workspace/src/gopkg.in/ini.v1/testdata/conf.ini b/Godeps/_workspace/src/gopkg.in/ini.v1/testdata/conf.ini deleted file mode 100644 index 2ed0ac1d3ac..00000000000 --- a/Godeps/_workspace/src/gopkg.in/ini.v1/testdata/conf.ini +++ /dev/null @@ -1,2 +0,0 @@ -[author] -E-MAIL = u@gogs.io \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE b/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE deleted file mode 100644 index 37ec93a14fd..00000000000 --- a/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -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: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -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 -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 [yyyy] [name of copyright owner] - - 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. diff --git a/Gruntfile.js b/Gruntfile.js index 9a0c69b96f9..1f96048746b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,7 +9,6 @@ module.exports = function (grunt) { genDir: 'public_gen', destDir: 'dist', tempDir: 'tmp', - arch: os.arch(), platform: process.platform.replace('win32', 'windows'), }; @@ -17,6 +16,10 @@ module.exports = function (grunt) { config.arch = process.env.hasOwnProperty('ProgramFiles(x86)') ? 'x64' : 'x86'; } + config.arch = grunt.option('arch') || os.arch(); + + config.phjs = grunt.option('phjsToRelease'); + config.pkg.version = grunt.option('pkgVer') || config.pkg.version; console.log('Version', config.pkg.version); diff --git a/Makefile b/Makefile index 6cccaa7f64b..e335993b8b0 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ all: deps build deps: go run build.go setup - godep restore npm install build: @@ -10,7 +9,7 @@ build: npm run build test: - godep go test -v ./pkg/... + go test -v ./pkg/... npm test run: diff --git a/README.md b/README.md index f8d1db6cd07..98f4b4d3c9d 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,8 @@ the latest master builds [here](http://grafana.org/download/builds) ### Dependencies -- Go 1.6 +- Go 1.7 - NodeJS v4+ -- [Godep](https://github.com/tools/godep) ### Get Code @@ -97,14 +96,13 @@ easily the grafana repository you want to build. ```bash go get github.com/*your_account*/grafana mkdir $GOPATH/src/github.com/grafana -ln -s github.com/*your_account*/grafana $GOPATH/src/github.com/grafana/grafana +ln -s $GOPATH/src/github.com/*your_account*/grafana $GOPATH/src/github.com/grafana/grafana ``` ### Building the backend ```bash cd $GOPATH/src/github.com/grafana/grafana -go run build.go setup (only needed once to install godep) -godep restore (will pull down all golang lib dependencies in your current GOPATH) +go run build.go setup go run build.go build ``` @@ -126,7 +124,7 @@ grunt watch ``` ### Recompile backend on source change -To rebuild on source change (requires that you executed godep restore) +To rebuild on source change. ```bash go get github.com/Unknwon/bra bra run diff --git a/bower.json b/bower.json index 2624254acbe..c71b0b7dea0 100644 --- a/bower.json +++ b/bower.json @@ -13,11 +13,13 @@ "tests" ], "dependencies": { - "jquery": "~2.2.4", - "angular": "~1.5.3", - "angular-route": "~1.5.3", - "angular-mocks": "~1.5.3", - "angular-sanitize": "~1.5.3", - "angular-bindonce": "~0.3.3" + "jquery": "3.1.0", + "lodash": "4.15.0", + "angular": "1.5.8", + "angular-route": "1.5.8", + "angular-mocks": "1.5.8", + "angular-sanitize": "1.5.8", + "angular-native-dragdrop": "1.2.2", + "angular-bindonce": "0.3.3" } } diff --git a/build.go b/build.go index 9b8aae6a68a..202caa1837b 100644 --- a/build.go +++ b/build.go @@ -25,16 +25,21 @@ var ( versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`) goarch string goos string + gocc string + gocxx string + cgo string + pkgArch string version string = "v1" // deb & rpm does not support semver so have to handle their version a little differently linuxPackageVersion string = "v1" linuxPackageIteration string = "" race bool + phjsToRelease string workingDir string binaries []string = []string{"grafana-server", "grafana-cli"} ) -const minGoVersion = 1.6 +const minGoVersion = 1.7 func main() { log.SetOutput(os.Stdout) @@ -47,6 +52,11 @@ func main() { flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH") flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS") + flag.StringVar(&gocc, "cc", "", "CC") + flag.StringVar(&gocxx, "cxx", "", "CXX") + flag.StringVar(&cgo, "cgo-enabled", "", "CGO_ENABLED") + flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH") + flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary") flag.BoolVar(&race, "race", race, "Use race detector") flag.Parse() @@ -73,15 +83,15 @@ func main() { grunt("test") case "package": - grunt("release", fmt.Sprintf("--pkgVer=%v-%v", linuxPackageVersion, linuxPackageIteration)) + grunt(gruntBuildArg("release")...) createLinuxPackages() case "pkg-rpm": - grunt("release") + grunt(gruntBuildArg("release")...) createRpmPackages() case "pkg-deb": - grunt("release") + grunt(gruntBuildArg("release")...) createDebPackages() case "latest": @@ -258,6 +268,10 @@ func createPackage(options linuxPackageOptions) { "-p", "./dist", } + if pkgArch != "" { + args = append(args, "-a", pkgArch) + } + if linuxPackageIteration != "" { args = append(args, "--iteration", linuxPackageIteration) } @@ -307,11 +321,20 @@ func grunt(params ...string) { runPrint("./node_modules/.bin/grunt", params...) } +func gruntBuildArg(task string) []string { + args := []string{task, fmt.Sprintf("--pkgVer=%v-%v", linuxPackageVersion, linuxPackageIteration)} + if pkgArch != "" { + args = append(args, fmt.Sprintf("--arch=%v", pkgArch)) + } + if phjsToRelease != "" { + args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease)) + } + return args +} + func setup() { - runPrint("go", "get", "-v", "github.com/tools/godep") - runPrint("go", "get", "-v", "github.com/blang/semver") - runPrint("go", "get", "-v", "github.com/mattn/go-sqlite3") - runPrint("go", "install", "-v", "github.com/mattn/go-sqlite3") + runPrint("go", "get", "-v", "github.com/kardianos/govendor") + runPrint("go", "install", "-v", "./pkg/cmd/grafana-server") } func test(pkg string) { @@ -366,7 +389,6 @@ func rmr(paths ...string) { } func clean() { - rmr("bin", "Godeps/_workspace/pkg", "Godeps/_workspace/bin") rmr("dist") rmr("tmp") rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/github.com/grafana", goos, goarch))) @@ -383,13 +405,15 @@ func setBuildEnv() { if goarch == "386" { os.Setenv("GO386", "387") } - wd, err := os.Getwd() - if err != nil { - log.Println("Warning: can't determine current dir:", err) - log.Println("Build might not work as expected") + if cgo != "" { + os.Setenv("CGO_ENABLED", cgo) + } + if gocc != "" { + os.Setenv("CC", gocc) + } + if gocxx != "" { + os.Setenv("CXX", gocxx) } - os.Setenv("GOPATH", fmt.Sprintf("%s%c%s", filepath.Join(wd, "Godeps", "_workspace"), os.PathListSeparator, os.Getenv("GOPATH"))) - log.Println("GOPATH=" + os.Getenv("GOPATH")) } func getGitSha() string { diff --git a/circle.yml b/circle.yml index 5640980b702..007d0b29217 100644 --- a/circle.yml +++ b/circle.yml @@ -5,30 +5,26 @@ machine: GOPATH: "/home/ubuntu/.go_workspace" ORG_PATH: "github.com/grafana" REPO_PATH: "${ORG_PATH}/grafana" + GODIST: "go1.7.1.linux-amd64.tar.gz" + post: + - mkdir -p download + - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST + - sudo rm -rf /usr/local/go + - sudo tar -C /usr/local -xzf download/$GODIST dependencies: override: - rm -rf ${GOPATH}/src/${REPO_PATH} - mkdir -p ${GOPATH}/src/${ORG_PATH} - - ln -s ~/grafana ${GOPATH}/src/${ORG_PATH} - - go get github.com/tools/godep - - rm -rf node_modules - - npm install -g npm - - npm install + - cp -r ~/grafana ${GOPATH}/src/${ORG_PATH} test: override: - - test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" - - go vet ./pkg/... - # JS tests - - npm test - - npm run coveralls - # GO tests - - godep go test -v ./pkg/... + - bash scripts/circle-test.sh deployment: master: branch: master owner: grafana commands: - - ./trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} \ No newline at end of file + - ./scripts/trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} diff --git a/conf/defaults.ini b/conf/defaults.ini index 2219b56a77d..49329a0a4ac 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -59,7 +59,7 @@ cert_key = #################################### Database #################################### [database] -# You can configure the database connection by specifying type, host, name, user and password +# You can configure the database connection by specifying type, host, name, user and password # as seperate properties or as on string using the url propertie. # Either "mysql", "postgres" or "sqlite3", it's your choice @@ -223,6 +223,19 @@ token_url = https://accounts.google.com/o/oauth2/token api_url = https://www.googleapis.com/oauth2/v1/userinfo allowed_domains = +#################################### Generic OAuth ########################## +[auth.generic_oauth] +enabled = false +allow_sign_up = false +client_id = some_id +client_secret = some_secret +scopes = user:email +auth_url = +token_url = +api_url = +team_ids = +allowed_organizations = + #################################### Basic Auth ########################## [auth.basic] enabled = true @@ -372,7 +385,7 @@ global_session = -1 # \______(_______;;;)__;;;) [alerting] -enabled = false +enabled = true #################################### Internal Grafana Metrics ########################## # Metrics available at HTTP API Url /api/metrics @@ -391,7 +404,7 @@ url = https://grafana.net #################################### External image storage ########################## [external_image_storage] -# You can choose between (s3, webdav or internal) +# You can choose between (s3, webdav) provider = s3 [external_image_storage.s3] diff --git a/conf/ldap.toml b/conf/ldap.toml index 395179e219f..812cd582c91 100644 --- a/conf/ldap.toml +++ b/conf/ldap.toml @@ -8,6 +8,8 @@ host = "127.0.0.1" port = 389 # Set to true if ldap server supports TLS use_ssl = false +# Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS) +start_tls = false # set to true if you want to skip ssl cert validation ssl_skip_verify = false # set to the path to your root CA certificate or leave unset to use system defaults diff --git a/conf/sample.ini b/conf/sample.ini index 949f64df025..2c428ea775f 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -61,7 +61,7 @@ #################################### Database #################################### [database] -# You can configure the database connection by specifying type, host, name, user and password +# You can configure the database connection by specifying type, host, name, user and password # as seperate properties or as on string using the url propertie. # Either "mysql", "postgres" or "sqlite3", it's your choice @@ -205,6 +205,19 @@ check_for_updates = true ;api_url = https://www.googleapis.com/oauth2/v1/userinfo ;allowed_domains = +#################################### Generic OAuth ########################## +[auth.generic_oauth] +;enabled = false +;allow_sign_up = false +;client_id = some_id +;client_secret = some_secret +;scopes = user:email,read:org +;auth_url = https://foo.bar/login/oauth/authorize +;token_url = https://foo.bar/login/oauth/access_token +;api_url = https://foo.bar/user +;team_ids = +;allowed_organizations = + #################################### Auth Proxy ########################## [auth.proxy] ;enabled = false diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e4a528dcdbd..e8a397328a1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -42,6 +42,7 @@ pages: - ['installation/performance.md', 'Installation', 'Performance Tips'] - ['installation/troubleshooting.md', 'Installation', 'Troubleshooting'] - ['installation/migrating_to2.md', 'Installation', 'Migrating from v1.x to v2.x'] +- ['installation/behind_proxy.md', 'Installation', 'Grafana behind reverse proxy'] - ['guides/basic_concepts.md', 'User Guides', 'Basic Concepts'] - ['guides/gettingstarted.md', 'User Guides', 'Getting Started'] diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index ba1a0923861..fb1b0659cd9 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -6,6 +6,10 @@ page_keywords: grafana, admin, http, api, documentation # Admin API +The admin http API does not currently work with an api token. Api Token's are currently only linked to an organization and organization role. They cannot given +the permission of server admin, only user's can be given that permission. So in order to use these API calls you will have to use basic auth and Grafana user +with Grafana admin permission. + ## Settings `GET /api/admin/settings` @@ -15,7 +19,6 @@ page_keywords: grafana, admin, http, api, documentation GET /api/admin/settings Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk **Example Response**: @@ -171,7 +174,6 @@ page_keywords: grafana, admin, http, api, documentation GET /api/admin/stats Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk **Example Response**: @@ -201,7 +203,6 @@ Create new user POST /api/admin/users HTTP/1.1 Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk { "name":"User", @@ -228,7 +229,6 @@ Change password for specific user PUT /api/admin/users/2/password HTTP/1.1 Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk **Example Response**: @@ -246,7 +246,6 @@ Change password for specific user PUT /api/admin/users/2/permissions HTTP/1.1 Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk **Example Response**: @@ -264,7 +263,6 @@ Change password for specific user DELETE /api/admin/users/2 HTTP/1.1 Accept: application/json Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk **Example Response**: diff --git a/docs/sources/installation/behind_proxy.md b/docs/sources/installation/behind_proxy.md new file mode 100644 index 00000000000..0cf2507b404 --- /dev/null +++ b/docs/sources/installation/behind_proxy.md @@ -0,0 +1,64 @@ +--- +page_title: Running Grafana behind a reverse proxy +page_description: Guide for running Grafana behind a reverse proxy +page_keywords: Grafana, reverse proxy, nginx, haproxy +--- + +# Running Grafana behind a reverse proxy + +It should be straight forward to get Grafana up and running behind a reverse proxy. But here are some things that you might run into. + +Links and redirects will not be rendered correctly unless you set the server.domain setting. +``` +[server] +domain = foo.bar +``` + +To use sub *path* ex `http://foo.bar/grafana` make sure to include `/grafana` in the end of root_url. +Otherwise Grafana will not behave correctly. See example below. + +# Examples +Here are some example configurations for running Grafana behind a reverse proxy. + +## Grafana configuration (ex http://foo.bar.com) +``` +[server] +domain = foo.bar +``` + +## Nginx configuration +``` +server { + listen 80; + root /usr/share/nginx/www; + index index.html index.htm; + + location / { + proxy_pass http://localhost:3000/; + } +} +``` + +# Examples with **sub path** (ex http://foo.bar.com/grafana) + +## Grafana configuration with sub path +``` +[server] +domain = foo.bar +root_url = %(protocol)s://%(domain)s:/grafana +``` + +## Nginx configuration with sub path +``` +server { + listen 80; + root /usr/share/nginx/www; + index index.html index.htm; + + location /grafana/ { + proxy_pass http://localhost:3000/; + } +} +``` + + diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 4bb4d262d46..4c7f63d53ae 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -276,9 +276,10 @@ example: scopes = user:email auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token - api_url = https://api.github.com + api_url = https://api.github.com/user allow_sign_up = false team_ids = + allowed_organizations = Restart the Grafana back-end. You should now see a GitHub login button on the login page. You can now login or sign up with your GitHub @@ -306,6 +307,24 @@ Grafana instance. For example: token_url = https://github.com/login/oauth/access_token allow_sign_up = false +### allowed_organizations + +Require an active organization membership for at least one of the given +organizations on GitHub. If the authenticated user isn't a member of at least +one of the organizations they will not be able to register or authenticate with +your Grafana instance. For example + + [auth.github] + enabled = true + client_id = YOUR_GITHUB_APP_CLIENT_ID + client_secret = YOUR_GITHUB_APP_CLIENT_SECRET + scopes = user:email,read:org + auth_url = https://github.com/login/oauth/authorize + token_url = https://github.com/login/oauth/access_token + allow_sign_up = false + # space-delimited organization names + allowed_organizations = github google +
## [auth.google] @@ -341,6 +360,23 @@ You may allow users to sign-up via Google authentication by setting the user successfully authenticating via Google authentication will be automatically signed up. +## [auth.generic_oauth] + +This option could be used if have your own oauth service. + +This callback URL must match the full HTTP address that you use in your +browser to access Grafana, but with the prefix path of `/login/generic_oauth`. + + [auth.generic_oauth] + enabled = true + client_id = YOUR_APP_CLIENT_ID + client_secret = YOUR_APP_CLIENT_SECRET + scopes = + auth_url = + token_url = + allowed_domains = mycompany.com mycompany.org + allow_sign_up = false +
## [auth.basic] @@ -392,7 +428,7 @@ session provider you have configured. - **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name` - **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=disable - **memcache:** ex: 127.0.0.1:11211 -- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,db=grafana` +- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,prefix=grafana` If you use MySQL or Postgres as the session store you need to create the session table manually. @@ -489,4 +525,3 @@ Set root url to a Grafana instance where you want to publish external snapshots ### external_snapshot_name Set name for external snapshot button. Defaults to `Publish to snapshot.raintank.io` - diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 363a4759cce..7498cab9c48 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -68,7 +68,7 @@ is `3000` and default user and group is `admin`. To configure the Grafana server to start at boot time: - $ sudo update-rc.d grafana-server defaults 95 10 + $ sudo update-rc.d grafana-server defaults ## Start the server (via systemd) diff --git a/docs/sources/installation/ldap.md b/docs/sources/installation/ldap.md index a5311fb4fa5..8002c045d82 100644 --- a/docs/sources/installation/ldap.md +++ b/docs/sources/installation/ldap.md @@ -27,6 +27,8 @@ host = "127.0.0.1" port = 389 # Set to true if ldap server supports TLS use_ssl = false +# Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS) +start_tls = false # set to true if you want to skip ssl cert validation ssl_skip_verify = false # set to the path to your root CA certificate or leave unset to use system defaults diff --git a/docs/sources/project/building_from_source.md b/docs/sources/project/building_from_source.md index e4f35f0a49b..40f963b237c 100644 --- a/docs/sources/project/building_from_source.md +++ b/docs/sources/project/building_from_source.md @@ -11,7 +11,7 @@ dev environment. Grafana ships with its own required backend server; also comple ## Dependencies -- [Go 1.5](https://golang.org/dl/) +- [Go 1.7](https://golang.org/dl/) - [NodeJS](https://nodejs.org/download/) ## Get Code @@ -24,9 +24,8 @@ go get github.com/grafana/grafana ## Building the backend ``` cd $GOPATH/src/github.com/grafana/grafana -go run build.go setup # (only needed once to install godep) -$GOPATH/bin/godep restore # (will pull down all golang lib dependencies in your current GOPATH) -go run build.go build # (or 'go build .') +go run build.go setup +go run build.go build # (or 'go build ./pkg/cmd/grafana-server') ``` #### Building on Windows @@ -45,7 +44,7 @@ grunt ``` ## Recompile backend on source change -To rebuild on source change (requires that you executed godep restore) +To rebuild on source change ``` go get github.com/Unknwon/bra bra run @@ -63,9 +62,10 @@ If you built it with `go build .`, run `./grafana` Open grafana in your browser (default [http://localhost:3000](http://localhost:3000)) and login with admin user (default user/pass = admin/admin). ## Developing for Grafana -To add features, customize your config, etc, you'll need to rebuild on source change (requires that you executed [godep restore](#build-the-backend), as outlined above). +To add features, customize your config, etc, you'll need to rebuild on source change. ``` go get github.com/Unknwon/bra + bra run ``` You'll also need to run `grunt watch` to watch for changes to the front-end. diff --git a/emails/assets/css/ink.css b/emails/assets/css/ink.css index e65aed19194..f4c1a291cb3 100644 --- a/emails/assets/css/ink.css +++ b/emails/assets/css/ink.css @@ -17,6 +17,8 @@ body{ padding:0; } + + .ExternalClass { width:100%; } @@ -42,7 +44,6 @@ img { text-decoration:none; -ms-interpolation-mode: bicubic; width: auto; - max-width: 100%; float: left; clear: both; display: block; @@ -581,8 +582,7 @@ body.outlook p { @media only screen and (max-width: 600px) { table[class="body"] img { - width: auto !important; - height: auto !important; + } table[class="body"] center { diff --git a/emails/assets/css/style.css b/emails/assets/css/style.css index f138a0c1296..83f8d2c67a6 100644 --- a/emails/assets/css/style.css +++ b/emails/assets/css/style.css @@ -9,7 +9,8 @@ h1 {font-size: 40px;} h2 {font-size: 36px;} h3 { font-size: 22px; - margin-top: 20px; + margin-top: 10px; + margin-bottom: 10px; } h4 {font-size: 20px;} h5 {font-size: 18px;} @@ -79,10 +80,17 @@ table.google-plus:hover td { } .header { - background: #333; +margin-top:25px; +margin-bottom: 25px; +} + +.data { + font-size: 16px; } .footer { + background-color: #2e2e2e; + color: #999999; margin-top: 20px; } @@ -94,6 +102,10 @@ table.google-plus:hover td { table[class="body"] .left-text-pad { padding-right: 10px !important; } + + .logo { + margin-left: 10px; + } } table.better-button { @@ -134,6 +146,45 @@ table.columns td.better-button { color: #FFFFFF !important; } +table.better-button-alt { + margin-top: 10px; + margin-bottom: 20px; +} + +table.columns td.better-button-alt { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + padding-bottom: 0px; +} + +.better-button-alt a { + text-decoration: none; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + + padding: 12px 25px; + border: 1px solid #ff8f2b; + background-color: #EFEFEF; + display: inline-block; + color: #ff8f2b; +} + +.better-button-alt:hover a { + color: #ff8f2b !important; + background-color: #DDDDDD; + border: 1px solid #F2821E; +} + +.better-button-alt:visited a { + color: #ff8f2b !important; +} + +.better-button-alt:active a { + color: #ff8f2b !important; +} + .verification-code { background-color: #EEEEEE; padding: 3px; diff --git a/emails/templates/alert_notification.html b/emails/templates/alert_notification.html index 5adcda1a620..2c9f78ce6fb 100644 --- a/emails/templates/alert_notification.html +++ b/emails/templates/alert_notification.html @@ -1,91 +1,106 @@ [[Subject .Subject "[[.Title]]"]] - - - + + +
- - - - -
-

[[.Title]]

-
-
+ + + + +
+

[[.Title]]

+
+
- - - + + +
- - - - -
-

[[.Message]]

-
-
+ + + + +
+

[[.Message]]

+
+
[[if ne .State "ok" ]] - - - + + +
-
- - - - - - [[range .EvalMatches]] - - - - - [[end]] -
- Metric name - - Value -
- [[.Metric]] - - [[.Value]] -
-
-
+
+ + + + + + [[range .EvalMatches]] + + + + + [[end]] +
+
Metric name
+
+
Value
+
+
[[.Metric]]
+
+
[[.Value]]
+
+
+
[[end]] - - + +
- - - - -
- -
-
+ + + + +
+ +
+
- - - + + +
- - - - -
- Alert rule - Alerts page -
-
+ + + + + +
+ + + + +
+ View your Alert rule +
+
+ + + + +
+ Go to the Alerts page +
+
+
diff --git a/emails/templates/invited_to_org.html b/emails/templates/invited_to_org.html index 59e76cd5a77..681830604b9 100644 --- a/emails/templates/invited_to_org.html +++ b/emails/templates/invited_to_org.html @@ -9,7 +9,7 @@ @@ -25,7 +25,7 @@
-

You have been added to [[.OrgName]]

+

You have been added to [[.OrgName]]

diff --git a/emails/templates/layouts/default.html b/emails/templates/layouts/default.html index 0fd5b35ea48..f6d729054cd 100644 --- a/emails/templates/layouts/default.html +++ b/emails/templates/layouts/default.html @@ -3,77 +3,160 @@ - - - - + + + + - -
-

[[.InvitedBy]] has added you to the [[.OrgName]] organization in Grafana. +

[[.InvitedBy]] has added you to the [[.OrgName]] organization in Grafana.

Once logged in, [[.OrgName]] will be available in the left side menu, in the dropdown below your username.

+ + +
-
-
+
- - - - -
-
- - - - - -
- - - - - - -
- -
- -
- -
-
- - +
- + +
- {{> body }} - - - - - - + +
+ + + + +
+ + + + + + +
+ +
+ +
+ +
+
+ + + + + + + - +
+ {{> body }}
+ + + + +
diff --git a/emails/templates/new_user_invite.html b/emails/templates/new_user_invite.html index 86948a665f6..6e75a203953 100644 --- a/emails/templates/new_user_invite.html +++ b/emails/templates/new_user_invite.html @@ -9,7 +9,7 @@ @@ -25,7 +25,7 @@
-

You're invited to join [[.OrgName]]

+

You're invited to join [[.OrgName]]

diff --git a/emails/templates/reset_password.html b/emails/templates/reset_password.html index f3aca5da95e..e9d1527116c 100644 --- a/emails/templates/reset_password.html +++ b/emails/templates/reset_password.html @@ -7,7 +7,7 @@
-

You've been invited to join the [[.OrgName]] organization by [[.InvitedBy]]. To accept your invitation and join the team, please click the link below:

+

You've been invited to join the [[.OrgName]] organization by [[.InvitedBy]]. To accept your invitation and join the team, please click the link below:

@@ -24,7 +24,7 @@ @@ -29,6 +34,13 @@ + + +
-

Hi [[.Name]]

+

Hi [[.Name]],

- Please click the following link to reset your password within [[.EmailCodeValidHours]] hours. + Please click the following link to reset your password within [[.EmailCodeValidHours]] hours.

[[.AppUrl]]user/password/reset?code=[[.Code]] diff --git a/emails/templates/signup_started.html b/emails/templates/signup_started.html index 39b369a1f7c..15689a0e62c 100644 --- a/emails/templates/signup_started.html +++ b/emails/templates/signup_started.html @@ -7,7 +7,7 @@ diff --git a/emails/templates/welcome_on_signup.html b/emails/templates/welcome_on_signup.html index b93c56b77b1..0e0e4dbe3bc 100644 --- a/emails/templates/welcome_on_signup.html +++ b/emails/templates/welcome_on_signup.html @@ -7,10 +7,15 @@
-

Complete the signup

+

Complete the signup

+ + +
-

Hi [[.Name]]

+

Hi [[.Name]],

+ Welcome! Ready to start building some beautiful metric and analytic dashboards? +

+ Thank you for joining our community. +
+

The Grafana Team

+
diff --git a/packaging/deb/systemd/grafana-server.service b/packaging/deb/systemd/grafana-server.service index e87635bb3de..ea7193c55a6 100644 --- a/packaging/deb/systemd/grafana-server.service +++ b/packaging/deb/systemd/grafana-server.service @@ -1,5 +1,5 @@ [Unit] -Description=Starts and stops a single grafana instance on this system +Description=Grafana instance Documentation=http://docs.grafana.org Wants=network-online.target After=network-online.target diff --git a/packaging/rpm/systemd/grafana-server.service b/packaging/rpm/systemd/grafana-server.service index d79c5987576..ec0ef7e804e 100644 --- a/packaging/rpm/systemd/grafana-server.service +++ b/packaging/rpm/systemd/grafana-server.service @@ -1,5 +1,5 @@ [Unit] -Description=Starts and stops a single grafana instance on this system +Description=Grafana instance Documentation=http://docs.grafana.org Wants=network-online.target After=network-online.target diff --git a/pkg/Godeps/Godeps.json b/pkg/Godeps/Godeps.json deleted file mode 100644 index 5cff21cdba5..00000000000 --- a/pkg/Godeps/Godeps.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ImportPath": "github.com/grafana/grafana/pkg", - "GoVersion": "go1.6", - "GodepVersion": "v60", - "Packages": [ - "./pkg/..." - ], - "Deps": [] -} diff --git a/pkg/Godeps/Readme b/pkg/Godeps/Readme deleted file mode 100644 index 4cdaa53d56d..00000000000 --- a/pkg/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 80b5108bbad..1233e09d504 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -8,7 +8,6 @@ import ( "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/alerting" - "github.com/grafana/grafana/pkg/services/annotations" ) func ValidateOrgAlert(c *middleware.Context) { @@ -26,13 +25,18 @@ func ValidateOrgAlert(c *middleware.Context) { } } -// GET /api/alerts/rules/ +// GET /api/alerts func GetAlerts(c *middleware.Context) Response { query := models.GetAlertsQuery{ OrgId: c.OrgId, - State: c.QueryStrings("state"), DashboardId: c.QueryInt64("dashboardId"), PanelId: c.QueryInt64("panelId"), + Limit: c.QueryInt64("limit"), + } + + states := c.QueryStrings("state") + if len(states) > 0 { + query.State = states } if err := bus.Dispatch(&query); err != nil { @@ -50,7 +54,6 @@ func GetAlerts(c *middleware.Context) Response { Name: alert.Name, Message: alert.Message, State: alert.State, - Severity: alert.Severity, EvalDate: alert.EvalDate, NewStateDate: alert.NewStateDate, ExecutionError: alert.ExecutionError, @@ -147,7 +150,7 @@ func DelAlert(c *middleware.Context) Response { } func GetAlertNotifications(c *middleware.Context) Response { - query := &models.GetAlertNotificationsQuery{OrgId: c.OrgId} + query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId} if err := bus.Dispatch(query); err != nil { return ApiError(500, "Failed to get alert notifications", err) @@ -157,11 +160,12 @@ func GetAlertNotifications(c *middleware.Context) Response { for _, notification := range query.Result { result = append(result, dtos.AlertNotification{ - Id: notification.Id, - Name: notification.Name, - Type: notification.Type, - Created: notification.Created, - Updated: notification.Updated, + Id: notification.Id, + Name: notification.Name, + Type: notification.Type, + IsDefault: notification.IsDefault, + Created: notification.Created, + Updated: notification.Updated, }) } @@ -178,7 +182,7 @@ func GetAlertNotificationById(c *middleware.Context) Response { return ApiError(500, "Failed to get alert notifications", err) } - return Json(200, query.Result[0]) + return Json(200, query.Result) } func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotificationCommand) Response { @@ -214,40 +218,19 @@ func DeleteAlertNotification(c *middleware.Context) Response { return ApiSuccess("Notification deleted") } -func GetAlertHistory(c *middleware.Context) Response { - alertId, err := getAlertIdForRequest(c) - if err != nil { - return ApiError(400, "Invalid request", err) +//POST /api/alert-notifications/test +func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response { + cmd := &alerting.NotificationTestCommand{ + Name: dto.Name, + Type: dto.Type, + Settings: dto.Settings, } - query := &annotations.ItemQuery{ - AlertId: alertId, - Type: annotations.AlertType, - OrgId: c.OrgId, - Limit: c.QueryInt64("limit"), + if err := bus.Dispatch(cmd); err != nil { + return ApiError(500, "Failed to send alert notifications", err) } - repo := annotations.GetRepository() - - items, err := repo.Find(query) - if err != nil { - return ApiError(500, "Failed to get history for alert", err) - } - - var result []dtos.AlertHistory - for _, item := range items { - result = append(result, dtos.AlertHistory{ - AlertId: item.AlertId, - Timestamp: item.Timestamp, - Data: item.Data, - NewState: item.NewState, - Text: item.Text, - Metric: item.Metric, - Title: item.Title, - }) - } - - return Json(200, result) + return ApiSuccess("Test notification sent") } func getAlertIdForRequest(c *middleware.Context) (int64, error) { diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go new file mode 100644 index 00000000000..2803aa46435 --- /dev/null +++ b/pkg/api/annotations.go @@ -0,0 +1,46 @@ +package api + +import ( + "github.com/grafana/grafana/pkg/api/dtos" + "github.com/grafana/grafana/pkg/middleware" + "github.com/grafana/grafana/pkg/services/annotations" +) + +func GetAnnotations(c *middleware.Context) Response { + + query := &annotations.ItemQuery{ + From: c.QueryInt64("from") / 1000, + To: c.QueryInt64("to") / 1000, + Type: annotations.ItemType(c.Query("type")), + OrgId: c.OrgId, + AlertId: c.QueryInt64("alertId"), + DashboardId: c.QueryInt64("dashboardId"), + PanelId: c.QueryInt64("panelId"), + Limit: c.QueryInt64("limit"), + NewState: c.QueryStrings("newState"), + } + + repo := annotations.GetRepository() + + items, err := repo.Find(query) + if err != nil { + return ApiError(500, "Failed to get annotations", err) + } + + result := make([]dtos.Annotation, 0) + + for _, item := range items { + result = append(result, dtos.Annotation{ + AlertId: item.AlertId, + Time: item.Epoch * 1000, + Data: item.Data, + NewState: item.NewState, + PrevState: item.PrevState, + Text: item.Text, + Metric: item.Metric, + Title: item.Title, + }) + } + + return Json(200, result) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 132c609dde4..35b667c55be 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -58,6 +58,7 @@ func Register(r *macaron.Macaron) { r.Get("/plugins/:id/page/:page", reqSignedIn, Index) r.Get("/dashboard/*", reqSignedIn, Index) + r.Get("/dashboard-solo/snapshot/*", Index) r.Get("/dashboard-solo/*", reqSignedIn, Index) r.Get("/import/dashboard", reqSignedIn, Index) r.Get("/dashboards/*", reqSignedIn, Index) @@ -254,17 +255,18 @@ func Register(r *macaron.Macaron) { r.Get("/", wrap(GetAlerts)) }) - r.Get("/alert-history", wrap(GetAlertHistory)) - r.Get("/alert-notifications", wrap(GetAlertNotifications)) r.Group("/alert-notifications", func() { + r.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest)) r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification)) r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification)) r.Get("/:notificationId", wrap(GetAlertNotificationById)) r.Delete("/:notificationId", wrap(DeleteAlertNotification)) }, reqOrgAdmin) + r.Get("/annotations", wrap(GetAnnotations)) + // error test r.Get("/metrics/error", wrap(GenerateError)) diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 4721924cff4..4d5fa1f52db 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -67,25 +67,29 @@ func init() { "CoreNodesRunning", "CoreNodesPending", "LiveDataNodes", "MRTotalNodes", "MRActiveNodes", "MRLostNodes", "MRUnhealthyNodes", "MRDecommissionedNodes", "MRRebootedNodes", "S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB", "HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"}, - "AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"}, - "AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"}, - "AWS/Kinesis": {"GetRecords.Bytes", "GetRecords.IteratorAge", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Records", "GetRecords.Success", "IncomingBytes", "IncomingRecords", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "ReadProvisionedThroughputExceeded", "WriteProvisionedThroughputExceeded", "IteratorAgeMilliseconds", "OutgoingBytes", "OutgoingRecords"}, - "AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"}, - "AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"}, - "AWS/ML": {"PredictCount", "PredictFailureCount"}, - "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, - "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"}, - "AWS/RDS": {"BinLogDiskUsage", "CPUUtilization", "CPUCreditUsage", "CPUCreditBalance", "DatabaseConnections", "DiskQueueDepth", "FreeableMemory", "FreeStorageSpace", "ReplicaLag", "SwapUsage", "ReadIOPS", "WriteIOPS", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "NetworkReceiveThroughput", "NetworkTransmitThroughput"}, - "AWS/Route53": {"HealthCheckStatus", "HealthCheckPercentageHealthy", "ConnectionTime", "SSLHandshakeTime", "TimeToFirstByte"}, - "AWS/S3": {"BucketSizeBytes", "NumberOfObjects"}, - "AWS/SNS": {"NumberOfMessagesPublished", "PublishSize", "NumberOfNotificationsDelivered", "NumberOfNotificationsFailed"}, - "AWS/SQS": {"NumberOfMessagesSent", "SentMessageSize", "NumberOfMessagesReceived", "NumberOfEmptyReceives", "NumberOfMessagesDeleted", "ApproximateNumberOfMessagesDelayed", "ApproximateNumberOfMessagesVisible", "ApproximateNumberOfMessagesNotVisible"}, + "AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"}, + "AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"}, + "AWS/Firehose": {"DeliveryToElasticsearch.Bytes", "DeliveryToElasticsearch.Records", "DeliveryToElasticsearch.Success", "DeliveryToRedshift.Bytes", "DeliveryToRedshift.Records", "DeliveryToRedshift.Success", "DeliveryToS3.Bytes", "DeliveryToS3.DataFreshness", "DeliveryToS3.Records", "DeliveryToS3.Success", "IncomingBytes", "IncomingRecords", "DescribeDeliveryStream.Latency", "DescribeDeliveryStream.Requests", "ListDeliveryStreams.Latency", "ListDeliveryStreams.Requests", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Requests", "PutRecordBatch.Bytes", "PutRecordBatch.Latency", "PutRecordBatch.Records", "PutRecordBatch.Requests", "UpdateDeliveryStream.Latency", "UpdateDeliveryStream.Requests"}, + "AWS/IoT": {"PublishIn.Success", "PublishOut.Success", "Subscribe.Success", "Ping.Success", "Connect.Success", "GetThingShadow.Accepted"}, + "AWS/Kinesis": {"GetRecords.Bytes", "GetRecords.IteratorAge", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Records", "GetRecords.Success", "IncomingBytes", "IncomingRecords", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "ReadProvisionedThroughputExceeded", "WriteProvisionedThroughputExceeded", "IteratorAgeMilliseconds", "OutgoingBytes", "OutgoingRecords"}, + "AWS/KinesisAnalytics": {"Bytes", "MillisBehindLatest", "Records", "Success"}, + "AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"}, + "AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"}, + "AWS/ML": {"PredictCount", "PredictFailureCount"}, + "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, + "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"}, + "AWS/RDS": {"BinLogDiskUsage", "CPUUtilization", "CPUCreditUsage", "CPUCreditBalance", "DatabaseConnections", "DiskQueueDepth", "FreeableMemory", "FreeStorageSpace", "ReplicaLag", "SwapUsage", "ReadIOPS", "WriteIOPS", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "NetworkReceiveThroughput", "NetworkTransmitThroughput"}, + "AWS/Route53": {"HealthCheckStatus", "HealthCheckPercentageHealthy", "ConnectionTime", "SSLHandshakeTime", "TimeToFirstByte"}, + "AWS/S3": {"BucketSizeBytes", "NumberOfObjects"}, + "AWS/SNS": {"NumberOfMessagesPublished", "PublishSize", "NumberOfNotificationsDelivered", "NumberOfNotificationsFailed"}, + "AWS/SQS": {"NumberOfMessagesSent", "SentMessageSize", "NumberOfMessagesReceived", "NumberOfEmptyReceives", "NumberOfMessagesDeleted", "ApproximateNumberOfMessagesDelayed", "ApproximateNumberOfMessagesVisible", "ApproximateNumberOfMessagesNotVisible"}, "AWS/StorageGateway": {"CacheHitPercent", "CachePercentUsed", "CachePercentDirty", "CloudBytesDownloaded", "CloudDownloadLatency", "CloudBytesUploaded", "UploadBufferFree", "UploadBufferPercentUsed", "UploadBufferUsed", "QueuedWrites", "ReadBytes", "ReadTime", "TotalCacheSize", "WriteBytes", "WriteTime", "TimeSinceLastRecoveryPoint", "WorkingStorageFree", "WorkingStoragePercentUsed", "WorkingStorageUsed", "CacheHitPercent", "CachePercentUsed", "CachePercentDirty", "ReadBytes", "ReadTime", "WriteBytes", "WriteTime", "QueuedWrites"}, "AWS/SWF": {"DecisionTaskScheduleToStartTime", "DecisionTaskStartToCloseTime", "DecisionTasksCompleted", "StartedDecisionTasksTimedOutOnClose", "WorkflowStartToCloseTime", "WorkflowsCanceled", "WorkflowsCompleted", "WorkflowsContinuedAsNew", "WorkflowsFailed", "WorkflowsTerminated", "WorkflowsTimedOut", "ActivityTaskScheduleToCloseTime", "ActivityTaskScheduleToStartTime", "ActivityTaskStartToCloseTime", "ActivityTasksCanceled", "ActivityTasksCompleted", "ActivityTasksFailed", "ScheduledActivityTasksTimedOutOnClose", "ScheduledActivityTasksTimedOutOnStart", "StartedActivityTasksTimedOutOnClose", "StartedActivityTasksTimedOutOnHeartbeat"}, "AWS/WAF": {"AllowedRequests", "BlockedRequests", "CountedRequests"}, "AWS/WorkSpaces": {"Available", "Unhealthy", "ConnectionAttempt", "ConnectionSuccess", "ConnectionFailure", "SessionLaunchTime", "InSessionLatency", "SessionDisconnect"}, + "KMS": {"SecondsUntilKeyMaterialExpiration"}, } dimensionsMap = map[string][]string{ "AWS/ApiGateway": {"ApiName", "Method", "Resource", "Stage"}, @@ -106,7 +110,10 @@ func init() { "AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"}, "AWS/ES": {"ClientId", "DomainName"}, "AWS/Events": {"RuleName"}, + "AWS/Firehose": {}, + "AWS/IoT": {"Protocol"}, "AWS/Kinesis": {"StreamName", "ShardID"}, + "AWS/KinesisAnalytics": {"Flow", "Id", "Application"}, "AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"}, "AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"}, "AWS/ML": {"MLModelId", "RequestMode"}, @@ -121,6 +128,7 @@ func init() { "AWS/SWF": {"Domain", "WorkflowTypeName", "WorkflowTypeVersion", "ActivityTypeName", "ActivityTypeVersion"}, "AWS/WAF": {"Rule", "WebACL"}, "AWS/WorkSpaces": {"DirectoryId", "WorkspaceId"}, + "KMS": {"KeyId"}, } customMetricsMetricsMap = make(map[string]map[string]map[string]*CustomMetricsCache) diff --git a/pkg/api/common.go b/pkg/api/common.go index 9d3ad90783b..82eed0db5fe 100644 --- a/pkg/api/common.go +++ b/pkg/api/common.go @@ -4,7 +4,6 @@ import ( "encoding/json" "net/http" - "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/setting" "gopkg.in/macaron.v1" @@ -88,10 +87,8 @@ func ApiError(status int, message string, err error) *NormalResponse { switch status { case 404: - metrics.M_Api_Status_404.Inc(1) data["message"] = "Not Found" case 500: - metrics.M_Api_Status_500.Inc(1) data["message"] = "Internal Server Error" } diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 66d654d4d93..97f2529c781 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -104,6 +104,22 @@ func ProxyDataSourceRequest(c *middleware.Context) { } proxyPath := c.Params("*") + + if ds.Type == m.DS_ES { + if c.Req.Request.Method == "DELETE" { + c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil) + return + } + if c.Req.Request.Method == "PUT" { + c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil) + return + } + if c.Req.Request.Method == "POST" && proxyPath != "_msearch" { + c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil) + return + } + } + proxy := NewReverseProxy(ds, proxyPath, targetUrl) proxy.Transport = dataProxyTransport proxy.ServeHTTP(c.Resp, c.Req.Request) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index d5fbb2c3a9f..e024768cd5e 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -8,25 +8,25 @@ import ( ) type AlertRule struct { - Id int64 `json:"id"` - DashboardId int64 `json:"dashboardId"` - PanelId int64 `json:"panelId"` - Name string `json:"name"` - Message string `json:"message"` - State m.AlertStateType `json:"state"` - Severity m.AlertSeverityType `json:"severity"` - NewStateDate time.Time `json:"newStateDate"` - EvalDate time.Time `json:"evalDate"` - ExecutionError string `json:"executionError"` - DashbboardUri string `json:"dashboardUri"` + Id int64 `json:"id"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` + Name string `json:"name"` + Message string `json:"message"` + State m.AlertStateType `json:"state"` + NewStateDate time.Time `json:"newStateDate"` + EvalDate time.Time `json:"evalDate"` + ExecutionError string `json:"executionError"` + DashbboardUri string `json:"dashboardUri"` } type AlertNotification struct { - Id int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Id int64 `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type AlertTestCommand struct { @@ -53,13 +53,8 @@ type EvalMatch struct { Value float64 `json:"value"` } -type AlertHistory struct { - AlertId int64 `json:"alertId"` - NewState string `json:"newState"` - Timestamp time.Time `json:"timestamp"` - Title string `json:"title"` - Text string `json:"text"` - Metric string `json:"metric"` - - Data *simplejson.Json `json:"data"` +type NotificationTestCommand struct { + Name string `json:"name"` + Type string `json:"type"` + Settings *simplejson.Json `json:"settings"` } diff --git a/pkg/api/dtos/annotations.go b/pkg/api/dtos/annotations.go new file mode 100644 index 00000000000..a5d5823e1a4 --- /dev/null +++ b/pkg/api/dtos/annotations.go @@ -0,0 +1,17 @@ +package dtos + +import "github.com/grafana/grafana/pkg/components/simplejson" + +type Annotation struct { + AlertId int64 `json:"alertId"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` + NewState string `json:"newState"` + PrevState string `json:"prevState"` + Time int64 `json:"time"` + Title string `json:"title"` + Text string `json:"text"` + Metric string `json:"metric"` + + Data *simplejson.Json `json:"data"` +} diff --git a/pkg/api/dtos/playlist.go b/pkg/api/dtos/playlist.go new file mode 100644 index 00000000000..317ff83339a --- /dev/null +++ b/pkg/api/dtos/playlist.go @@ -0,0 +1,23 @@ +package dtos + +type PlaylistDashboard struct { + Id int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Uri string `json:"uri"` + Order int `json:"order"` +} + +type PlaylistDashboardsSlice []PlaylistDashboard + +func (slice PlaylistDashboardsSlice) Len() int { + return len(slice) +} + +func (slice PlaylistDashboardsSlice) Less(i, j int) bool { + return slice[i].Order < slice[j].Order +} + +func (slice PlaylistDashboardsSlice) Swap(i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 5a84409d2c1..3a019e80c49 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -105,6 +105,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro grafanaDatasourceMeta, _ := plugins.DataSources["grafana"] datasources["-- Grafana --"] = map[string]interface{}{ "type": "grafana", + "name": "-- Grafana --", "meta": grafanaDatasourceMeta, } diff --git a/pkg/api/login.go b/pkg/api/login.go index 4f976f753a2..789765ee01e 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -27,6 +27,8 @@ func LoginView(c *middleware.Context) { viewData.Settings["googleAuthEnabled"] = setting.OAuthService.Google viewData.Settings["githubAuthEnabled"] = setting.OAuthService.GitHub + viewData.Settings["genericOAuthEnabled"] = setting.OAuthService.Generic + viewData.Settings["oauthProviderName"] = setting.OAuthService.OAuthProviderName viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp viewData.Settings["loginHint"] = setting.LoginHint viewData.Settings["allowUserPassLogin"] = setting.AllowUserPassLogin diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 6512a827341..83c342937c1 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -3,7 +3,6 @@ package api import ( "errors" "fmt" - "net/url" "golang.org/x/oauth2" @@ -46,9 +45,9 @@ func OAuthLogin(ctx *middleware.Context) { userInfo, err := connect.UserInfo(token) if err != nil { if err == social.ErrMissingTeamMembership { - ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github team membership not fulfilled")) + ctx.Redirect(setting.AppSubUrl + "/login?failCode=1000") } else if err == social.ErrMissingOrganizationMembership { - ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github organization membership not fulfilled")) + ctx.Redirect(setting.AppSubUrl + "/login?failCode=1001") } else { ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err) } @@ -60,7 +59,7 @@ func OAuthLogin(ctx *middleware.Context) { // validate that the email is allowed to login to grafana if !connect.IsEmailAllowed(userInfo.Email) { ctx.Logger.Info("OAuth login attempt with unallowed email", "email", userInfo.Email) - ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required email domain not fulfilled")) + ctx.Redirect(setting.AppSubUrl + "/login?failCode=1002") return } diff --git a/pkg/api/playlist_play.go b/pkg/api/playlist_play.go index e4feb3442fb..780767531a8 100644 --- a/pkg/api/playlist_play.go +++ b/pkg/api/playlist_play.go @@ -1,16 +1,18 @@ package api import ( + "sort" "strconv" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" _ "github.com/grafana/grafana/pkg/log" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/search" ) -func populateDashboardsById(dashboardByIds []int64) ([]m.PlaylistDashboardDto, error) { - result := make([]m.PlaylistDashboardDto, 0) +func populateDashboardsById(dashboardByIds []int64, dashboardIdOrder map[int64]int) (dtos.PlaylistDashboardsSlice, error) { + result := make(dtos.PlaylistDashboardsSlice, 0) if len(dashboardByIds) > 0 { dashboardQuery := m.GetDashboardsQuery{DashboardIds: dashboardByIds} @@ -19,11 +21,12 @@ func populateDashboardsById(dashboardByIds []int64) ([]m.PlaylistDashboardDto, e } for _, item := range dashboardQuery.Result { - result = append(result, m.PlaylistDashboardDto{ + result = append(result, dtos.PlaylistDashboard{ Id: item.Id, Slug: item.Slug, Title: item.Title, Uri: "db/" + item.Slug, + Order: dashboardIdOrder[item.Id], }) } } @@ -31,8 +34,8 @@ func populateDashboardsById(dashboardByIds []int64) ([]m.PlaylistDashboardDto, e return result, nil } -func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string) []m.PlaylistDashboardDto { - result := make([]m.PlaylistDashboardDto, 0) +func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice { + result := make(dtos.PlaylistDashboardsSlice, 0) if len(dashboardByTag) > 0 { for _, tag := range dashboardByTag { @@ -47,10 +50,11 @@ func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string) []m.P if err := bus.Dispatch(&searchQuery); err == nil { for _, item := range searchQuery.Result { - result = append(result, m.PlaylistDashboardDto{ + result = append(result, dtos.PlaylistDashboard{ Id: item.Id, Title: item.Title, Uri: item.Uri, + Order: dashboardTagOrder[tag], }) } } @@ -60,28 +64,33 @@ func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string) []m.P return result } -func LoadPlaylistDashboards(orgId, userId, playlistId int64) ([]m.PlaylistDashboardDto, error) { +func LoadPlaylistDashboards(orgId, userId, playlistId int64) (dtos.PlaylistDashboardsSlice, error) { playlistItems, _ := LoadPlaylistItems(playlistId) dashboardByIds := make([]int64, 0) dashboardByTag := make([]string, 0) + dashboardIdOrder := make(map[int64]int) + dashboardTagOrder := make(map[string]int) for _, i := range playlistItems { if i.Type == "dashboard_by_id" { dashboardId, _ := strconv.ParseInt(i.Value, 10, 64) dashboardByIds = append(dashboardByIds, dashboardId) + dashboardIdOrder[dashboardId] = i.Order } if i.Type == "dashboard_by_tag" { dashboardByTag = append(dashboardByTag, i.Value) + dashboardTagOrder[i.Value] = i.Order } } - result := make([]m.PlaylistDashboardDto, 0) + result := make(dtos.PlaylistDashboardsSlice, 0) - var k, _ = populateDashboardsById(dashboardByIds) + var k, _ = populateDashboardsById(dashboardByIds, dashboardIdOrder) result = append(result, k...) - result = append(result, populateDashboardsByTag(orgId, userId, dashboardByTag)...) + result = append(result, populateDashboardsByTag(orgId, userId, dashboardByTag, dashboardTagOrder)...) + sort.Sort(sort.Reverse(result)) return result, nil } diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index 401606e5ec8..1f391ea10b3 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -90,13 +90,12 @@ func InstallPlugin(pluginName, version string, c CommandLine) error { logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), plugin.Id) - /* Enable once we need support for downloading depedencies res, _ := s.ReadPlugin(pluginFolder, pluginName) - for _, v := range res.Dependency.Plugins { + for _, v := range res.Dependencies.Plugins { InstallPlugin(v.Id, version, c) - log.Infof("Installed dependency: %v ✔\n", v.Id) + logger.Infof("Installed dependency: %v ✔\n", v.Id) } - */ + return err } diff --git a/pkg/cmd/grafana-cli/main.go b/pkg/cmd/grafana-cli/main.go index fbdec792322..658edd0f895 100644 --- a/pkg/cmd/grafana-cli/main.go +++ b/pkg/cmd/grafana-cli/main.go @@ -8,6 +8,7 @@ import ( "github.com/codegangsta/cli" "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands" "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" ) @@ -16,6 +17,8 @@ var version = "master" func main() { setupLogging() + services.Init(version) + app := cli.NewApp() app.Name = "Grafana cli" app.Usage = "" diff --git a/pkg/cmd/grafana-cli/models/model.go b/pkg/cmd/grafana-cli/models/model.go index 3a39c5fbe65..0700cb9a9e4 100644 --- a/pkg/cmd/grafana-cli/models/model.go +++ b/pkg/cmd/grafana-cli/models/model.go @@ -9,11 +9,11 @@ type InstalledPlugin struct { Name string `json:"name"` Type string `json:"type"` - Info PluginInfo `json:"info"` - Dependency Dependency `json:"dependencies"` + Info PluginInfo `json:"info"` + Dependencies Dependencies `json:"dependencies"` } -type Dependency struct { +type Dependencies struct { GrafanaVersion string `json:"grafanaVersion"` Plugins []Plugin `json:"plugins"` } diff --git a/pkg/cmd/grafana-cli/services/services.go b/pkg/cmd/grafana-cli/services/services.go index c5c45460722..e2ccce8418f 100644 --- a/pkg/cmd/grafana-cli/services/services.go +++ b/pkg/cmd/grafana-cli/services/services.go @@ -1,35 +1,59 @@ package services import ( + "crypto/tls" "encoding/json" "errors" "fmt" + "io/ioutil" + "net/http" + "net/url" "path" + "time" - "github.com/franela/goreq" "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models" ) -var IoHelper m.IoUtil = IoUtilImp{} +var ( + IoHelper m.IoUtil = IoUtilImp{} + HttpClient http.Client + grafanaVersion string +) + +func Init(version string) { + grafanaVersion = version + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, + } + + HttpClient = http.Client{ + Timeout: time.Duration(10 * time.Second), + Transport: tr, + } +} func ListAllPlugins(repoUrl string) (m.PluginRepo, error) { - fullUrl := repoUrl + "/repo" - res, err := goreq.Request{Uri: fullUrl, MaxRedirects: 3}.Do() + body, err := createRequest(repoUrl, "repo") + + if err != nil { + logger.Info("Failed to create request", "error", err) + return m.PluginRepo{}, fmt.Errorf("Failed to create request. error: %v", err) + } + if err != nil { return m.PluginRepo{}, err } - if res.StatusCode != 200 { - return m.PluginRepo{}, fmt.Errorf("Could not access %s statuscode %v", fullUrl, res.StatusCode) - } - var resp m.PluginRepo - err = res.Body.FromJsonTo(&resp) + var data m.PluginRepo + err = json.Unmarshal(body, &data) if err != nil { - return m.PluginRepo{}, errors.New("Could not load plugin data") + logger.Info("Failed to unmarshal graphite response error: %v", err) + return m.PluginRepo{}, err } - return resp, nil + return data, nil } func ReadPlugin(pluginDir, pluginName string) (m.InstalledPlugin, error) { @@ -88,21 +112,46 @@ func RemoveInstalledPlugin(pluginPath, pluginName string) error { } func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) { - fullUrl := repoUrl + "/repo/" + pluginId + body, err := createRequest(repoUrl, "repo", pluginId) + + if err != nil { + logger.Info("Failed to create request", "error", err) + return m.Plugin{}, fmt.Errorf("Failed to create request. error: %v", err) + } - res, err := goreq.Request{Uri: fullUrl, MaxRedirects: 3}.Do() if err != nil { return m.Plugin{}, err } - if res.StatusCode != 200 { - return m.Plugin{}, fmt.Errorf("Could not access %s statuscode %v", fullUrl, res.StatusCode) - } - var resp m.Plugin - err = res.Body.FromJsonTo(&resp) + var data m.Plugin + err = json.Unmarshal(body, &data) if err != nil { - return m.Plugin{}, errors.New("Could not load plugin data") + logger.Info("Failed to unmarshal graphite response error: %v", err) + return m.Plugin{}, err } - return resp, nil + return data, nil +} + +func createRequest(repoUrl string, subPaths ...string) ([]byte, error) { + u, _ := url.Parse(repoUrl) + for _, v := range subPaths { + u.Path = path.Join(u.Path, v) + } + + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + + req.Header.Set("grafana-version", grafanaVersion) + req.Header.Set("User-Agent", "grafana "+grafanaVersion) + + if err != nil { + return []byte{}, err + } + + res, err := HttpClient.Do(req) + + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + + return body, err } diff --git a/pkg/cmd/grafana-server/web.go b/pkg/cmd/grafana-server/web.go index 51975ac5617..4c294a814ca 100644 --- a/pkg/cmd/grafana-server/web.go +++ b/pkg/cmd/grafana-server/web.go @@ -53,6 +53,7 @@ func newMacaron() *macaron.Macaron { m.Use(middleware.GetContextHandler()) m.Use(middleware.Sessioner(&setting.SessionOptions)) + m.Use(middleware.RequestMetrics()) return m } diff --git a/pkg/components/imguploader/imguploader.go b/pkg/components/imguploader/imguploader.go index 5b383e69f34..1de46e7fd1d 100644 --- a/pkg/components/imguploader/imguploader.go +++ b/pkg/components/imguploader/imguploader.go @@ -19,9 +19,9 @@ func NewImageUploader() (ImageUploader, error) { return nil, err } - bucket := s3sec.Key("secret_key").String() - accessKey := s3sec.Key("access_key").String() - secretKey := s3sec.Key("secret_key").String() + bucket := s3sec.Key("bucket_url").MustString("") + accessKey := s3sec.Key("access_key").MustString("") + secretKey := s3sec.Key("secret_key").MustString("") if bucket == "" { return nil, fmt.Errorf("Could not find bucket setting for image.uploader.s3") diff --git a/pkg/components/imguploader/imguploader_test.go b/pkg/components/imguploader/imguploader_test.go index d12464dae69..4a18f22c173 100644 --- a/pkg/components/imguploader/imguploader_test.go +++ b/pkg/components/imguploader/imguploader_test.go @@ -1,7 +1,6 @@ package imguploader import ( - "reflect" "testing" "github.com/grafana/grafana/pkg/setting" @@ -27,7 +26,12 @@ func TestImageUploaderFactory(t *testing.T) { uploader, err := NewImageUploader() So(err, ShouldBeNil) - So(reflect.TypeOf(uploader), ShouldEqual, reflect.TypeOf(&S3Uploader{})) + original, ok := uploader.(*S3Uploader) + + So(ok, ShouldBeTrue) + So(original.accessKey, ShouldEqual, "access_key") + So(original.secretKey, ShouldEqual, "secret_key") + So(original.bucket, ShouldEqual, "bucket_url") }) Convey("Webdav uploader", func() { @@ -47,7 +51,12 @@ func TestImageUploaderFactory(t *testing.T) { uploader, err := NewImageUploader() So(err, ShouldBeNil) - So(reflect.TypeOf(uploader), ShouldEqual, reflect.TypeOf(&WebdavUploader{})) + original, ok := uploader.(*WebdavUploader) + + So(ok, ShouldBeTrue) + So(original.url, ShouldEqual, "webdavUrl") + So(original.username, ShouldEqual, "username") + So(original.password, ShouldEqual, "password") }) }) } diff --git a/pkg/components/imguploader/s3uploader.go b/pkg/components/imguploader/s3uploader.go index af995d566c1..59ec598412b 100644 --- a/pkg/components/imguploader/s3uploader.go +++ b/pkg/components/imguploader/s3uploader.go @@ -3,7 +3,10 @@ package imguploader import ( "io/ioutil" "net/http" + "net/url" + "path" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/util" "github.com/kr/s3/s3util" ) @@ -12,6 +15,7 @@ type S3Uploader struct { bucket string secretKey string accessKey string + log log.Logger } func NewS3Uploader(bucket, accessKey, secretKey string) *S3Uploader { @@ -19,10 +23,11 @@ func NewS3Uploader(bucket, accessKey, secretKey string) *S3Uploader { bucket: bucket, accessKey: accessKey, secretKey: secretKey, + log: log.New("s3uploader"), } } -func (u *S3Uploader) Upload(path string) (string, error) { +func (u *S3Uploader) Upload(imageDiskPath string) (string, error) { s3util.DefaultConfig.AccessKey = u.accessKey s3util.DefaultConfig.SecretKey = u.secretKey @@ -31,15 +36,26 @@ func (u *S3Uploader) Upload(path string) (string, error) { header.Add("x-amz-acl", "public-read") header.Add("Content-Type", "image/png") - fullUrl := u.bucket + util.GetRandomString(20) + ".png" - writer, err := s3util.Create(fullUrl, header, nil) + var imageUrl *url.URL + var err error + + if imageUrl, err = url.Parse(u.bucket); err != nil { + return "", err + } + + // add image to url + imageUrl.Path = path.Join(imageUrl.Path, util.GetRandomString(20)+".png") + imageUrlString := imageUrl.String() + log.Debug("Uploading image to s3", "url", imageUrlString) + + writer, err := s3util.Create(imageUrlString, header, nil) if err != nil { return "", err } defer writer.Close() - imgData, err := ioutil.ReadFile(path) + imgData, err := ioutil.ReadFile(imageDiskPath) if err != nil { return "", err } @@ -49,5 +65,5 @@ func (u *S3Uploader) Upload(path string) (string, error) { return "", err } - return fullUrl, nil + return imageUrlString, nil } diff --git a/pkg/components/imguploader/s3uploader_test.go b/pkg/components/imguploader/s3uploader_test.go new file mode 100644 index 00000000000..1204a2db4e2 --- /dev/null +++ b/pkg/components/imguploader/s3uploader_test.go @@ -0,0 +1,23 @@ +package imguploader + +import ( + "testing" + + "github.com/grafana/grafana/pkg/setting" + . "github.com/smartystreets/goconvey/convey" +) + +func TestUploadToS3(t *testing.T) { + SkipConvey("[Integration test] for external_image_store.webdav", t, func() { + setting.NewConfigContext(&setting.CommandLineArgs{ + HomePath: "../../../", + }) + + s3Uploader, _ := NewImageUploader() + + path, err := s3Uploader.Upload("../../../public/img/logo_transparent_400x.png") + + So(err, ShouldBeNil) + So(path, ShouldNotEqual, "") + }) +} diff --git a/pkg/components/imguploader/webdavuploader.go b/pkg/components/imguploader/webdavuploader.go index 74444b1b123..3b59e1690fd 100644 --- a/pkg/components/imguploader/webdavuploader.go +++ b/pkg/components/imguploader/webdavuploader.go @@ -9,7 +9,6 @@ import ( "path" "time" - "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/util" ) @@ -20,7 +19,6 @@ type WebdavUploader struct { } func (u *WebdavUploader) Upload(pa string) (string, error) { - log.Error2("Hej") client := http.Client{Timeout: time.Duration(10 * time.Second)} url, _ := url.Parse(u.url) diff --git a/pkg/login/ldap.go b/pkg/login/ldap.go index 4e8188e7a4b..d8c916bb765 100644 --- a/pkg/login/ldap.go +++ b/pkg/login/ldap.go @@ -48,7 +48,16 @@ func (a *ldapAuther) Dial() error { ServerName: host, RootCAs: certPool, } - a.conn, err = ldap.DialTLS("tcp", address, tlsCfg) + if a.server.StartTLS { + a.conn, err = ldap.Dial("tcp", address) + if err == nil { + if err = a.conn.StartTLS(tlsCfg); err == nil { + return nil + } + } + } else { + a.conn, err = ldap.DialTLS("tcp", address, tlsCfg) + } } else { a.conn, err = ldap.Dial("tcp", address) } diff --git a/pkg/login/settings.go b/pkg/login/settings.go index e01c0e50992..e0713302a6d 100644 --- a/pkg/login/settings.go +++ b/pkg/login/settings.go @@ -19,6 +19,7 @@ type LdapServerConf struct { Host string `toml:"host"` Port int `toml:"port"` UseSSL bool `toml:"use_ssl"` + StartTLS bool `toml:"start_tls"` SkipVerifySSL bool `toml:"ssl_skip_verify"` RootCACert string `toml:"root_ca_cert"` BindDN string `toml:"bind_dn"` diff --git a/pkg/metrics/gauge.go b/pkg/metrics/gauge.go index 01cd584cb39..59758aa4ecb 100644 --- a/pkg/metrics/gauge.go +++ b/pkg/metrics/gauge.go @@ -24,10 +24,10 @@ func NewGauge(meta *MetricMeta) Gauge { } } -func RegGauge(meta *MetricMeta) Gauge { - g := NewGauge(meta) - MetricStats.Register(g) - return g +func RegGauge(name string, tagStrings ...string) Gauge { + tr := NewGauge(NewMetricMeta(name, tagStrings)) + MetricStats.Register(tr) + return tr } // GaugeSnapshot is a read-only copy of another Gauge. diff --git a/pkg/metrics/graphite.go b/pkg/metrics/graphite.go index e88df2ebb1b..59c992776de 100644 --- a/pkg/metrics/graphite.go +++ b/pkg/metrics/graphite.go @@ -63,6 +63,8 @@ func (this *GraphitePublisher) Publish(metrics []Metric) { switch metric := m.(type) { case Counter: this.addCount(buf, metricName+".count", metric.Count(), now) + case Gauge: + this.addCount(buf, metricName, metric.Value(), now) case Timer: percentiles := metric.Percentiles([]float64{0.25, 0.75, 0.90, 0.99}) this.addCount(buf, metricName+".count", metric.Count(), now) diff --git a/pkg/metrics/graphite_test.go b/pkg/metrics/graphite_test.go index 2f866ddd7b6..ff2bf530d5e 100644 --- a/pkg/metrics/graphite_test.go +++ b/pkg/metrics/graphite_test.go @@ -10,6 +10,8 @@ import ( func TestGraphitePublisher(t *testing.T) { + setting.CustomInitPath = "conf/does_not_exist.ini" + Convey("Test graphite prefix replacement", t, func() { var err error err = setting.NewConfigContext(&setting.CommandLineArgs{ @@ -67,7 +69,6 @@ func TestGraphitePublisher(t *testing.T) { _, err = setting.Cfg.NewSection("metrics.graphite") - setting.InstanceName = "hostname.with.dots.com" publisher, err := CreateGraphitePublisher() So(err, ShouldBeNil) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 80c7520eaf2..002f2369c9b 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -9,40 +9,52 @@ func init() { } var ( - M_Instance_Start Counter - M_Page_Status_200 Counter - M_Page_Status_500 Counter - M_Page_Status_404 Counter - M_Api_Status_500 Counter - M_Api_Status_404 Counter - M_Api_User_SignUpStarted Counter - M_Api_User_SignUpCompleted Counter - M_Api_User_SignUpInvite Counter - M_Api_Dashboard_Save Timer - M_Api_Dashboard_Get Timer - M_Api_Dashboard_Search Timer - M_Api_Admin_User_Create Counter - M_Api_Login_Post Counter - M_Api_Login_OAuth Counter - M_Api_Org_Create Counter - M_Api_Dashboard_Snapshot_Create Counter - M_Api_Dashboard_Snapshot_External Counter - M_Api_Dashboard_Snapshot_Get Counter - M_Models_Dashboard_Insert Counter - M_Alerting_Result_State_Critical Counter - M_Alerting_Result_State_Warning Counter - M_Alerting_Result_State_Ok Counter - M_Alerting_Result_State_Paused Counter - M_Alerting_Result_State_Pending Counter - M_Alerting_Result_State_ExecutionError Counter - M_Alerting_Active_Alerts Counter - M_Alerting_Notification_Sent_Slack Counter - M_Alerting_Notification_Sent_Email Counter - M_Alerting_Notification_Sent_Webhook Counter + M_Instance_Start Counter + M_Page_Status_200 Counter + M_Page_Status_500 Counter + M_Page_Status_404 Counter + M_Page_Status_Unknown Counter + M_Api_Status_200 Counter + M_Api_Status_404 Counter + M_Api_Status_500 Counter + M_Api_Status_Unknown Counter + M_Proxy_Status_200 Counter + M_Proxy_Status_404 Counter + M_Proxy_Status_500 Counter + M_Proxy_Status_Unknown Counter + M_Api_User_SignUpStarted Counter + M_Api_User_SignUpCompleted Counter + M_Api_User_SignUpInvite Counter + M_Api_Dashboard_Save Timer + M_Api_Dashboard_Get Timer + M_Api_Dashboard_Search Timer + M_Api_Admin_User_Create Counter + M_Api_Login_Post Counter + M_Api_Login_OAuth Counter + M_Api_Org_Create Counter + M_Api_Dashboard_Snapshot_Create Counter + M_Api_Dashboard_Snapshot_External Counter + M_Api_Dashboard_Snapshot_Get Counter + M_Models_Dashboard_Insert Counter + M_Alerting_Result_State_Alerting Counter + M_Alerting_Result_State_Ok Counter + M_Alerting_Result_State_Paused Counter + M_Alerting_Result_State_NoData Counter + M_Alerting_Result_State_ExecError Counter + M_Alerting_Active_Alerts Counter + M_Alerting_Notification_Sent_Slack Counter + M_Alerting_Notification_Sent_Email Counter + M_Alerting_Notification_Sent_Webhook Counter // Timers M_DataSource_ProxyReq_Timer Timer M_Alerting_Exeuction_Time Timer + + // StatTotals + M_StatTotal_Dashboards Gauge + M_StatTotal_Users Gauge + M_StatTotal_Orgs Gauge + M_StatTotal_Playlists Gauge ) func initMetricVars(settings *MetricSettings) { @@ -54,9 +66,17 @@ func initMetricVars(settings *MetricSettings) { M_Page_Status_200 = RegCounter("page.resp_status", "code", "200") M_Page_Status_500 = RegCounter("page.resp_status", "code", "500") M_Page_Status_404 = RegCounter("page.resp_status", "code", "404") + M_Page_Status_Unknown = RegCounter("page.resp_status", "code", "unknown") - M_Api_Status_500 = RegCounter("api.resp_status", "code", "500") + M_Api_Status_200 = RegCounter("api.resp_status", "code", "200") M_Api_Status_404 = RegCounter("api.resp_status", "code", "404") + M_Api_Status_500 = RegCounter("api.resp_status", "code", "500") + M_Api_Status_Unknown = RegCounter("api.resp_status", "code", "unknown") + + M_Proxy_Status_200 = RegCounter("proxy.resp_status", "code", "200") + M_Proxy_Status_404 = RegCounter("proxy.resp_status", "code", "404") + M_Proxy_Status_500 = RegCounter("proxy.resp_status", "code", "500") + M_Proxy_Status_Unknown = RegCounter("proxy.resp_status", "code", "unknown") M_Api_User_SignUpStarted = RegCounter("api.user.signup_started") M_Api_User_SignUpCompleted = RegCounter("api.user.signup_completed") @@ -77,12 +97,11 @@ func initMetricVars(settings *MetricSettings) { M_Models_Dashboard_Insert = RegCounter("models.dashboard.insert") - M_Alerting_Result_State_Critical = RegCounter("alerting.result", "state", "critical") - M_Alerting_Result_State_Warning = RegCounter("alerting.result", "state", "warning") + M_Alerting_Result_State_Alerting = RegCounter("alerting.result", "state", "alerting") M_Alerting_Result_State_Ok = RegCounter("alerting.result", "state", "ok") M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused") - M_Alerting_Result_State_Pending = RegCounter("alerting.result", "state", "pending") - M_Alerting_Result_State_ExecutionError = RegCounter("alerting.result", "state", "execution_error") + M_Alerting_Result_State_NoData = RegCounter("alerting.result", "state", "no_data") + M_Alerting_Result_State_ExecError = RegCounter("alerting.result", "state", "exec_error") M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts") M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack") @@ -92,4 +111,10 @@ func initMetricVars(settings *MetricSettings) { // Timers M_DataSource_ProxyReq_Timer = RegTimer("api.dataproxy.request.all") M_Alerting_Exeuction_Time = RegTimer("alerting.execution_time") + + // StatTotals + M_StatTotal_Dashboards = RegGauge("stat_totals", "stat", "dashboards") + M_StatTotal_Users = RegGauge("stat_totals", "stat", "users") + M_StatTotal_Orgs = RegGauge("stat_totals", "stat", "orgs") + M_StatTotal_Playlists = RegGauge("stat_totals", "stat", "playlists") } diff --git a/pkg/metrics/publish.go b/pkg/metrics/publish.go index 9c1de6e05d2..4255481b8d1 100644 --- a/pkg/metrics/publish.go +++ b/pkg/metrics/publish.go @@ -15,6 +15,7 @@ import ( ) var metricsLogger log.Logger = log.New("metrics") +var metricPublishCounter int64 = 0 func Init() { settings := readSettings() @@ -45,12 +46,33 @@ func sendMetrics(settings *MetricSettings) { return } + updateTotalStats() + metrics := MetricStats.GetSnapshots() for _, publisher := range settings.Publishers { publisher.Publish(metrics) } } +func updateTotalStats() { + + // every interval also publish totals + metricPublishCounter++ + if metricPublishCounter%10 == 0 { + // get stats + statsQuery := m.GetSystemStatsQuery{} + if err := bus.Dispatch(&statsQuery); err != nil { + metricsLogger.Error("Failed to get system stats", "error", err) + return + } + + M_StatTotal_Dashboards.Update(statsQuery.Result.DashboardCount) + M_StatTotal_Users.Update(statsQuery.Result.UserCount) + M_StatTotal_Playlists.Update(statsQuery.Result.PlaylistCount) + M_StatTotal_Orgs.Update(statsQuery.Result.OrgCount) + } +} + func sendUsageStats() { if !setting.ReportingEnabled { return diff --git a/pkg/metrics/timer.go b/pkg/metrics/timer.go index a22d61c408e..61c3bf9533d 100644 --- a/pkg/metrics/timer.go +++ b/pkg/metrics/timer.go @@ -222,7 +222,8 @@ func (t *StandardTimer) Update(d time.Duration) { func (t *StandardTimer) UpdateSince(ts time.Time) { t.mutex.Lock() defer t.mutex.Unlock() - t.histogram.Update(int64(time.Since(ts))) + sinceMs := time.Since(ts) / time.Millisecond + t.histogram.Update(int64(sinceMs)) t.meter.Mark(1) } diff --git a/pkg/middleware/logger.go b/pkg/middleware/logger.go index c6405ef80f9..9bed7cbe16b 100644 --- a/pkg/middleware/logger.go +++ b/pkg/middleware/logger.go @@ -49,9 +49,9 @@ func Logger() macaron.Handler { if ctx, ok := c.Data["ctx"]; ok { ctxTyped := ctx.(*Context) if status == 500 { - ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ns", timeTakenMs, "size", rw.Size()) + ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", timeTakenMs, "size", rw.Size()) } else { - ctxTyped.Logger.Info("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ns", timeTakenMs, "size", rw.Size()) + ctxTyped.Logger.Info("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", timeTakenMs, "size", rw.Size()) } } } diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 5d52c68722e..df1768e1c3a 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -208,15 +208,6 @@ func (ctx *Context) Handle(status int, title string, err error) { } } - switch status { - case 200: - metrics.M_Page_Status_200.Inc(1) - case 404: - metrics.M_Page_Status_404.Inc(1) - case 500: - metrics.M_Page_Status_500.Inc(1) - } - ctx.Data["Title"] = title ctx.HTML(status, strconv.Itoa(status)) } @@ -243,10 +234,8 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) { switch status { case 404: - metrics.M_Api_Status_404.Inc(1) resp["message"] = "Not Found" case 500: - metrics.M_Api_Status_500.Inc(1) resp["message"] = "Internal Server Error" } diff --git a/pkg/middleware/request_metrics.go b/pkg/middleware/request_metrics.go new file mode 100644 index 00000000000..417a1817d15 --- /dev/null +++ b/pkg/middleware/request_metrics.go @@ -0,0 +1,65 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/grafana/grafana/pkg/metrics" + "gopkg.in/macaron.v1" +) + +func RequestMetrics() macaron.Handler { + return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) { + rw := res.(macaron.ResponseWriter) + c.Next() + + status := rw.Status() + + if strings.HasPrefix(req.RequestURI, "/api/datasources/proxy") { + countProxyRequests(status) + } else if strings.HasPrefix(req.RequestURI, "/api/") { + countApiRequests(status) + } else { + countPageRequests(status) + } + } +} + +func countApiRequests(status int) { + switch status { + case 200: + metrics.M_Api_Status_200.Inc(1) + case 404: + metrics.M_Api_Status_404.Inc(1) + case 500: + metrics.M_Api_Status_500.Inc(1) + default: + metrics.M_Api_Status_Unknown.Inc(1) + } +} + +func countPageRequests(status int) { + switch status { + case 200: + metrics.M_Page_Status_200.Inc(1) + case 404: + metrics.M_Page_Status_404.Inc(1) + case 500: + metrics.M_Page_Status_500.Inc(1) + default: + metrics.M_Page_Status_Unknown.Inc(1) + } +} + +func countProxyRequests(status int) { + switch status { + case 200: + metrics.M_Proxy_Status_200.Inc(1) + case 404: + metrics.M_Proxy_Status_404.Inc(1) + case 500: + metrics.M_Proxy_Status_500.Inc(1) + default: + metrics.M_Proxy_Status_Unknown.Inc(1) + } +} diff --git a/pkg/models/alert.go b/pkg/models/alert.go index b7d0c3e0c1c..bf21b8e5ec6 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -10,27 +10,15 @@ type AlertStateType string type AlertSeverityType string const ( - AlertStatePending AlertStateType = "pending" - AlertStateExeuctionError AlertStateType = "execution_error" - AlertStatePaused AlertStateType = "paused" - AlertStateCritical AlertStateType = "critical" - AlertStateWarning AlertStateType = "warning" - AlertStateOK AlertStateType = "ok" + AlertStateNoData AlertStateType = "no_data" + AlertStateExecError AlertStateType = "execution_error" + AlertStatePaused AlertStateType = "paused" + AlertStateAlerting AlertStateType = "alerting" + AlertStateOK AlertStateType = "ok" ) func (s AlertStateType) IsValid() bool { - return s == AlertStateOK || s == AlertStatePending || s == AlertStateExeuctionError || s == AlertStatePaused || s == AlertStateCritical || s == AlertStateWarning -} - -const ( - AlertSeverityCritical AlertSeverityType = "critical" - AlertSeverityWarning AlertSeverityType = "warning" - AlertSeverityInfo AlertSeverityType = "info" - AlertSeverityOK AlertSeverityType = "ok" -) - -func (s AlertSeverityType) IsValid() bool { - return s == AlertSeverityCritical || s == AlertSeverityInfo || s == AlertSeverityWarning + return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused } type Alert struct { @@ -41,7 +29,7 @@ type Alert struct { PanelId int64 Name string Message string - Severity AlertSeverityType + Severity string State AlertStateType Handler int64 Silenced bool @@ -114,10 +102,12 @@ type SaveAlertsCommand struct { } type SetAlertStateCommand struct { - AlertId int64 - OrgId int64 - State AlertStateType - Error string + AlertId int64 + OrgId int64 + State AlertStateType + Error string + EvalData *simplejson.Json + Timestamp time.Time } @@ -131,6 +121,7 @@ type GetAlertsQuery struct { State []string DashboardId int64 PanelId int64 + Limit int64 Result []*Alert } diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index 464d6dc88da..87b515f370c 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -7,29 +7,32 @@ import ( ) type AlertNotification struct { - Id int64 `json:"id"` - OrgId int64 `json:"-"` - Name string `json:"name"` - Type string `json:"type"` - Settings *simplejson.Json `json:"settings"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Id int64 `json:"id"` + OrgId int64 `json:"-"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type CreateAlertNotificationCommand struct { - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` OrgId int64 `json:"-"` Result *AlertNotification } type UpdateAlertNotificationCommand struct { - Id int64 `json:"id" binding:"Required"` - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - Settings *simplejson.Json `json:"settings" binding:"Required"` + Id int64 `json:"id" binding:"Required"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings" binding:"Required"` OrgId int64 `json:"-"` Result *AlertNotification @@ -43,8 +46,20 @@ type DeleteAlertNotificationCommand struct { type GetAlertNotificationsQuery struct { Name string Id int64 + OrgId int64 + + Result *AlertNotification +} + +type GetAlertNotificationsToSendQuery struct { Ids []int64 OrgId int64 Result []*AlertNotification } + +type GetAllAlertNotificationsQuery struct { + OrgId int64 + + Result []*AlertNotification +} diff --git a/pkg/models/alert_state.go b/pkg/models/alert_state.go deleted file mode 100644 index 5071efc2171..00000000000 --- a/pkg/models/alert_state.go +++ /dev/null @@ -1,47 +0,0 @@ -package models - -// type AlertState struct { -// Id int64 `json:"-"` -// OrgId int64 `json:"-"` -// AlertId int64 `json:"alertId"` -// State string `json:"state"` -// Created time.Time `json:"created"` -// Info string `json:"info"` -// TriggeredAlerts *simplejson.Json `json:"triggeredAlerts"` -// } -// -// func (this *UpdateAlertStateCommand) IsValidState() bool { -// for _, v := range alertstates.ValidStates { -// if this.State == v { -// return true -// } -// } -// return false -// } -// -// // Commands -// -// type UpdateAlertStateCommand struct { -// AlertId int64 `json:"alertId" binding:"Required"` -// OrgId int64 `json:"orgId" binding:"Required"` -// State string `json:"state" binding:"Required"` -// Info string `json:"info"` -// -// Result *Alert -// } -// -// // Queries -// -// type GetAlertsStateQuery struct { -// OrgId int64 `json:"orgId" binding:"Required"` -// AlertId int64 `json:"alertId" binding:"Required"` -// -// Result *[]AlertState -// } -// -// type GetLastAlertStateQuery struct { -// AlertId int64 -// OrgId int64 -// -// Result *AlertState -// } diff --git a/pkg/models/models.go b/pkg/models/models.go index 189e594576b..5a53cfdabb3 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -6,4 +6,5 @@ const ( GITHUB OAuthType = iota + 1 GOOGLE TWITTER + GENERIC ) diff --git a/pkg/models/playlist.go b/pkg/models/playlist.go index 4c6eacbb6a6..5c49bb9256c 100644 --- a/pkg/models/playlist.go +++ b/pkg/models/playlist.go @@ -57,17 +57,6 @@ func (this PlaylistDashboard) TableName() string { type Playlists []*Playlist type PlaylistDashboards []*PlaylistDashboard -// -// DTOS -// - -type PlaylistDashboardDto struct { - Id int64 `json:"id"` - Slug string `json:"slug"` - Title string `json:"title"` - Uri string `json:"uri"` -} - // // COMMANDS // diff --git a/pkg/models/stats.go b/pkg/models/stats.go index fa9cfdab6e8..067dec763e5 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -1,10 +1,10 @@ package models type SystemStats struct { - DashboardCount int - UserCount int - OrgCount int - PlaylistCount int + DashboardCount int64 + UserCount int64 + OrgCount int64 + PlaylistCount int64 } type DataSourceStats struct { diff --git a/pkg/services/alerting/conditions/common.go b/pkg/services/alerting/conditions/common.go deleted file mode 100644 index 06702fd1e08..00000000000 --- a/pkg/services/alerting/conditions/common.go +++ /dev/null @@ -1 +0,0 @@ -package conditions diff --git a/pkg/services/alerting/conditions/evaluator.go b/pkg/services/alerting/conditions/evaluator.go index 8c28a278d9f..18a2bf35262 100644 --- a/pkg/services/alerting/conditions/evaluator.go +++ b/pkg/services/alerting/conditions/evaluator.go @@ -5,25 +5,21 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/services/alerting" - "github.com/grafana/grafana/pkg/tsdb" ) var ( - defaultTypes []string = []string{"gt", "lt"} - rangedTypes []string = []string{"within_range", "outside_range"} - paramlessTypes []string = []string{"no_value"} + defaultTypes []string = []string{"gt", "lt"} + rangedTypes []string = []string{"within_range", "outside_range"} ) type AlertEvaluator interface { - Eval(timeSeries *tsdb.TimeSeries, reducedValue float64) bool + Eval(reducedValue *float64) bool } -type ParameterlessEvaluator struct { - Type string -} +type NoDataEvaluator struct{} -func (e *ParameterlessEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool { - return len(series.Points) == 0 +func (e *NoDataEvaluator) Eval(reducedValue *float64) bool { + return reducedValue == nil } type ThresholdEvaluator struct { @@ -47,14 +43,16 @@ func newThresholdEvaludator(typ string, model *simplejson.Json) (*ThresholdEvalu return defaultEval, nil } -func (e *ThresholdEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool { +func (e *ThresholdEvaluator) Eval(reducedValue *float64) bool { + if reducedValue == nil { + return false + } + switch e.Type { case "gt": - return reducedValue > e.Threshold + return *reducedValue > e.Threshold case "lt": - return reducedValue < e.Threshold - case "no_value": - return len(series.Points) == 0 + return *reducedValue < e.Threshold } return false @@ -88,12 +86,16 @@ func newRangedEvaluator(typ string, model *simplejson.Json) (*RangedEvaluator, e return rangedEval, nil } -func (e *RangedEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool { +func (e *RangedEvaluator) Eval(reducedValue *float64) bool { + if reducedValue == nil { + return false + } + switch e.Type { case "within_range": - return (e.Lower < reducedValue && e.Upper > reducedValue) || (e.Upper < reducedValue && e.Lower > reducedValue) + return (e.Lower < *reducedValue && e.Upper > *reducedValue) || (e.Upper < *reducedValue && e.Lower > *reducedValue) case "outside_range": - return (e.Upper < reducedValue && e.Lower < reducedValue) || (e.Upper > reducedValue && e.Lower > reducedValue) + return (e.Upper < *reducedValue && e.Lower < *reducedValue) || (e.Upper > *reducedValue && e.Lower > *reducedValue) } return false @@ -113,8 +115,8 @@ func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) { return newRangedEvaluator(typ, model) } - if inSlice(typ, paramlessTypes) { - return &ParameterlessEvaluator{Type: typ}, nil + if typ == "no_data" { + return &NoDataEvaluator{}, nil } return nil, alerting.ValidationError{Reason: "Evaludator invalid evaluator type"} diff --git a/pkg/services/alerting/conditions/evaluator_test.go b/pkg/services/alerting/conditions/evaluator_test.go index 8cc6899ff81..d2919f37d9d 100644 --- a/pkg/services/alerting/conditions/evaluator_test.go +++ b/pkg/services/alerting/conditions/evaluator_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/tsdb" . "github.com/smartystreets/goconvey/convey" ) @@ -15,19 +14,7 @@ func evalutorScenario(json string, reducedValue float64, datapoints ...float64) evaluator, err := NewAlertEvaluator(jsonModel) So(err, ShouldBeNil) - var timeserie [][2]float64 - dummieTimestamp := float64(521452145) - - for _, v := range datapoints { - timeserie = append(timeserie, [2]float64{v, dummieTimestamp}) - } - - tsdb := &tsdb.TimeSeries{ - Name: "test time serie", - Points: timeserie, - } - - return evaluator.Eval(tsdb, reducedValue) + return evaluator.Eval(&reducedValue) } func TestEvalutors(t *testing.T) { @@ -55,8 +42,15 @@ func TestEvalutors(t *testing.T) { So(evalutorScenario(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse) }) - Convey("no_value", t, func() { - So(evalutorScenario(`{"type": "no_value", "params": [] }`, 1000), ShouldBeTrue) - So(evalutorScenario(`{"type": "no_value", "params": [] }`, 1000, 1, 2), ShouldBeFalse) + Convey("no_data", t, func() { + So(evalutorScenario(`{"type": "no_data", "params": [] }`, 50), ShouldBeFalse) + + jsonModel, err := simplejson.NewJson([]byte(`{"type": "no_data", "params": [] }`)) + So(err, ShouldBeNil) + + evaluator, err := NewAlertEvaluator(jsonModel) + So(err, ShouldBeNil) + + So(evaluator.Eval(nil), ShouldBeTrue) }) } diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index 0a661e8aaab..f966985f132 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -38,25 +38,32 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { return } + emptySerieCount := 0 for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) - evalMatch := c.Evaluator.Eval(series, reducedValue) + evalMatch := c.Evaluator.Eval(reducedValue) + + if reducedValue == nil { + emptySerieCount++ + continue + } if context.IsTestRun { context.Logs = append(context.Logs, &alerting.ResultLogEntry{ - Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, evalMatch, series.Name, reducedValue), + Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, evalMatch, series.Name, *reducedValue), }) } if evalMatch { context.EvalMatches = append(context.EvalMatches, &alerting.EvalMatch{ Metric: series.Name, - Value: reducedValue, + Value: *reducedValue, }) } - - context.Firing = evalMatch } + + context.NoDataFound = emptySerieCount == len(seriesList) + context.Firing = len(context.EvalMatches) > 0 } func (c *QueryCondition) executeQuery(context *alerting.EvalContext) (tsdb.TimeSeriesSlice, error) { diff --git a/pkg/services/alerting/conditions/query_test.go b/pkg/services/alerting/conditions/query_test.go index 07cc6871801..983e75c4c1b 100644 --- a/pkg/services/alerting/conditions/query_test.go +++ b/pkg/services/alerting/conditions/query_test.go @@ -41,7 +41,9 @@ func TestQueryCondition(t *testing.T) { }) Convey("should fire when avg is above 100", func() { - ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", [][2]float64{{120, 0}})} + one := float64(120) + two := float64(0) + ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", [][2]*float64{{&one, &two}})} ctx.exec() So(ctx.result.Error, ShouldBeNil) @@ -49,12 +51,65 @@ func TestQueryCondition(t *testing.T) { }) Convey("Should not fire when avg is below 100", func() { - ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", [][2]float64{{90, 0}})} + one := float64(90) + two := float64(0) + ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", [][2]*float64{{&one, &two}})} ctx.exec() So(ctx.result.Error, ShouldBeNil) So(ctx.result.Firing, ShouldBeFalse) }) + + Convey("Should fire if only first serie matches", func() { + one := float64(120) + two := float64(0) + ctx.series = tsdb.TimeSeriesSlice{ + tsdb.NewTimeSeries("test1", [][2]*float64{{&one, &two}}), + tsdb.NewTimeSeries("test2", [][2]*float64{{&two, &two}}), + } + ctx.exec() + + So(ctx.result.Error, ShouldBeNil) + So(ctx.result.Firing, ShouldBeTrue) + }) + + Convey("Empty series", func() { + Convey("Should set NoDataFound both series are empty", func() { + ctx.series = tsdb.TimeSeriesSlice{ + tsdb.NewTimeSeries("test1", [][2]*float64{}), + tsdb.NewTimeSeries("test2", [][2]*float64{}), + } + ctx.exec() + + So(ctx.result.Error, ShouldBeNil) + So(ctx.result.NoDataFound, ShouldBeTrue) + }) + + Convey("Should set NoDataFound both series contains null", func() { + one := float64(120) + ctx.series = tsdb.TimeSeriesSlice{ + tsdb.NewTimeSeries("test1", [][2]*float64{{nil, &one}}), + tsdb.NewTimeSeries("test2", [][2]*float64{{nil, &one}}), + } + ctx.exec() + + So(ctx.result.Error, ShouldBeNil) + So(ctx.result.NoDataFound, ShouldBeTrue) + }) + + Convey("Should not set NoDataFound if one serie is empty", func() { + one := float64(120) + two := float64(0) + ctx.series = tsdb.TimeSeriesSlice{ + tsdb.NewTimeSeries("test1", [][2]*float64{}), + tsdb.NewTimeSeries("test2", [][2]*float64{{&one, &two}}), + } + ctx.exec() + + So(ctx.result.Error, ShouldBeNil) + So(ctx.result.NoDataFound, ShouldBeFalse) + }) + }) }) }) } diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index f97ff6a56d7..2bb4cec00be 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -1,52 +1,73 @@ package conditions -import "github.com/grafana/grafana/pkg/tsdb" +import ( + "math" + + "github.com/grafana/grafana/pkg/tsdb" +) type QueryReducer interface { - Reduce(timeSeries *tsdb.TimeSeries) float64 + Reduce(timeSeries *tsdb.TimeSeries) *float64 } type SimpleReducer struct { Type string } -func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) float64 { - var value float64 = 0 +func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) *float64 { + if len(series.Points) == 0 { + return nil + } + + value := float64(0) + allNull := true switch s.Type { case "avg": for _, point := range series.Points { - value += point[0] + if point[0] != nil { + value += *point[0] + allNull = false + } } value = value / float64(len(series.Points)) case "sum": for _, point := range series.Points { - value += point[0] + if point[0] != nil { + value += *point[0] + allNull = false + } } case "min": - for i, point := range series.Points { - if i == 0 { - value = point[0] - } - - if value > point[0] { - value = point[0] + value = math.MaxFloat64 + for _, point := range series.Points { + if point[0] != nil { + allNull = false + if value > *point[0] { + value = *point[0] + } } } case "max": + value = -math.MaxFloat64 for _, point := range series.Points { - if value < point[0] { - value = point[0] + if point[0] != nil { + allNull = false + if value < *point[0] { + value = *point[0] + } } } - case "mean": - meanPosition := int64(len(series.Points) / 2) - value = series.Points[meanPosition][0] case "count": value = float64(len(series.Points)) + allNull = false } - return value + if allNull { + return nil + } + + return &value } func NewSimpleReducer(typ string) *SimpleReducer { diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index af242bbd668..f60154bc98d 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -10,44 +10,39 @@ import ( func TestSimpleReducer(t *testing.T) { Convey("Test simple reducer by calculating", t, func() { Convey("avg", func() { - result := testReducer("avg", 1, 2, 3) + result := *testReducer("avg", 1, 2, 3) So(result, ShouldEqual, float64(2)) }) Convey("sum", func() { - result := testReducer("sum", 1, 2, 3) + result := *testReducer("sum", 1, 2, 3) So(result, ShouldEqual, float64(6)) }) Convey("min", func() { - result := testReducer("min", 3, 2, 1) + result := *testReducer("min", 3, 2, 1) So(result, ShouldEqual, float64(1)) }) Convey("max", func() { - result := testReducer("max", 1, 2, 3) + result := *testReducer("max", 1, 2, 3) So(result, ShouldEqual, float64(3)) }) - Convey("mean odd numbers", func() { - result := testReducer("mean", 1, 2, 3000) - So(result, ShouldEqual, float64(2)) - }) - Convey("count", func() { - result := testReducer("count", 1, 2, 3000) + result := *testReducer("count", 1, 2, 3000) So(result, ShouldEqual, float64(3)) }) }) } -func testReducer(typ string, datapoints ...float64) float64 { +func testReducer(typ string, datapoints ...float64) *float64 { reducer := NewSimpleReducer(typ) - var timeserie [][2]float64 + var timeserie [][2]*float64 dummieTimestamp := float64(521452145) - for _, v := range datapoints { - timeserie = append(timeserie, [2]float64{v, dummieTimestamp}) + for idx := range datapoints { + timeserie = append(timeserie, [2]*float64{&datapoints[idx], &dummieTimestamp}) } tsdb := &tsdb.TimeSeries{ diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go index 29c51e02abd..13067c25f08 100644 --- a/pkg/services/alerting/eval_context.go +++ b/pkg/services/alerting/eval_context.go @@ -26,6 +26,8 @@ type EvalContext struct { dashboardSlug string ImagePublicUrl string ImageOnDiskPath string + NoDataFound bool + RetryCount int } type StateDescription struct { @@ -35,30 +37,29 @@ type StateDescription struct { } func (c *EvalContext) GetStateModel() *StateDescription { - if c.Error != nil { - return &StateDescription{ - Color: "#D63232", - Text: "EXECUTION ERROR", - } - } - - if !c.Firing { + switch c.Rule.State { + case m.AlertStateOK: return &StateDescription{ Color: "#36a64f", Text: "OK", } - } - - if c.Rule.Severity == m.AlertSeverityWarning { + case m.AlertStateNoData: return &StateDescription{ - Color: "#fd821b", - Text: "WARNING", + Color: "#888888", + Text: "No Data", } - } else { + case m.AlertStateExecError: + return &StateDescription{ + Color: "#000", + Text: "Execution Error", + } + case m.AlertStateAlerting: return &StateDescription{ Color: "#D63232", - Text: "CRITICAL", + Text: "Alerting", } + default: + panic("Unknown rule state " + c.Rule.State) } } @@ -111,5 +112,6 @@ func NewEvalContext(rule *Rule) *EvalContext { DoneChan: make(chan bool, 1), CancelChan: make(chan bool, 1), log: log.New("alerting.evalContext"), + RetryCount: 0, } } diff --git a/pkg/services/alerting/eval_handler.go b/pkg/services/alerting/eval_handler.go index 3abcd978064..ab4c377197b 100644 --- a/pkg/services/alerting/eval_handler.go +++ b/pkg/services/alerting/eval_handler.go @@ -8,6 +8,10 @@ import ( "github.com/grafana/grafana/pkg/metrics" ) +var ( + MaxRetries int = 1 +) + type DefaultEvalHandler struct { log log.Logger alertJobTimeout time.Duration @@ -16,26 +20,48 @@ type DefaultEvalHandler struct { func NewEvalHandler() *DefaultEvalHandler { return &DefaultEvalHandler{ log: log.New("alerting.evalHandler"), - alertJobTimeout: time.Second * 5, + alertJobTimeout: time.Second * 10, } } func (e *DefaultEvalHandler) Eval(context *EvalContext) { - go e.eval(context) select { case <-time.After(e.alertJobTimeout): - context.Error = fmt.Errorf("Timeout") + context.Error = fmt.Errorf("Execution timed out after %v", e.alertJobTimeout) context.EndTime = time.Now() - e.log.Debug("Job Execution timeout", "alertId", context.Rule.Id) + e.log.Debug("Job Execution timeout", "alertId", context.Rule.Id, "timeout setting", e.alertJobTimeout) + e.retry(context) case <-context.DoneChan: e.log.Debug("Job Execution done", "timeMs", context.GetDurationMs(), "alertId", context.Rule.Id, "firing", context.Firing) - } + if context.Error != nil { + e.retry(context) + } + } +} + +func (e *DefaultEvalHandler) retry(context *EvalContext) { + e.log.Debug("Retrying eval exeuction", "alertId", context.Rule.Id) + + if context.RetryCount < MaxRetries { + context.DoneChan = make(chan bool, 1) + context.CancelChan = make(chan bool, 1) + context.RetryCount++ + e.Eval(context) + } } func (e *DefaultEvalHandler) eval(context *EvalContext) { + defer func() { + if err := recover(); err != nil { + e.log.Error("Alerting rule eval panic", "error", err, "stack", log.Stack(1)) + if panicErr, ok := err.(error); ok { + context.Error = panicErr + } + } + }() for _, condition := range context.Rule.Conditions { condition.Eval(context) @@ -52,7 +78,7 @@ func (e *DefaultEvalHandler) eval(context *EvalContext) { } context.EndTime = time.Now() - elapsedTime := context.EndTime.Sub(context.StartTime) + elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond metrics.M_Alerting_Exeuction_Time.Update(elapsedTime) context.DoneChan <- true } diff --git a/pkg/services/alerting/eval_handler_test.go b/pkg/services/alerting/eval_handler_test.go index 039e9be9a30..ae5b4e4501d 100644 --- a/pkg/services/alerting/eval_handler_test.go +++ b/pkg/services/alerting/eval_handler_test.go @@ -40,6 +40,5 @@ func TestAlertingExecutor(t *testing.T) { handler.eval(context) So(context.Firing, ShouldEqual, false) }) - }) } diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index 7d3b800fb31..cf516786412 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -88,14 +88,9 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) { Name: jsonAlert.Get("name").MustString(), Handler: jsonAlert.Get("handler").MustInt64(), Message: jsonAlert.Get("message").MustString(), - Severity: m.AlertSeverityType(jsonAlert.Get("severity").MustString()), Frequency: getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString()), } - if !alert.Severity.IsValid() { - return nil, ValidationError{Reason: "Invalid alert Severity"} - } - for _, condition := range jsonAlert.Get("conditions").MustArray() { jsonCondition := simplejson.NewFromAny(condition) diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index e3df5e571d1..b25ce313b54 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -44,7 +44,6 @@ func TestAlertRuleExtraction(t *testing.T) { "handler": 1, "enabled": true, "frequency": "60s", - "severity": "critical", "conditions": [ { "type": "query", @@ -129,11 +128,6 @@ func TestAlertRuleExtraction(t *testing.T) { So(alerts[1].Handler, ShouldEqual, 0) }) - Convey("should extract Severity property", func() { - So(alerts[0].Severity, ShouldEqual, "critical") - So(alerts[1].Severity, ShouldEqual, "warning") - }) - Convey("should extract frequency in seconds", func() { So(alerts[0].Frequency, ShouldEqual, 60) So(alerts[1].Frequency, ShouldEqual, 60) diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 9688aba153a..78ffc280375 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -1,6 +1,8 @@ package alerting -import "time" +import ( + "time" +) type EvalHandler interface { Eval(context *EvalContext) @@ -15,6 +17,7 @@ type Notifier interface { Notify(alertResult *EvalContext) GetType() string NeedsImage() bool + PassesFilter(rule *Rule) bool } type Condition interface { diff --git a/pkg/services/alerting/models.go b/pkg/services/alerting/models.go index e11e1e1aaaf..39e7eb83166 100644 --- a/pkg/services/alerting/models.go +++ b/pkg/services/alerting/models.go @@ -1,10 +1,11 @@ package alerting type Job struct { - Offset int64 - Delay bool - Running bool - Rule *Rule + Offset int64 + OffsetWait bool + Delay bool + Running bool + Rule *Rule } type ResultLogEntry struct { diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index c345594de2c..bb48f71cdcd 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -28,10 +28,14 @@ func (n *RootNotifier) NeedsImage() bool { return false } +func (n *RootNotifier) PassesFilter(rule *Rule) bool { + return false +} + func (n *RootNotifier) Notify(context *EvalContext) { n.log.Info("Sending notifications for", "ruleId", context.Rule.Id) - notifiers, err := n.getNotifiers(context.Rule.OrgId, context.Rule.Notifications) + notifiers, err := n.getNotifiers(context.Rule.OrgId, context.Rule.Notifications, context) if err != nil { n.log.Error("Failed to read notifications", "error", err) return @@ -46,6 +50,10 @@ func (n *RootNotifier) Notify(context *EvalContext) { n.log.Error("Failed to upload alert panel image", "error", err) } + n.sendNotifications(notifiers, context) +} + +func (n *RootNotifier) sendNotifications(notifiers []Notifier, context *EvalContext) { for _, notifier := range notifiers { n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType()) go notifier.Notify(context) @@ -53,7 +61,6 @@ func (n *RootNotifier) Notify(context *EvalContext) { } func (n *RootNotifier) uploadImage(context *EvalContext) error { - uploader, _ := imguploader.NewImageUploader() imageUrl, err := context.GetImageUrl() @@ -84,29 +91,28 @@ func (n *RootNotifier) uploadImage(context *EvalContext) error { return nil } -func (n *RootNotifier) getNotifiers(orgId int64, notificationIds []int64) ([]Notifier, error) { - if len(notificationIds) == 0 { - return []Notifier{}, nil - } +func (n *RootNotifier) getNotifiers(orgId int64, notificationIds []int64, context *EvalContext) ([]Notifier, error) { + query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds} - query := &m.GetAlertNotificationsQuery{OrgId: orgId, Ids: notificationIds} if err := bus.Dispatch(query); err != nil { return nil, err } var result []Notifier for _, notification := range query.Result { - if not, err := n.getNotifierFor(notification); err != nil { + if not, err := n.createNotifierFor(notification); err != nil { return nil, err } else { - result = append(result, not) + if shouldUseNotification(not, context) { + result = append(result, not) + } } } return result, nil } -func (n *RootNotifier) getNotifierFor(model *m.AlertNotification) (Notifier, error) { +func (n *RootNotifier) createNotifierFor(model *m.AlertNotification) (Notifier, error) { factory, found := notifierFactories[model.Type] if !found { return nil, errors.New("Unsupported notification type") @@ -115,6 +121,18 @@ func (n *RootNotifier) getNotifierFor(model *m.AlertNotification) (Notifier, err return factory(model) } +func shouldUseNotification(notifier Notifier, context *EvalContext) bool { + if !context.Firing { + return true + } + + if context.Error != nil { + return true + } + + return notifier.PassesFilter(context.Rule) +} + type NotifierFactory func(notification *m.AlertNotification) (Notifier, error) var notifierFactories map[string]NotifierFactory = make(map[string]NotifierFactory) diff --git a/pkg/services/alerting/notifier_test.go b/pkg/services/alerting/notifier_test.go index ebe305c6174..c854d8475b5 100644 --- a/pkg/services/alerting/notifier_test.go +++ b/pkg/services/alerting/notifier_test.go @@ -1,114 +1,81 @@ package alerting -// func TestAlertNotificationExtraction(t *testing.T) { -// Convey("Notifier tests", t, func() { -// Convey("rules for sending notifications", func() { -// dummieNotifier := NotifierImpl{} -// -// result := &AlertResult{ -// State: alertstates.Critical, -// } -// -// notifier := &Notification{ -// Name: "Test Notifier", -// Type: "TestType", -// SendCritical: true, -// SendWarning: true, -// } -// -// Convey("Should send notification", func() { -// So(dummieNotifier.ShouldDispath(result, notifier), ShouldBeTrue) -// }) -// -// Convey("warn:false and state:warn should not send", func() { -// result.State = alertstates.Warn -// notifier.SendWarning = false -// So(dummieNotifier.ShouldDispath(result, notifier), ShouldBeFalse) -// }) -// }) -// -// Convey("Parsing alert notification from settings", func() { -// Convey("Parsing email", func() { -// Convey("empty settings should return error", func() { -// json := `{ }` -// -// settingsJSON, _ := simplejson.NewJson([]byte(json)) -// model := &m.AlertNotification{ -// Name: "ops", -// Type: "email", -// Settings: settingsJSON, -// } -// -// _, err := NewNotificationFromDBModel(model) -// So(err, ShouldNotBeNil) -// }) -// -// Convey("from settings", func() { -// json := ` -// { -// "to": "ops@grafana.org" -// }` -// -// settingsJSON, _ := simplejson.NewJson([]byte(json)) -// model := &m.AlertNotification{ -// Name: "ops", -// Type: "email", -// Settings: settingsJSON, -// } -// -// not, err := NewNotificationFromDBModel(model) -// -// So(err, ShouldBeNil) -// So(not.Name, ShouldEqual, "ops") -// So(not.Type, ShouldEqual, "email") -// So(reflect.TypeOf(not.Notifierr).Elem().String(), ShouldEqual, "alerting.EmailNotifier") -// -// email := not.Notifierr.(*EmailNotifier) -// So(email.To, ShouldEqual, "ops@grafana.org") -// }) -// }) -// -// Convey("Parsing webhook", func() { -// Convey("empty settings should return error", func() { -// json := `{ }` -// -// settingsJSON, _ := simplejson.NewJson([]byte(json)) -// model := &m.AlertNotification{ -// Name: "ops", -// Type: "webhook", -// Settings: settingsJSON, -// } -// -// _, err := NewNotificationFromDBModel(model) -// So(err, ShouldNotBeNil) -// }) -// -// Convey("from settings", func() { -// json := ` -// { -// "url": "http://localhost:3000", -// "username": "username", -// "password": "password" -// }` -// -// settingsJSON, _ := simplejson.NewJson([]byte(json)) -// model := &m.AlertNotification{ -// Name: "slack", -// Type: "webhook", -// Settings: settingsJSON, -// } -// -// not, err := NewNotificationFromDBModel(model) -// -// So(err, ShouldBeNil) -// So(not.Name, ShouldEqual, "slack") -// So(not.Type, ShouldEqual, "webhook") -// So(reflect.TypeOf(not.Notifierr).Elem().String(), ShouldEqual, "alerting.WebhookNotifier") -// -// webhook := not.Notifierr.(*WebhookNotifier) -// So(webhook.Url, ShouldEqual, "http://localhost:3000") -// }) -// }) -// }) -// }) -// } +import ( + "testing" + + "fmt" + + "github.com/grafana/grafana/pkg/models" + m "github.com/grafana/grafana/pkg/models" + . "github.com/smartystreets/goconvey/convey" +) + +type FakeNotifier struct { + FakeMatchResult bool +} + +func (fn *FakeNotifier) GetType() string { + return "FakeNotifier" +} + +func (fn *FakeNotifier) NeedsImage() bool { + return true +} + +func (fn *FakeNotifier) Notify(alertResult *EvalContext) {} + +func (fn *FakeNotifier) PassesFilter(rule *Rule) bool { + return fn.FakeMatchResult +} + +func TestAlertNotificationExtraction(t *testing.T) { + + Convey("Notifier tests", t, func() { + Convey("none firing alerts", func() { + ctx := &EvalContext{ + Firing: false, + Rule: &Rule{ + State: m.AlertStateAlerting, + }, + } + notifier := &FakeNotifier{FakeMatchResult: false} + + So(shouldUseNotification(notifier, ctx), ShouldBeTrue) + }) + + Convey("execution error cannot be ignored", func() { + ctx := &EvalContext{ + Firing: true, + Error: fmt.Errorf("I used to be a programmer just like you"), + Rule: &Rule{ + State: m.AlertStateOK, + }, + } + notifier := &FakeNotifier{FakeMatchResult: false} + + So(shouldUseNotification(notifier, ctx), ShouldBeTrue) + }) + + Convey("firing alert that match", func() { + ctx := &EvalContext{ + Firing: true, + Rule: &Rule{ + State: models.AlertStateAlerting, + }, + } + notifier := &FakeNotifier{FakeMatchResult: true} + + So(shouldUseNotification(notifier, ctx), ShouldBeTrue) + }) + + Convey("firing alert that dont match", func() { + ctx := &EvalContext{ + Firing: true, + Rule: &Rule{State: m.AlertStateOK}, + } + notifier := &FakeNotifier{FakeMatchResult: false} + + So(shouldUseNotification(notifier, ctx), ShouldBeFalse) + }) + }) +} diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go index 48fe6c4eaa5..27c2a625d17 100644 --- a/pkg/services/alerting/notifiers/base.go +++ b/pkg/services/alerting/notifiers/base.go @@ -1,10 +1,24 @@ package notifiers +import ( + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/services/alerting" +) + type NotifierBase struct { Name string Type string } +func NewNotifierBase(name, notifierType string, model *simplejson.Json) NotifierBase { + base := NotifierBase{Name: name, Type: notifierType} + return base +} + +func (n *NotifierBase) PassesFilter(rule *alerting.Rule) bool { + return true +} + func (n *NotifierBase) GetType() string { return n.Type } diff --git a/pkg/services/alerting/notifiers/base_test.go b/pkg/services/alerting/notifiers/base_test.go new file mode 100644 index 00000000000..8cfc1ec3ae9 --- /dev/null +++ b/pkg/services/alerting/notifiers/base_test.go @@ -0,0 +1,30 @@ +package notifiers + +// import . "github.com/smartystreets/goconvey/convey" +// +// func TestBaseNotifier( t *testing.T ) { +// Convey("Parsing base notification state", t, func() { +// +// Convey("matches", func() { +// json := ` +// { +// "states": "critical" +// }` +// +// settingsJSON, _ := simplejson.NewJson([]byte(json)) +// not := NewNotifierBase("ops", "email", settingsJSON) +// So(not.MatchSeverity(m.AlertSeverityCritical), ShouldBeTrue) +// }) +// +// Convey("does not match", func() { +// json := ` +// { +// "severityFilter": "critical" +// }` +// +// settingsJSON, _ := simplejson.NewJson([]byte(json)) +// not := NewNotifierBase("ops", "email", settingsJSON) +// So(not.MatchSeverity(m.AlertSeverityWarning), ShouldBeFalse) +// }) +// }) +// } diff --git a/pkg/services/alerting/notifiers/common.go b/pkg/services/alerting/notifiers/common.go deleted file mode 100644 index 48b634c44d7..00000000000 --- a/pkg/services/alerting/notifiers/common.go +++ /dev/null @@ -1 +0,0 @@ -package notifiers diff --git a/pkg/services/alerting/notifiers/email.go b/pkg/services/alerting/notifiers/email.go index d63ca7d13f5..eccd3ce9dfb 100644 --- a/pkg/services/alerting/notifiers/email.go +++ b/pkg/services/alerting/notifiers/email.go @@ -29,12 +29,9 @@ func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) { } return &EmailNotifier{ - NotifierBase: NotifierBase{ - Name: model.Name, - Type: model.Type, - }, - Addresses: strings.Split(addressesString, "\n"), - log: log.New("alerting.notifier.email"), + NotifierBase: NewNotifierBase(model.Name, model.Type, model.Settings), + Addresses: strings.Split(addressesString, "\n"), + log: log.New("alerting.notifier.email"), }, nil } @@ -50,16 +47,15 @@ func (this *EmailNotifier) Notify(context *alerting.EvalContext) { cmd := &m.SendEmailCommand{ Data: map[string]interface{}{ - "Title": context.GetNotificationTitle(), - "State": context.Rule.State, - "Name": context.Rule.Name, - "Severity": context.Rule.Severity, - "SeverityColor": context.GetStateModel().Color, - "Message": context.Rule.Message, - "RuleUrl": ruleUrl, - "ImageLink": context.ImagePublicUrl, - "AlertPageUrl": setting.AppUrl + "alerting", - "EvalMatches": context.EvalMatches, + "Title": context.GetNotificationTitle(), + "State": context.Rule.State, + "Name": context.Rule.Name, + "StateModel": context.GetStateModel(), + "Message": context.Rule.Message, + "RuleUrl": ruleUrl, + "ImageLink": context.ImagePublicUrl, + "AlertPageUrl": setting.AppUrl + "alerting", + "EvalMatches": context.EvalMatches, }, To: this.Addresses, Template: "alert_notification.html", diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index 837621e80bb..d0d67ca5a88 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -23,12 +23,9 @@ func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) { } return &SlackNotifier{ - NotifierBase: NotifierBase{ - Name: model.Name, - Type: model.Type, - }, - Url: url, - log: log.New("alerting.notifier.slack"), + NotifierBase: NewNotifierBase(model.Name, model.Type, model.Settings), + Url: url, + log: log.New("alerting.notifier.slack"), }, nil } diff --git a/pkg/services/alerting/notifiers/webhook.go b/pkg/services/alerting/notifiers/webhook.go index e725752778f..7e28b35cd0a 100644 --- a/pkg/services/alerting/notifiers/webhook.go +++ b/pkg/services/alerting/notifiers/webhook.go @@ -20,14 +20,11 @@ func NewWebHookNotifier(model *m.AlertNotification) (alerting.Notifier, error) { } return &WebhookNotifier{ - NotifierBase: NotifierBase{ - Name: model.Name, - Type: model.Type, - }, - Url: url, - User: model.Settings.Get("user").MustString(), - Password: model.Settings.Get("password").MustString(), - log: log.New("alerting.notifier.webhook"), + NotifierBase: NewNotifierBase(model.Name, model.Type, model.Settings), + Url: url, + User: model.Settings.Get("user").MustString(), + Password: model.Settings.Get("password").MustString(), + log: log.New("alerting.notifier.webhook"), }, nil } @@ -48,7 +45,6 @@ func (this *WebhookNotifier) Notify(context *alerting.EvalContext) { bodyJSON.Set("ruleId", context.Rule.Id) bodyJSON.Set("ruleName", context.Rule.Name) bodyJSON.Set("state", context.Rule.State) - bodyJSON.Set("severity", context.Rule.Severity) bodyJSON.Set("evalMatches", context.EvalMatches) ruleUrl, err := context.GetRuleUrl() diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index f22afb279d8..bb9f46d6084 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -34,14 +34,19 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { annotationData := simplejson.New() if ctx.Error != nil { handler.log.Error("Alert Rule Result Error", "ruleId", ctx.Rule.Id, "error", ctx.Error) - ctx.Rule.State = m.AlertStateExeuctionError + ctx.Rule.State = m.AlertStateExecError exeuctionError = ctx.Error.Error() annotationData.Set("errorMessage", exeuctionError) } else if ctx.Firing { - ctx.Rule.State = m.AlertStateType(ctx.Rule.Severity) + ctx.Rule.State = m.AlertStateAlerting annotationData = simplejson.NewFromAny(ctx.EvalMatches) } else { - ctx.Rule.State = m.AlertStateOK + // handle no data case + if ctx.NoDataFound { + ctx.Rule.State = ctx.Rule.NoDataState + } else { + ctx.Rule.State = m.AlertStateOK + } } countStateResult(ctx.Rule.State) @@ -49,10 +54,11 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { handler.log.Info("New state change", "alertId", ctx.Rule.Id, "newState", ctx.Rule.State, "oldState", oldState) cmd := &m.SetAlertStateCommand{ - AlertId: ctx.Rule.Id, - OrgId: ctx.Rule.OrgId, - State: ctx.Rule.State, - Error: exeuctionError, + AlertId: ctx.Rule.Id, + OrgId: ctx.Rule.OrgId, + State: ctx.Rule.State, + Error: exeuctionError, + EvalData: annotationData, } if err := bus.Dispatch(cmd); err != nil { @@ -61,15 +67,17 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { // save annotation item := annotations.Item{ - OrgId: ctx.Rule.OrgId, - Type: annotations.AlertType, - AlertId: ctx.Rule.Id, - Title: ctx.Rule.Name, - Text: ctx.GetStateModel().Text, - NewState: string(ctx.Rule.State), - PrevState: string(oldState), - Timestamp: time.Now(), - Data: annotationData, + OrgId: ctx.Rule.OrgId, + DashboardId: ctx.Rule.DashboardId, + PanelId: ctx.Rule.PanelId, + Type: annotations.AlertType, + AlertId: ctx.Rule.Id, + Title: ctx.Rule.Name, + Text: ctx.GetStateModel().Text, + NewState: string(ctx.Rule.State), + PrevState: string(oldState), + Epoch: time.Now().Unix(), + Data: annotationData, } annotationRepo := annotations.GetRepository() @@ -83,17 +91,15 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { func countStateResult(state m.AlertStateType) { switch state { - case m.AlertStateCritical: - metrics.M_Alerting_Result_State_Critical.Inc(1) - case m.AlertStateWarning: - metrics.M_Alerting_Result_State_Warning.Inc(1) + case m.AlertStateAlerting: + metrics.M_Alerting_Result_State_Alerting.Inc(1) case m.AlertStateOK: metrics.M_Alerting_Result_State_Ok.Inc(1) case m.AlertStatePaused: metrics.M_Alerting_Result_State_Paused.Inc(1) - case m.AlertStatePending: - metrics.M_Alerting_Result_State_Pending.Inc(1) - case m.AlertStateExeuctionError: - metrics.M_Alerting_Result_State_ExecutionError.Inc(1) + case m.AlertStateNoData: + metrics.M_Alerting_Result_State_NoData.Inc(1) + case m.AlertStateExecError: + metrics.M_Alerting_Result_State_ExecError.Inc(1) } } diff --git a/pkg/services/alerting/rule.go b/pkg/services/alerting/rule.go index 8292041e04c..5f59b60b64f 100644 --- a/pkg/services/alerting/rule.go +++ b/pkg/services/alerting/rule.go @@ -18,8 +18,8 @@ type Rule struct { Frequency int64 Name string Message string + NoDataState m.AlertStateType State m.AlertStateType - Severity m.AlertSeverityType Conditions []Condition Notifications []int64 } @@ -65,8 +65,8 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { model.Name = ruleDef.Name model.Message = ruleDef.Message model.Frequency = ruleDef.Frequency - model.Severity = ruleDef.Severity model.State = ruleDef.State + model.NoDataState = m.AlertStateType(ruleDef.Settings.Get("noDataState").MustString("no_data")) for _, v := range ruleDef.Settings.Get("notifications").MustArray() { jsonModel := simplejson.NewFromAny(v) diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go index 91b31d6d07f..622904ec3fc 100644 --- a/pkg/services/alerting/rule_test.go +++ b/pkg/services/alerting/rule_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" + m "github.com/grafana/grafana/pkg/models" . "github.com/smartystreets/goconvey/convey" ) @@ -45,6 +45,7 @@ func TestAlertRuleModel(t *testing.T) { "name": "name2", "description": "desc2", "handler": 0, + "noDataMode": "critical", "enabled": true, "frequency": "60s", "conditions": [ @@ -63,7 +64,7 @@ func TestAlertRuleModel(t *testing.T) { alertJSON, jsonErr := simplejson.NewJson([]byte(json)) So(jsonErr, ShouldBeNil) - alert := &models.Alert{ + alert := &m.Alert{ Id: 1, OrgId: 1, DashboardId: 1, @@ -80,6 +81,11 @@ func TestAlertRuleModel(t *testing.T) { Convey("Can read notifications", func() { So(len(alertRule.Notifications), ShouldEqual, 2) }) + /* + Convey("Can read noDataMode", func() { + So(len(alertRule.NoDataMode), ShouldEqual, m.AlertStateCritical) + }) + */ }) }) } diff --git a/pkg/services/alerting/scheduler.go b/pkg/services/alerting/scheduler.go index ffac7ddb659..9d20796f3dc 100644 --- a/pkg/services/alerting/scheduler.go +++ b/pkg/services/alerting/scheduler.go @@ -1,6 +1,7 @@ package alerting import ( + "math" "time" "github.com/grafana/grafana/pkg/log" @@ -34,8 +35,9 @@ func (s *SchedulerImpl) Update(rules []*Rule) { } job.Rule = rule - job.Offset = int64(i) + offset := ((rule.Frequency * 1000) / int64(len(rules))) * int64(i) + job.Offset = int64(math.Floor(float64(offset) / 1000)) jobs[rule.Id] = job } @@ -46,9 +48,27 @@ func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { now := tickTime.Unix() for _, job := range s.jobs { - if now%job.Rule.Frequency == 0 && job.Running == false { - s.log.Debug("Scheduler: Putting job on to exec queue", "name", job.Rule.Name) - execQueue <- job + if job.Running { + continue + } + + if job.OffsetWait && now%job.Offset == 0 { + job.OffsetWait = false + s.enque(job, execQueue) + continue + } + + if now%job.Rule.Frequency == 0 { + if job.Offset > 0 { + job.OffsetWait = true + } else { + s.enque(job, execQueue) + } } } } + +func (s *SchedulerImpl) enque(job *Job, execQueue chan *Job) { + s.log.Debug("Scheduler: Putting job on to exec queue", "name", job.Rule.Name, "id", job.Rule.Id) + execQueue <- job +} diff --git a/pkg/services/alerting/test_notification.go b/pkg/services/alerting/test_notification.go new file mode 100644 index 00000000000..de2cb981aaa --- /dev/null +++ b/pkg/services/alerting/test_notification.go @@ -0,0 +1,77 @@ +package alerting + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" +) + +type NotificationTestCommand struct { + State m.AlertStateType + Name string + Type string + Settings *simplejson.Json +} + +func init() { + bus.AddHandler("alerting", handleNotificationTestCommand) + +} + +func handleNotificationTestCommand(cmd *NotificationTestCommand) error { + notifier := NewRootNotifier() + + model := &m.AlertNotification{ + Name: cmd.Name, + Type: cmd.Type, + Settings: cmd.Settings, + } + + notifiers, err := notifier.createNotifierFor(model) + + if err != nil { + log.Error2("Failed to create notifier", "error", err.Error()) + return err + } + + notifier.sendNotifications([]Notifier{notifiers}, createTestEvalContext()) + + return nil +} + +func createTestEvalContext() *EvalContext { + + testRule := &Rule{ + DashboardId: 1, + PanelId: 1, + Name: "Test notification", + Message: "Someone is testing the alert notification within grafana.", + State: m.AlertStateAlerting, + } + + ctx := NewEvalContext(testRule) + ctx.ImagePublicUrl = "http://grafana.org/assets/img/blog/mixed_styles.png" + + ctx.IsTestRun = true + ctx.Firing = true + ctx.Error = nil + ctx.EvalMatches = evalMatchesBasedOnState() + + return ctx +} + +func evalMatchesBasedOnState() []*EvalMatch { + matches := make([]*EvalMatch, 0) + matches = append(matches, &EvalMatch{ + Metric: "High value", + Value: 100, + }) + + matches = append(matches, &EvalMatch{ + Metric: "Higher Value", + Value: 200, + }) + + return matches +} diff --git a/pkg/services/annotations/annotations.go b/pkg/services/annotations/annotations.go index 06be152ec31..189c3d823cf 100644 --- a/pkg/services/annotations/annotations.go +++ b/pkg/services/annotations/annotations.go @@ -1,10 +1,6 @@ package annotations -import ( - "time" - - "github.com/grafana/grafana/pkg/components/simplejson" -) +import "github.com/grafana/grafana/pkg/components/simplejson" type Repository interface { Save(item *Item) error @@ -12,9 +8,14 @@ type Repository interface { } type ItemQuery struct { - OrgId int64 `json:"orgId"` - Type ItemType `json:"type"` - AlertId int64 `json:"alertId"` + OrgId int64 `json:"orgId"` + From int64 `json:"from"` + To int64 `json:"from"` + Type ItemType `json:"type"` + AlertId int64 `json:"alertId"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` + NewState []string `json:"newState"` Limit int64 `json:"alertId"` } @@ -36,17 +37,20 @@ const ( ) type Item struct { - Id int64 `json:"id"` - OrgId int64 `json:"orgId"` - Type ItemType `json:"type"` - Title string `json:"title"` - Text string `json:"text"` - Metric string `json:"metric"` - AlertId int64 `json:"alertId"` - UserId int64 `json:"userId"` - PrevState string `json:"prevState"` - NewState string `json:"newState"` - Timestamp time.Time `json:"timestamp"` + Id int64 `json:"id"` + OrgId int64 `json:"orgId"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` + CategoryId int64 `json:"panelId"` + Type ItemType `json:"type"` + Title string `json:"title"` + Text string `json:"text"` + Metric string `json:"metric"` + AlertId int64 `json:"alertId"` + UserId int64 `json:"userId"` + PrevState string `json:"prevState"` + NewState string `json:"newState"` + Epoch int64 `json:"epoch"` Data *simplejson.Json `json:"data"` } diff --git a/pkg/services/notifications/mailer.go b/pkg/services/notifications/mailer.go index 309436cb7d9..91c75a1889e 100644 --- a/pkg/services/notifications/mailer.go +++ b/pkg/services/notifications/mailer.go @@ -12,6 +12,7 @@ import ( "net/smtp" "os" "strings" + "time" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/setting" @@ -66,7 +67,7 @@ func sendToSmtpServer(recipients []string, msgContent []byte) error { tlsconfig.Certificates = []tls.Certificate{cert} } - conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) + conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), time.Second*10) if err != nil { return err } diff --git a/pkg/services/notifications/send_email_integration_test.go b/pkg/services/notifications/send_email_integration_test.go index b91cc178240..1a4406dc13a 100644 --- a/pkg/services/notifications/send_email_integration_test.go +++ b/pkg/services/notifications/send_email_integration_test.go @@ -40,7 +40,7 @@ func TestEmailIntegrationTest(t *testing.T) { "RuleUrl": "http://localhost:3000/dashboard/db/graphite-dashboard", "ImageLink": "http://localhost:3000/render/dashboard-solo/db/graphite-dashboard?panelId=1&from=1471008499616&to=1471012099617&width=1000&height=500", "AlertPageUrl": "http://localhost:3000/alerting", - "Events": []map[string]string{ + "EvalMatches": []map[string]string{ { "Metric": "desktop", "Value": "40", diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index be67749a627..5a430238823 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -87,6 +87,13 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error { sql.WriteString(")") } + if query.Limit != 0 { + sql.WriteString(" LIMIT ?") + params = append(params, query.Limit) + } + + sql.WriteString("ORDER BY name ASC") + alerts := make([]*m.Alert, 0) if err := x.Sql(sql.String(), params...).Find(&alerts); err != nil { return err @@ -159,7 +166,7 @@ func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xor } else { alert.Updated = time.Now() alert.Created = time.Now() - alert.State = m.AlertStatePending + alert.State = m.AlertStateNoData alert.NewStateDate = time.Now() _, err := sess.Insert(alert) @@ -222,6 +229,8 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { alert.State = cmd.State alert.StateChanges += 1 alert.NewStateDate = time.Now() + alert.EvalData = cmd.EvalData + if cmd.Error == "" { alert.ExecutionError = " " //without this space, xorm skips updating this field } else { diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index c5b3a2f5975..5105ca39eff 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -16,6 +16,8 @@ func init() { bus.AddHandler("sql", CreateAlertNotificationCommand) bus.AddHandler("sql", UpdateAlertNotification) bus.AddHandler("sql", DeleteAlertNotification) + bus.AddHandler("sql", GetAlertNotificationsToSend) + bus.AddHandler("sql", GetAllAlertNotifications) } func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error { @@ -32,73 +34,122 @@ func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error { } func GetAlertNotifications(query *m.GetAlertNotificationsQuery) error { - return getAlertNotificationsInternal(query, x.NewSession()) + return getAlertNotificationInternal(query, x.NewSession()) } -func getAlertNotificationsInternal(query *m.GetAlertNotificationsQuery, sess *xorm.Session) error { +func GetAllAlertNotifications(query *m.GetAllAlertNotificationsQuery) error { + results := make([]*m.AlertNotification, 0) + if err := x.Where("org_id = ?", query.OrgId).Find(&results); err != nil { + return err + } + + query.Result = results + return nil +} + +func GetAlertNotificationsToSend(query *m.GetAlertNotificationsToSendQuery) error { var sql bytes.Buffer params := make([]interface{}, 0) sql.WriteString(`SELECT - alert_notification.id, - alert_notification.org_id, - alert_notification.name, - alert_notification.type, - alert_notification.created, - alert_notification.updated, - alert_notification.settings - FROM alert_notification - `) + alert_notification.id, + alert_notification.org_id, + alert_notification.name, + alert_notification.type, + alert_notification.created, + alert_notification.updated, + alert_notification.settings, + alert_notification.is_default + FROM alert_notification + `) sql.WriteString(` WHERE alert_notification.org_id = ?`) params = append(params, query.OrgId) - if query.Name != "" { - sql.WriteString(` AND alert_notification.name = ?`) - params = append(params, query.Name) - } - - if query.Id != 0 { - sql.WriteString(` AND alert_notification.id = ?`) - params = append(params, query.Id) - } - + sql.WriteString(` AND ((alert_notification.is_default = 1)`) if len(query.Ids) > 0 { - sql.WriteString(` AND alert_notification.id IN (?` + strings.Repeat(",?", len(query.Ids)-1) + ")") + sql.WriteString(` OR alert_notification.id IN (?` + strings.Repeat(",?", len(query.Ids)-1) + ")") for _, v := range query.Ids { params = append(params, v) } } + sql.WriteString(`)`) + + results := make([]*m.AlertNotification, 0) + if err := x.Sql(sql.String(), params...).Find(&results); err != nil { + return err + } + + query.Result = results + return nil +} + +func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *xorm.Session) error { + var sql bytes.Buffer + params := make([]interface{}, 0) + + sql.WriteString(`SELECT + alert_notification.id, + alert_notification.org_id, + alert_notification.name, + alert_notification.type, + alert_notification.created, + alert_notification.updated, + alert_notification.settings, + alert_notification.is_default + FROM alert_notification + `) + + sql.WriteString(` WHERE alert_notification.org_id = ?`) + params = append(params, query.OrgId) + + if query.Name != "" || query.Id != 0 { + if query.Name != "" { + sql.WriteString(` AND alert_notification.name = ?`) + params = append(params, query.Name) + } + + if query.Id != 0 { + sql.WriteString(` AND alert_notification.id = ?`) + params = append(params, query.Id) + } + } results := make([]*m.AlertNotification, 0) if err := sess.Sql(sql.String(), params...).Find(&results); err != nil { return err } - query.Result = results + if len(results) == 0 { + query.Result = nil + } else { + query.Result = results[0] + } + return nil } func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error { return inTransaction(func(sess *xorm.Session) error { existingQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name} - err := getAlertNotificationsInternal(existingQuery, sess) + err := getAlertNotificationInternal(existingQuery, sess) if err != nil { return err } - if len(existingQuery.Result) > 0 { + if existingQuery.Result != nil { return fmt.Errorf("Alert notification name %s already exists", cmd.Name) } alertNotification := &m.AlertNotification{ - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Settings: cmd.Settings, - Created: time.Now(), - Updated: time.Now(), + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Settings: cmd.Settings, + Created: time.Now(), + Updated: time.Now(), + IsDefault: cmd.IsDefault, } if _, err = sess.Insert(alertNotification); err != nil { @@ -120,11 +171,11 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error { // check if name exists sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name} - if err := getAlertNotificationsInternal(sameNameQuery, sess); err != nil { + if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil { return err } - if len(sameNameQuery.Result) > 0 && sameNameQuery.Result[0].Id != current.Id { + if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id { return fmt.Errorf("Alert notification name %s already exists", cmd.Name) } @@ -132,6 +183,9 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error { current.Settings = cmd.Settings current.Name = cmd.Name current.Type = cmd.Type + current.IsDefault = cmd.IsDefault + + sess.UseBool("is_default") if affected, err := sess.Id(cmd.Id).Update(current); err != nil { return err diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 4cbcf13a000..d37062fb58f 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -23,7 +23,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { err := GetAlertNotifications(cmd) fmt.Printf("errror %v", err) So(err, ShouldBeNil) - So(len(cmd.Result), ShouldEqual, 0) + So(cmd.Result, ShouldBeNil) }) Convey("Can save Alert Notification", func() { @@ -63,20 +63,35 @@ func TestAlertNotificationSQLAccess(t *testing.T) { cmd1 := m.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, Settings: simplejson.New()} cmd2 := m.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, Settings: simplejson.New()} cmd3 := m.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, Settings: simplejson.New()} + cmd4 := m.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, Settings: simplejson.New()} + + otherOrg := m.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, Settings: simplejson.New()} So(CreateAlertNotificationCommand(&cmd1), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd2), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd3), ShouldBeNil) + So(CreateAlertNotificationCommand(&cmd4), ShouldBeNil) + So(CreateAlertNotificationCommand(&otherOrg), ShouldBeNil) Convey("search", func() { - query := &m.GetAlertNotificationsQuery{ + query := &m.GetAlertNotificationsToSendQuery{ Ids: []int64{cmd1.Result.Id, cmd2.Result.Id, 112341231}, OrgId: 1, } - err := GetAlertNotifications(query) + err := GetAlertNotificationsToSend(query) So(err, ShouldBeNil) - So(len(query.Result), ShouldEqual, 2) + So(len(query.Result), ShouldEqual, 3) + }) + + Convey("all", func() { + query := &m.GetAllAlertNotificationsQuery{ + OrgId: 1, + } + + err := GetAllAlertNotifications(query) + So(err, ShouldBeNil) + So(len(query.Result), ShouldEqual, 4) }) }) }) diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index e22b1c48c47..02126c2984a 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -47,7 +47,7 @@ func TestAlertingDataAccess(t *testing.T) { So(err2, ShouldBeNil) So(alert.Name, ShouldEqual, "Alerting title") So(alert.Message, ShouldEqual, "Alerting message") - So(alert.State, ShouldEqual, "pending") + So(alert.State, ShouldEqual, "no_data") So(alert.Frequency, ShouldEqual, 1) }) @@ -77,7 +77,7 @@ func TestAlertingDataAccess(t *testing.T) { So(query.Result[0].Name, ShouldEqual, "Name") Convey("Alert state should not be updated", func() { - So(query.Result[0].State, ShouldEqual, "pending") + So(query.Result[0].State, ShouldEqual, "no_data") }) }) diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 5cf4461b370..1b0f02fce09 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -3,6 +3,7 @@ package sqlstore import ( "bytes" "fmt" + "strings" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/services/annotations" @@ -38,16 +39,43 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I params = append(params, query.AlertId) } + if query.AlertId != 0 { + sql.WriteString(` AND alert_id = ?`) + params = append(params, query.AlertId) + } + + if query.DashboardId != 0 { + sql.WriteString(` AND dashboard_id = ?`) + params = append(params, query.DashboardId) + } + + if query.PanelId != 0 { + sql.WriteString(` AND panel_id = ?`) + params = append(params, query.PanelId) + } + + if query.From > 0 && query.To > 0 { + sql.WriteString(` AND epoch BETWEEN ? AND ?`) + params = append(params, query.From, query.To) + } + if query.Type != "" { sql.WriteString(` AND type = ?`) params = append(params, string(query.Type)) } + if len(query.NewState) > 0 { + sql.WriteString(` AND new_state IN (?` + strings.Repeat(",?", len(query.NewState)-1) + ")") + for _, v := range query.NewState { + params = append(params, v) + } + } + if query.Limit == 0 { query.Limit = 10 } - sql.WriteString(fmt.Sprintf("ORDER BY timestamp DESC LIMIT %v", query.Limit)) + sql.WriteString(fmt.Sprintf("ORDER BY epoch DESC LIMIT %v", query.Limit)) items := make([]*annotations.Item, 0) if err := x.Sql(sql.String(), params...).Find(&items); err != nil { diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index c028fd0fccc..2f1b40b2d61 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -43,7 +43,7 @@ func GetDataSourceByName(query *m.GetDataSourceByNameQuery) error { } func GetDataSources(query *m.GetDataSourcesQuery) error { - sess := x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name") + sess := x.Limit(1000, 0).Where("org_id=?", query.OrgId).Asc("name") query.Result = make([]*m.DataSource, 0) return sess.Find(&query.Result) diff --git a/pkg/services/sqlstore/migrations/alert_mig.go b/pkg/services/sqlstore/migrations/alert_mig.go index 63d61d8b196..b6956be41c7 100644 --- a/pkg/services/sqlstore/migrations/alert_mig.go +++ b/pkg/services/sqlstore/migrations/alert_mig.go @@ -62,5 +62,9 @@ func addAlertMigrations(mg *Migrator) { } mg.AddMigration("create alert_notification table v1", NewAddTableMigration(alert_notification)) + mg.AddMigration("Add column is_default", NewAddColumnMigration(alert_notification, &Column{ + Name: "is_default", Type: DB_Bool, Nullable: false, Default: "0", + })) mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0])) + } diff --git a/pkg/services/sqlstore/migrations/annotation_mig.go b/pkg/services/sqlstore/migrations/annotation_mig.go index 11b4eeed629..a307100ad3c 100644 --- a/pkg/services/sqlstore/migrations/annotation_mig.go +++ b/pkg/services/sqlstore/migrations/annotation_mig.go @@ -5,6 +5,7 @@ import ( ) func addAnnotationMig(mg *Migrator) { + table := Table{ Name: "annotation", Columns: []*Column{ @@ -12,6 +13,9 @@ func addAnnotationMig(mg *Migrator) { {Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "alert_id", Type: DB_BigInt, Nullable: true}, {Name: "user_id", Type: DB_BigInt, Nullable: true}, + {Name: "dashboard_id", Type: DB_BigInt, Nullable: true}, + {Name: "panel_id", Type: DB_BigInt, Nullable: true}, + {Name: "category_id", Type: DB_BigInt, Nullable: true}, {Name: "type", Type: DB_NVarchar, Length: 25, Nullable: false}, {Name: "title", Type: DB_Text, Nullable: false}, {Name: "text", Type: DB_Text, Nullable: false}, @@ -19,20 +23,25 @@ func addAnnotationMig(mg *Migrator) { {Name: "prev_state", Type: DB_NVarchar, Length: 25, Nullable: false}, {Name: "new_state", Type: DB_NVarchar, Length: 25, Nullable: false}, {Name: "data", Type: DB_Text, Nullable: false}, - {Name: "timestamp", Type: DB_DateTime, Nullable: false}, + {Name: "epoch", Type: DB_BigInt, Nullable: false}, }, Indices: []*Index{ {Cols: []string{"org_id", "alert_id"}, Type: IndexType}, {Cols: []string{"org_id", "type"}, Type: IndexType}, - {Cols: []string{"timestamp"}, Type: IndexType}, + {Cols: []string{"org_id", "category_id"}, Type: IndexType}, + {Cols: []string{"org_id", "dashboard_id", "panel_id", "epoch"}, Type: IndexType}, + {Cols: []string{"org_id", "epoch"}, Type: IndexType}, }, } - mg.AddMigration("create annotation table v1", NewAddTableMigration(table)) + mg.AddMigration("Drop old annotation table v4", NewDropTableMigration("annotation")) + + mg.AddMigration("create annotation table v5", NewAddTableMigration(table)) // create indices - mg.AddMigration("add index annotation org_id & alert_id ", NewAddIndexMigration(table, table.Indices[0])) - - mg.AddMigration("add index annotation org_id & type", NewAddIndexMigration(table, table.Indices[1])) - mg.AddMigration("add index annotation timestamp", NewAddIndexMigration(table, table.Indices[2])) + mg.AddMigration("add index annotation 0 v3", NewAddIndexMigration(table, table.Indices[0])) + mg.AddMigration("add index annotation 1 v3", NewAddIndexMigration(table, table.Indices[1])) + mg.AddMigration("add index annotation 2 v3", NewAddIndexMigration(table, table.Indices[2])) + mg.AddMigration("add index annotation 3 v3", NewAddIndexMigration(table, table.Indices[3])) + mg.AddMigration("add index annotation 4 v3", NewAddIndexMigration(table, table.Indices[4])) } diff --git a/pkg/services/sqlstore/migrations/dashboard_mig.go b/pkg/services/sqlstore/migrations/dashboard_mig.go index 4f286dce68a..283501d366f 100644 --- a/pkg/services/sqlstore/migrations/dashboard_mig.go +++ b/pkg/services/sqlstore/migrations/dashboard_mig.go @@ -120,4 +120,9 @@ func addDashboardMigration(mg *Migrator) { mg.AddMigration("Add index for plugin_id in dashboard", NewAddIndexMigration(dashboardV2, &Index{ Cols: []string{"org_id", "plugin_id"}, Type: IndexType, })) + + // dashboard_id index for dashboard_tag table + mg.AddMigration("Add index for dashboard_id in dashboard_tag", NewAddIndexMigration(dashboardTagV1, &Index{ + Cols: []string{"dashboard_id"}, Type: IndexType, + })) } diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index fd010c42ca2..33b026713a1 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -47,10 +47,11 @@ var ( BuildStamp int64 // Paths - LogsPath string - HomePath string - DataPath string - PluginsPath string + LogsPath string + HomePath string + DataPath string + PluginsPath string + CustomInitPath = "conf/custom.ini" // Log settings. LogModes []string @@ -312,7 +313,7 @@ func evalConfigValues() { func loadSpecifedConfigFile(configFile string) error { if configFile == "" { - configFile = filepath.Join(HomePath, "conf/custom.ini") + configFile = filepath.Join(HomePath, CustomInitPath) // return without error if custom file does not exist if !pathExists(configFile) { return nil diff --git a/pkg/setting/setting_oauth.go b/pkg/setting/setting_oauth.go index db2f0fb3802..71c4ade1468 100644 --- a/pkg/setting/setting_oauth.go +++ b/pkg/setting/setting_oauth.go @@ -11,8 +11,9 @@ type OAuthInfo struct { } type OAuther struct { - GitHub, Google, Twitter bool - OAuthInfos map[string]*OAuthInfo + GitHub, Google, Twitter, Generic bool + OAuthInfos map[string]*OAuthInfo + OAuthProviderName string } var OAuthService *OAuther diff --git a/pkg/social/common.go b/pkg/social/common.go new file mode 100644 index 00000000000..7bce5d2ae8f --- /dev/null +++ b/pkg/social/common.go @@ -0,0 +1,20 @@ +package social + +import ( + "fmt" + "strings" +) + +func isEmailAllowed(email string, allowedDomains []string) bool { + if len(allowedDomains) == 0 { + return true + } + + valid := false + for _, domain := range allowedDomains { + emailSuffix := fmt.Sprintf("@%s", domain) + valid = valid || strings.HasSuffix(email, emailSuffix) + } + + return valid +} diff --git a/pkg/social/generic_oauth.go b/pkg/social/generic_oauth.go new file mode 100644 index 00000000000..f016c87e201 --- /dev/null +++ b/pkg/social/generic_oauth.go @@ -0,0 +1,205 @@ +package social + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/grafana/grafana/pkg/models" + + "golang.org/x/oauth2" +) + +type GenericOAuth struct { + *oauth2.Config + allowedDomains []string + allowedOrganizations []string + apiUrl string + allowSignup bool + teamIds []int +} + +func (s *GenericOAuth) Type() int { + return int(models.GENERIC) +} + +func (s *GenericOAuth) IsEmailAllowed(email string) bool { + return isEmailAllowed(email, s.allowedDomains) +} + +func (s *GenericOAuth) IsSignupAllowed() bool { + return s.allowSignup +} + +func (s *GenericOAuth) IsTeamMember(client *http.Client) bool { + if len(s.teamIds) == 0 { + return true + } + + teamMemberships, err := s.FetchTeamMemberships(client) + if err != nil { + return false + } + + for _, teamId := range s.teamIds { + for _, membershipId := range teamMemberships { + if teamId == membershipId { + return true + } + } + } + + return false +} + +func (s *GenericOAuth) IsOrganizationMember(client *http.Client) bool { + if len(s.allowedOrganizations) == 0 { + return true + } + + organizations, err := s.FetchOrganizations(client) + if err != nil { + return false + } + + for _, allowedOrganization := range s.allowedOrganizations { + for _, organization := range organizations { + if organization == allowedOrganization { + return true + } + } + } + + return false +} + +func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) { + type Record struct { + Email string `json:"email"` + Primary bool `json:"primary"` + Verified bool `json:"verified"` + } + + emailsUrl := fmt.Sprintf(s.apiUrl + "/emails") + r, err := client.Get(emailsUrl) + if err != nil { + return "", err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return "", err + } + + var email = "" + for _, record := range records { + if record.Primary { + email = record.Email + } + } + + return email, nil +} + +func (s *GenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error) { + type Record struct { + Id int `json:"id"` + } + + membershipUrl := fmt.Sprintf(s.apiUrl + "/teams") + r, err := client.Get(membershipUrl) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return nil, err + } + + var ids = make([]int, len(records)) + for i, record := range records { + ids[i] = record.Id + } + + return ids, nil +} + +func (s *GenericOAuth) FetchOrganizations(client *http.Client) ([]string, error) { + type Record struct { + Login string `json:"login"` + } + + url := fmt.Sprintf(s.apiUrl + "/orgs") + r, err := client.Get(url) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return nil, err + } + + var logins = make([]string, len(records)) + for i, record := range records { + logins[i] = record.Login + } + + return logins, nil +} + +func (s *GenericOAuth) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { + var data struct { + Id int `json:"id"` + Name string `json:"login"` + Email string `json:"email"` + } + + var err error + client := s.Client(oauth2.NoContext, token) + r, err := client.Get(s.apiUrl) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + if err = json.NewDecoder(r.Body).Decode(&data); err != nil { + return nil, err + } + + userInfo := &BasicUserInfo{ + Identity: strconv.Itoa(data.Id), + Name: data.Name, + Email: data.Email, + } + + if !s.IsTeamMember(client) { + return nil, errors.New("User not a member of one of the required teams") + } + + if !s.IsOrganizationMember(client) { + return nil, errors.New("User not a member of one of the required organizations") + } + + if userInfo.Email == "" { + userInfo.Email, err = s.FetchPrivateEmail(client) + if err != nil { + return nil, err + } + } + + return userInfo, nil +} diff --git a/pkg/social/github_oauth.go b/pkg/social/github_oauth.go new file mode 100644 index 00000000000..40c8f2a2f7c --- /dev/null +++ b/pkg/social/github_oauth.go @@ -0,0 +1,213 @@ +package social + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/grafana/grafana/pkg/models" + + "golang.org/x/oauth2" +) + +type SocialGithub struct { + *oauth2.Config + allowedDomains []string + allowedOrganizations []string + apiUrl string + allowSignup bool + teamIds []int +} + +var ( + ErrMissingTeamMembership = errors.New("User not a member of one of the required teams") +) + +var ( + ErrMissingOrganizationMembership = errors.New("User not a member of one of the required organizations") +) + +func (s *SocialGithub) Type() int { + return int(models.GITHUB) +} + +func (s *SocialGithub) IsEmailAllowed(email string) bool { + return isEmailAllowed(email, s.allowedDomains) +} + +func (s *SocialGithub) IsSignupAllowed() bool { + return s.allowSignup +} + +func (s *SocialGithub) IsTeamMember(client *http.Client) bool { + if len(s.teamIds) == 0 { + return true + } + + teamMemberships, err := s.FetchTeamMemberships(client) + if err != nil { + return false + } + + for _, teamId := range s.teamIds { + for _, membershipId := range teamMemberships { + if teamId == membershipId { + return true + } + } + } + + return false +} + +func (s *SocialGithub) IsOrganizationMember(client *http.Client) bool { + if len(s.allowedOrganizations) == 0 { + return true + } + + organizations, err := s.FetchOrganizations(client) + if err != nil { + return false + } + + for _, allowedOrganization := range s.allowedOrganizations { + for _, organization := range organizations { + if organization == allowedOrganization { + return true + } + } + } + + return false +} + +func (s *SocialGithub) FetchPrivateEmail(client *http.Client) (string, error) { + type Record struct { + Email string `json:"email"` + Primary bool `json:"primary"` + Verified bool `json:"verified"` + } + + emailsUrl := fmt.Sprintf(s.apiUrl + "/emails") + r, err := client.Get(emailsUrl) + if err != nil { + return "", err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return "", err + } + + var email = "" + for _, record := range records { + if record.Primary { + email = record.Email + } + } + + return email, nil +} + +func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error) { + type Record struct { + Id int `json:"id"` + } + + membershipUrl := fmt.Sprintf(s.apiUrl + "/teams") + r, err := client.Get(membershipUrl) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return nil, err + } + + var ids = make([]int, len(records)) + for i, record := range records { + ids[i] = record.Id + } + + return ids, nil +} + +func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error) { + type Record struct { + Login string `json:"login"` + } + + url := fmt.Sprintf(s.apiUrl + "/orgs") + r, err := client.Get(url) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + var records []Record + + if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + return nil, err + } + + var logins = make([]string, len(records)) + for i, record := range records { + logins[i] = record.Login + } + + return logins, nil +} + +func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { + var data struct { + Id int `json:"id"` + Name string `json:"login"` + Email string `json:"email"` + } + + var err error + client := s.Client(oauth2.NoContext, token) + r, err := client.Get(s.apiUrl) + if err != nil { + return nil, err + } + + defer r.Body.Close() + + if err = json.NewDecoder(r.Body).Decode(&data); err != nil { + return nil, err + } + + userInfo := &BasicUserInfo{ + Identity: strconv.Itoa(data.Id), + Name: data.Name, + Email: data.Email, + } + + if !s.IsTeamMember(client) { + return nil, ErrMissingTeamMembership + } + + if !s.IsOrganizationMember(client) { + return nil, ErrMissingOrganizationMembership + } + + if userInfo.Email == "" { + userInfo.Email, err = s.FetchPrivateEmail(client) + if err != nil { + return nil, err + } + } + + return userInfo, nil +} diff --git a/pkg/social/google_oauth.go b/pkg/social/google_oauth.go new file mode 100644 index 00000000000..7f0fdcc250a --- /dev/null +++ b/pkg/social/google_oauth.go @@ -0,0 +1,52 @@ +package social + +import ( + "encoding/json" + + "github.com/grafana/grafana/pkg/models" + + "golang.org/x/oauth2" +) + +type SocialGoogle struct { + *oauth2.Config + allowedDomains []string + apiUrl string + allowSignup bool +} + +func (s *SocialGoogle) Type() int { + return int(models.GOOGLE) +} + +func (s *SocialGoogle) IsEmailAllowed(email string) bool { + return isEmailAllowed(email, s.allowedDomains) +} + +func (s *SocialGoogle) IsSignupAllowed() bool { + return s.allowSignup +} + +func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { + var data struct { + Id string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + } + var err error + + client := s.Client(oauth2.NoContext, token) + r, err := client.Get(s.apiUrl) + if err != nil { + return nil, err + } + defer r.Body.Close() + if err = json.NewDecoder(r.Body).Decode(&data); err != nil { + return nil, err + } + return &BasicUserInfo{ + Identity: data.Id, + Name: data.Name, + Email: data.Email, + }, nil +} diff --git a/pkg/social/social.go b/pkg/social/social.go index 4a8cb8bac5d..66d0f5fa778 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -1,14 +1,8 @@ package social import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "strconv" "strings" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "golang.org/x/net/context" @@ -42,7 +36,7 @@ func NewOAuthService() { setting.OAuthService = &setting.OAuther{} setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo) - allOauthes := []string{"github", "google"} + allOauthes := []string{"github", "google", "generic_oauth"} for _, name := range allOauthes { sec := setting.Cfg.Section("auth." + name) @@ -98,269 +92,21 @@ func NewOAuthService() { allowSignup: info.AllowSignup, } } - } -} -func isEmailAllowed(email string, allowedDomains []string) bool { - if len(allowedDomains) == 0 { - return true - } - - valid := false - for _, domain := range allowedDomains { - emailSuffix := fmt.Sprintf("@%s", domain) - valid = valid || strings.HasSuffix(email, emailSuffix) - } - - return valid -} - -type SocialGithub struct { - *oauth2.Config - allowedDomains []string - allowedOrganizations []string - apiUrl string - allowSignup bool - teamIds []int -} - -var ( - ErrMissingTeamMembership = errors.New("User not a member of one of the required teams") -) - -var ( - ErrMissingOrganizationMembership = errors.New("User not a member of one of the required organizations") -) - -func (s *SocialGithub) Type() int { - return int(models.GITHUB) -} - -func (s *SocialGithub) IsEmailAllowed(email string) bool { - return isEmailAllowed(email, s.allowedDomains) -} - -func (s *SocialGithub) IsSignupAllowed() bool { - return s.allowSignup -} - -func (s *SocialGithub) IsTeamMember(client *http.Client) bool { - if len(s.teamIds) == 0 { - return true - } - - teamMemberships, err := s.FetchTeamMemberships(client) - if err != nil { - return false - } - - for _, teamId := range s.teamIds { - for _, membershipId := range teamMemberships { - if teamId == membershipId { - return true + // Generic - Uses the same scheme as Github. + if name == "generic_oauth" { + setting.OAuthService.Generic = true + setting.OAuthService.OAuthProviderName = sec.Key("oauth_provider_name").String() + teamIds := sec.Key("team_ids").Ints(",") + allowedOrganizations := sec.Key("allowed_organizations").Strings(" ") + SocialMap["generic_oauth"] = &GenericOAuth{ + Config: &config, + allowedDomains: info.AllowedDomains, + apiUrl: info.ApiUrl, + allowSignup: info.AllowSignup, + teamIds: teamIds, + allowedOrganizations: allowedOrganizations, } } } - - return false -} - -func (s *SocialGithub) IsOrganizationMember(client *http.Client) bool { - if len(s.allowedOrganizations) == 0 { - return true - } - - organizations, err := s.FetchOrganizations(client) - if err != nil { - return false - } - - for _, allowedOrganization := range s.allowedOrganizations { - for _, organization := range organizations { - if organization == allowedOrganization { - return true - } - } - } - - return false -} - -func (s *SocialGithub) FetchPrivateEmail(client *http.Client) (string, error) { - type Record struct { - Email string `json:"email"` - Primary bool `json:"primary"` - Verified bool `json:"verified"` - } - - emailsUrl := fmt.Sprintf(s.apiUrl + "/emails") - r, err := client.Get(emailsUrl) - if err != nil { - return "", err - } - - defer r.Body.Close() - - var records []Record - - if err = json.NewDecoder(r.Body).Decode(&records); err != nil { - return "", err - } - - var email = "" - for _, record := range records { - if record.Primary { - email = record.Email - } - } - - return email, nil -} - -func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error) { - type Record struct { - Id int `json:"id"` - } - - membershipUrl := fmt.Sprintf(s.apiUrl + "/teams") - r, err := client.Get(membershipUrl) - if err != nil { - return nil, err - } - - defer r.Body.Close() - - var records []Record - - if err = json.NewDecoder(r.Body).Decode(&records); err != nil { - return nil, err - } - - var ids = make([]int, len(records)) - for i, record := range records { - ids[i] = record.Id - } - - return ids, nil -} - -func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error) { - type Record struct { - Login string `json:"login"` - } - - url := fmt.Sprintf(s.apiUrl + "/orgs") - r, err := client.Get(url) - if err != nil { - return nil, err - } - - defer r.Body.Close() - - var records []Record - - if err = json.NewDecoder(r.Body).Decode(&records); err != nil { - return nil, err - } - - var logins = make([]string, len(records)) - for i, record := range records { - logins[i] = record.Login - } - - return logins, nil -} - -func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { - var data struct { - Id int `json:"id"` - Name string `json:"login"` - Email string `json:"email"` - } - - var err error - client := s.Client(oauth2.NoContext, token) - r, err := client.Get(s.apiUrl) - if err != nil { - return nil, err - } - - defer r.Body.Close() - - if err = json.NewDecoder(r.Body).Decode(&data); err != nil { - return nil, err - } - - userInfo := &BasicUserInfo{ - Identity: strconv.Itoa(data.Id), - Name: data.Name, - Email: data.Email, - } - - if !s.IsTeamMember(client) { - return nil, ErrMissingTeamMembership - } - - if !s.IsOrganizationMember(client) { - return nil, ErrMissingOrganizationMembership - } - - if userInfo.Email == "" { - userInfo.Email, err = s.FetchPrivateEmail(client) - if err != nil { - return nil, err - } - } - - return userInfo, nil -} - -// ________ .__ -// / _____/ ____ ____ ____ | | ____ -// / \ ___ / _ \ / _ \ / ___\| | _/ __ \ -// \ \_\ ( <_> | <_> ) /_/ > |_\ ___/ -// \______ /\____/ \____/\___ /|____/\___ > -// \/ /_____/ \/ - -type SocialGoogle struct { - *oauth2.Config - allowedDomains []string - apiUrl string - allowSignup bool -} - -func (s *SocialGoogle) Type() int { - return int(models.GOOGLE) -} - -func (s *SocialGoogle) IsEmailAllowed(email string) bool { - return isEmailAllowed(email, s.allowedDomains) -} - -func (s *SocialGoogle) IsSignupAllowed() bool { - return s.allowSignup -} - -func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { - var data struct { - Id string `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - } - var err error - - client := s.Client(oauth2.NoContext, token) - r, err := client.Get(s.apiUrl) - if err != nil { - return nil, err - } - defer r.Body.Close() - if err = json.NewDecoder(r.Body).Decode(&data); err != nil { - return nil, err - } - return &BasicUserInfo{ - Identity: data.Id, - Name: data.Name, - Email: data.Email, - }, nil } diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 0247ad0dca8..4042702378c 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -1,6 +1,7 @@ package graphite import ( + "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -11,13 +12,10 @@ import ( "time" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb" ) -var ( - HttpClient = http.Client{Timeout: time.Duration(10 * time.Second)} -) - type GraphiteExecutor struct { *tsdb.DataSourceInfo } @@ -26,11 +24,23 @@ func NewGraphiteExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { return &GraphiteExecutor{dsInfo} } -var glog log.Logger +var ( + glog log.Logger + HttpClient http.Client +) func init() { glog = log.New("tsdb.graphite") tsdb.RegisterExecutor("graphite", NewGraphiteExecutor) + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + HttpClient = http.Client{ + Timeout: time.Duration(10 * time.Second), + Transport: tr, + } } func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult { @@ -47,7 +57,9 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC formData["target"] = []string{query.Query} } - glog.Info("Graphite request body", "formdata", formData.Encode()) + if setting.Env == setting.DEV { + glog.Debug("Graphite request", "params", formData) + } req, err := e.createRequest(formData) if err != nil { @@ -73,6 +85,10 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC Name: series.Target, Points: series.DataPoints, }) + + if setting.Env == setting.DEV { + glog.Debug("Graphite response", "target", series.Target, "datapoints", len(series.DataPoints)) + } } result.QueryResults["A"] = queryRes diff --git a/pkg/tsdb/graphite/types.go b/pkg/tsdb/graphite/types.go index 4cd1b601bbc..085b1fb2b94 100644 --- a/pkg/tsdb/graphite/types.go +++ b/pkg/tsdb/graphite/types.go @@ -1,6 +1,6 @@ package graphite type TargetResponseDTO struct { - Target string `json:"target"` - DataPoints [][2]float64 `json:"datapoints"` + Target string `json:"target"` + DataPoints [][2]*float64 `json:"datapoints"` } diff --git a/pkg/tsdb/models.go b/pkg/tsdb/models.go index 2954c630b68..05a8b13ef84 100644 --- a/pkg/tsdb/models.go +++ b/pkg/tsdb/models.go @@ -46,13 +46,13 @@ type QueryResult struct { } type TimeSeries struct { - Name string `json:"name"` - Points [][2]float64 `json:"points"` + Name string `json:"name"` + Points [][2]*float64 `json:"points"` } type TimeSeriesSlice []*TimeSeries -func NewTimeSeries(name string, points [][2]float64) *TimeSeries { +func NewTimeSeries(name string, points [][2]*float64) *TimeSeries { return &TimeSeries{ Name: name, Points: points, diff --git a/public/app/core/components/query_part/query_part.ts b/public/app/core/components/query_part/query_part.ts index 90724f65d2d..fcd337a0a26 100644 --- a/public/app/core/components/query_part/query_part.ts +++ b/public/app/core/components/query_part/query_part.ts @@ -54,9 +54,9 @@ export class QueryPart { // handle optional parameters // if string contains ',' and next param is optional, split and update both if (this.hasMultipleParamsInString(strValue, index)) { - _.each(strValue.split(','), function(partVal: string, idx) { + _.each(strValue.split(','), (partVal, idx) => { this.updateParam(partVal.trim(), idx); - }, this); + }); return; } diff --git a/public/app/core/controllers/login_ctrl.js b/public/app/core/controllers/login_ctrl.js index 748b64dc5c1..8067202d511 100644 --- a/public/app/core/controllers/login_ctrl.js +++ b/public/app/core/controllers/login_ctrl.js @@ -6,6 +6,12 @@ define([ function (angular, coreModule, config) { 'use strict'; + var failCodes = { + "1000": "Required Github team membership not fulfilled", + "1001": "Required Github organization membership not fulfilled", + "1002": "Required email domain not fulfilled", + }; + coreModule.default.controller('LoginCtrl', function($scope, backendSrv, contextSrv, $location) { $scope.formModel = { user: '', @@ -17,8 +23,10 @@ function (angular, coreModule, config) { $scope.googleAuthEnabled = config.googleAuthEnabled; $scope.githubAuthEnabled = config.githubAuthEnabled; - $scope.oauthEnabled = config.githubAuthEnabled || config.googleAuthEnabled; + $scope.oauthEnabled = config.githubAuthEnabled || config.googleAuthEnabled || config.genericOAuthEnabled; $scope.allowUserPassLogin = config.allowUserPassLogin; + $scope.genericOAuthEnabled = config.genericOAuthEnabled; + $scope.oauthProviderName = config.oauthProviderName; $scope.disableUserSignUp = config.disableUserSignUp; $scope.loginHint = config.loginHint; @@ -29,8 +37,8 @@ function (angular, coreModule, config) { $scope.$watch("loginMode", $scope.loginModeChanged); var params = $location.search(); - if (params.failedMsg) { - $scope.appEvent('alert-warning', ['Login Failed', params.failedMsg]); + if (params.failCode) { + $scope.appEvent('alert-warning', ['Login Failed', failCodes[params.failCode]]); delete params.failedMsg; $location.search(params); } diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index 36eac942c3e..d51260395de 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -40,7 +40,7 @@ function (_, $, coreModule) { } $scope.$apply(function() { - var selected = _.findWhere($scope.altSegments, { value: value }); + var selected = _.find($scope.altSegments, {value: value}); if (selected) { segment.value = selected.value; segment.html = selected.html; @@ -76,10 +76,8 @@ function (_, $, coreModule) { }; $scope.source = function(query, callback) { - if (options) { return options; } - $scope.$apply(function() { - $scope.getOptions().then(function(altSegments) { + $scope.getOptions({ measurementFilter: query }).then(function(altSegments) { $scope.altSegments = altSegments; options = _.map($scope.altSegments, function(alt) { return alt.value; }); @@ -174,7 +172,7 @@ function (_, $, coreModule) { pre: function postLink($scope, elem, attrs) { $scope.valueToSegment = function(value) { - var option = _.findWhere($scope.options, {value: value}); + var option = _.find($scope.options, {value: value}); var segment = { cssClass: attrs.cssClass, custom: attrs.custom, @@ -197,7 +195,7 @@ function (_, $, coreModule) { $scope.onSegmentChange = function() { if ($scope.options) { - var option = _.findWhere($scope.options, {text: $scope.segment.value}); + var option = _.find($scope.options, {text: $scope.segment.value}); if (option && option.value !== $scope.property) { $scope.property = option.value; } else if (attrs.custom !== 'false') { diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index dbe9932d574..60685fae74e 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -136,12 +136,12 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ } // Annotations case "annotations-query-ctrl": { - return System.import(scope.currentDatasource.meta.module).then(function(dsModule) { + return System.import(scope.ctrl.currentDatasource.meta.module).then(function(dsModule) { return { - baseUrl: scope.currentDatasource.meta.baseUrl, - name: 'annotations-query-ctrl-' + scope.currentDatasource.meta.id, + baseUrl: scope.ctrl.currentDatasource.meta.baseUrl, + name: 'annotations-query-ctrl-' + scope.ctrl.currentDatasource.meta.id, bindings: {annotation: "=", datasource: "="}, - attrs: {"annotation": "currentAnnotation", datasource: "currentDatasource"}, + attrs: {"annotation": "ctrl.currentAnnotation", datasource: "ctrl.currentDatasource"}, Component: dsModule.AnnotationsQueryCtrl, }; }); diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js index 60d257d6fa8..d97124f3406 100644 --- a/public/app/core/directives/value_select_dropdown.js +++ b/public/app/core/directives/value_select_dropdown.js @@ -51,7 +51,7 @@ function (angular, _, coreModule) { }); // convert values to text - var currentTexts = _.pluck(selectedAndNotInTag, 'text'); + var currentTexts = _.map(selectedAndNotInTag, 'text'); // join texts vm.linkText = currentTexts.join(' + '); @@ -167,7 +167,7 @@ function (angular, _, coreModule) { _.each(vm.tags, function(tag) { if (tag.selected) { _.each(tag.values, function(value) { - if (!_.findWhere(vm.selectedValues, {value: value})) { + if (!_.find(vm.selectedValues, {value: value})) { tag.selected = false; } }); @@ -175,8 +175,8 @@ function (angular, _, coreModule) { }); vm.selectedTags = _.filter(vm.tags, {selected: true}); - vm.variable.current.value = _.pluck(vm.selectedValues, 'value'); - vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + '); + vm.variable.current.value = _.map(vm.selectedValues, 'value'); + vm.variable.current.text = _.map(vm.selectedValues, 'text').join(' + '); vm.variable.current.tags = vm.selectedTags; if (!vm.variable.multi) { diff --git a/public/app/core/lodash_extended.js b/public/app/core/lodash_extended.js index 4edae35f02d..6487b19193e 100644 --- a/public/app/core/lodash_extended.js +++ b/public/app/core/lodash_extended.js @@ -19,7 +19,7 @@ function () { return variable === value ? alt : value; }, toggleInOut: function(array,value) { - if(_.contains(array,value)) { + if(_.includes(array,value)) { array = _.without(array,value); } else { array.push(value); diff --git a/public/app/core/routes/bundle_loader.ts b/public/app/core/routes/bundle_loader.ts index 3d275aeda36..473ac66b4b9 100644 --- a/public/app/core/routes/bundle_loader.ts +++ b/public/app/core/routes/bundle_loader.ts @@ -2,21 +2,22 @@ export class BundleLoader { lazy: any; - loadingDefer: any; constructor(bundleName) { + var defer = null; + this.lazy = ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => { - if (this.loadingDefer) { - return this.loadingDefer.promise; + if (defer) { + return defer.promise; } - this.loadingDefer = $q.defer(); + defer = $q.defer(); System.import(bundleName).then(() => { - this.loadingDefer.resolve(); + defer.resolve(); }); - return this.loadingDefer.promise; + return defer.promise; }]; } diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 4ef329f975e..286f03db5f5 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -80,28 +80,27 @@ export class AlertSrv { showConfirmModal(payload) { var scope = this.$rootScope.$new(); - scope.title = payload.title; - scope.text = payload.text; - scope.text2 = payload.text2; - scope.confirmTextRequired = payload.confirmText !== undefined && payload.confirmText !== ""; - scope.onConfirm = function() { - if (!scope.confirmTextRequired || (scope.confirmTextRequired && scope.confirmTextValid)) { - payload.onConfirm(); - scope.dismiss(); - } + payload.onConfirm(); + scope.dismiss(); }; scope.updateConfirmText = function(value) { scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase(); }; + scope.title = payload.title; + scope.text = payload.text; + scope.text2 = payload.text2; + scope.confirmText = payload.confirmText; + scope.onConfirm = payload.onConfirm; scope.onAltAction = payload.onAltAction; scope.altActionText = payload.altActionText; scope.icon = payload.icon || "fa-check"; scope.yesText = payload.yesText || "Yes"; scope.noText = payload.noText || "Cancel"; + scope.confirmTextValid = scope.confirmText ? false : true; var confirmModal = this.$modal({ template: 'public/app/partials/confirm_modal.html', diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index 41d99c3198f..ac00528db20 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -9,6 +9,7 @@ export class User { isGrafanaAdmin: any; isSignedIn: any; orgRole: any; + timezone: string; constructor() { if (config.bootData.user) { diff --git a/public/app/core/utils/datemath.ts b/public/app/core/utils/datemath.ts index 467d3750c9a..2aa793016ba 100644 --- a/public/app/core/utils/datemath.ts +++ b/public/app/core/utils/datemath.ts @@ -93,7 +93,7 @@ export function parseDateMath(mathString, time, roundUp?) { } unit = mathString.charAt(i++); - if (!_.contains(units, unit)) { + if (!_.includes(units, unit)) { return undefined; } else { if (type === 0) { diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index c0d162b0ece..cf80d671d71 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -9,6 +9,10 @@ function($, _, moment) { var kbn = {}; kbn.valueFormats = {}; + kbn.regexEscape = function(value) { + return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&'); + }; + ///// HELPER FUNCTIONS ///// kbn.round_interval = function(interval) { diff --git a/public/app/features/admin/adminEditUserCtrl.js b/public/app/features/admin/adminEditUserCtrl.js index d8500a845d3..104ccff99cd 100644 --- a/public/app/features/admin/adminEditUserCtrl.js +++ b/public/app/features/admin/adminEditUserCtrl.js @@ -81,20 +81,20 @@ function (angular, _) { $scope.searchOrgs = function(queryStr, callback) { if ($scope.orgsSearchCache.length > 0) { - callback(_.pluck($scope.orgsSearchCache, "name")); + callback(_.map($scope.orgsSearchCache, "name")); return; } backendSrv.get('/api/orgs', {query: ''}).then(function(result) { $scope.orgsSearchCache = result; - callback(_.pluck(result, "name")); + callback(_.map(result, "name")); }); }; $scope.addOrgUser = function() { if (!$scope.addOrgForm.$valid) { return; } - var orgInfo = _.findWhere($scope.orgsSearchCache, {name: $scope.newOrg.name}); + var orgInfo = _.find($scope.orgsSearchCache, {name: $scope.newOrg.name}); if (!orgInfo) { return; } $scope.newOrg.loginOrEmail = $scope.user.login; diff --git a/public/app/features/alerting/alert_def.ts b/public/app/features/alerting/alert_def.ts index 0b8efbe9889..8e9a86735ad 100644 --- a/public/app/features/alerting/alert_def.ts +++ b/public/app/features/alerting/alert_def.ts @@ -1,6 +1,6 @@ /// - +import _ from 'lodash'; import { QueryPartDef, QueryPart, @@ -36,15 +36,17 @@ var reducerTypes = [ {text: 'count()', value: 'count'}, ]; +var noDataModes = [ + {text: 'OK', value: 'ok'}, + {text: 'Alerting', value: 'alerting'}, + {text: 'No Data', value: 'no_data'}, +]; + function createReducerPart(model) { var def = new QueryPartDef({type: model.type, defaultParams: []}); return new QueryPart(model, def); } -var severityLevels = { - 'critical': {text: 'Critical', iconClass: 'icon-gf icon-gf-critical', stateClass: 'alert-state-critical'}, - 'warning': {text: 'Warning', iconClass: 'icon-gf icon-gf-warning', stateClass: 'alert-state-warning'}, -}; function getStateDisplayModel(state) { switch (state) { @@ -55,23 +57,16 @@ function getStateDisplayModel(state) { stateClass: 'alert-state-ok' }; } - case 'critical': { + case 'alerting': { return { - text: 'CRITICAL', + text: 'ALERTING', iconClass: 'icon-gf icon-gf-critical', stateClass: 'alert-state-critical' }; } - case 'warning': { + case 'no_data': { return { - text: 'WARNING', - iconClass: 'icon-gf icon-gf-warning', - stateClass: 'alert-state-warning' - }; - } - case 'pending': { - return { - text: 'PENDING', + text: 'NO DATA', iconClass: "fa fa-question", stateClass: 'alert-state-warning' }; @@ -94,12 +89,23 @@ function getStateDisplayModel(state) { } } +function joinEvalMatches(matches, seperator: string) { + return _.reduce(matches, (res, ev)=> { + if (ev.Metric !== undefined && ev.Value !== undefined) { + res.push(ev.Metric + "=" + ev.Value); + } + + return res; + }, []).join(seperator); +} + export default { alertQueryDef: alertQueryDef, getStateDisplayModel: getStateDisplayModel, conditionTypes: conditionTypes, evalFunctions: evalFunctions, - severityLevels: severityLevels, + noDataModes: noDataModes, reducerTypes: reducerTypes, createReducerPart: createReducerPart, + joinEvalMatches: joinEvalMatches, }; diff --git a/public/app/features/alerting/alert_list_ctrl.ts b/public/app/features/alerting/alert_list_ctrl.ts index b1ae2730fbb..4b429e961a7 100644 --- a/public/app/features/alerting/alert_list_ctrl.ts +++ b/public/app/features/alerting/alert_list_ctrl.ts @@ -13,9 +13,8 @@ export class AlertListCtrl { stateFilters = [ {text: 'All', value: null}, {text: 'OK', value: 'ok'}, - {text: 'Pending', value: 'pending'}, - {text: 'Warning', value: 'warning'}, - {text: 'Critical', value: 'critical'}, + {text: 'Alerting', value: 'alerting'}, + {text: 'No Data', value: 'no_data'}, {text: 'Execution Error', value: 'execution_error'}, ]; diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index c5ed6187f35..01cacaf9740 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -17,7 +17,7 @@ export class AlertTabCtrl { alert: any; conditionModels: any; evalFunctions: any; - severityLevels: any; + noDataModes: any; addNotificationSegment; notifications; alertNotifications; @@ -40,7 +40,7 @@ export class AlertTabCtrl { this.subTabIndex = 0; this.evalFunctions = alertDef.evalFunctions; this.conditionTypes = alertDef.conditionTypes; - this.severityLevels = alertDef.severityLevels; + this.noDataModes = alertDef.noDataModes; this.appSubUrl = config.appSubUrl; } @@ -68,24 +68,30 @@ export class AlertTabCtrl { this.notifications = res; _.each(this.alert.notifications, item => { - var model = _.findWhere(this.notifications, {id: item.id}); + var model = _.find(this.notifications, {id: item.id}); if (model) { model.iconClass = this.getNotificationIcon(model.type); this.alertNotifications.push(model); } }); - }).then(() => { - this.backendSrv.get(`/api/alert-history?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}`).then(res => { - this.alertHistory = _.map(res, ah => { - ah.time = moment(ah.timestamp).format('MMM D, YYYY HH:mm:ss'); - ah.stateModel = alertDef.getStateDisplayModel(ah.newState); - ah.metrics = _.map(ah.data, ev=> { - return ev.Metric + "=" + ev.Value; - }).join(', '); + _.each(this.notifications, item => { + if (item.isDefault) { + item.iconClass = this.getNotificationIcon(item.type); + item.bgColor = "#00678b"; + this.alertNotifications.push(item); + } + }); + }); + } - return ah; - }); + getAlertHistory() { + this.backendSrv.get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50`).then(res => { + this.alertHistory = _.map(res, ah => { + ah.time = moment(ah.time).format('MMM D, YYYY HH:mm:ss'); + ah.stateModel = alertDef.getStateDisplayModel(ah.newState); + ah.metrics = alertDef.joinEvalMatches(ah.data, ', '); + return ah; }); }); } @@ -104,14 +110,25 @@ export class AlertTabCtrl { })); } + changeTabIndex(newTabIndex) { + this.subTabIndex = newTabIndex; + + if (this.subTabIndex === 2) { + this.getAlertHistory(); + } + } notificationAdded() { - var model = _.findWhere(this.notifications, {name: this.addNotificationSegment.value}); + var model = _.find(this.notifications, {name: this.addNotificationSegment.value}); if (!model) { return; } - this.alertNotifications.push({name: model.name, iconClass: this.getNotificationIcon(model.type)}); + this.alertNotifications.push({ + name: model.name, + iconClass: this.getNotificationIcon(model.type), + isDefault: false + }); this.alert.notifications.push({id: model.id}); // reset plus button @@ -125,14 +142,18 @@ export class AlertTabCtrl { } initModel() { - var alert = this.alert = this.panel.alert = this.panel.alert || {}; + var alert = this.alert = this.panel.alert = this.panel.alert || {enabled: false}; + + if (!this.alert.enabled) { + return; + } alert.conditions = alert.conditions || []; if (alert.conditions.length === 0) { alert.conditions.push(this.buildDefaultCondition()); } - alert.severity = alert.severity || 'critical'; + alert.noDataState = alert.noDataState || 'no_data'; alert.frequency = alert.frequency || '60s'; alert.handler = alert.handler || 1; alert.notifications = alert.notifications || []; @@ -145,11 +166,9 @@ export class AlertTabCtrl { return memo; }, []); - if (this.alert.enabled) { - this.panelCtrl.editingThresholds = true; - } - ThresholdMapper.alertToGraphThresholds(this.panel); + + this.panelCtrl.editingThresholds = true; this.panelCtrl.render(); } @@ -173,6 +192,10 @@ export class AlertTabCtrl { } validateModel() { + if (!this.alert.enabled) { + return; + } + let firstTarget; var fixed = false; let foundTarget = null; @@ -295,11 +318,6 @@ export class AlertTabCtrl { this.panelCtrl.render(); } - severityChanged() { - ThresholdMapper.alertToGraphThresholds(this.panel); - this.panelCtrl.render(); - } - evaluatorTypeChanged(evaluator) { // ensure params array is correct length switch (evaluator.type) { diff --git a/public/app/features/alerting/notification_edit_ctrl.ts b/public/app/features/alerting/notification_edit_ctrl.ts index fc33118cb00..de5703a0631 100644 --- a/public/app/features/alerting/notification_edit_ctrl.ts +++ b/public/app/features/alerting/notification_edit_ctrl.ts @@ -7,6 +7,8 @@ import config from 'app/core/config'; export class AlertNotificationEditCtrl { model: any; + showTest: boolean = false; + testSeverity: string = "critical"; /** @ngInject */ constructor(private $routeParams, private backendSrv, private $scope, private $location) { @@ -15,7 +17,10 @@ export class AlertNotificationEditCtrl { } else { this.model = { type: 'email', - settings: {} + settings: { + severityFilter: 'none' + }, + isDefault: false }; } } @@ -47,6 +52,23 @@ export class AlertNotificationEditCtrl { typeChanged() { this.model.settings = {}; } + + toggleTest() { + this.showTest = !this.showTest; + } + + testNotification() { + var payload = { + name: this.model.name, + type: this.model.type, + settings: this.model.settings, + }; + + this.backendSrv.post(`/api/alert-notifications/test`, payload) + .then(res => { + this.$scope.appEvent('alert-succes', ['Test notification sent', '']); + }); + } } coreModule.controller('AlertNotificationEditCtrl', AlertNotificationEditCtrl); diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index e755496b732..3d6e183d93f 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -2,15 +2,15 @@