diff --git a/.circleci/config.yml b/.circleci/config.yml index c2fde3b..e180bbe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,12 +9,8 @@ jobs: working_directory: /go/src/github.com/xchapter7x/clarity steps: - checkout - - setup_remote_docker: - docker_layer_caching: true - run: sudo chmod -R 777 /usr/local - - run: go get -u github.com/maxbrunsfeld/counterfeiter - - run: go get github.com/DATA-DOG/godog/cmd/godog && mv /go/bin/godog /usr/local/bin/. - - run: dep ensure + - run: go get github.com/cucumber/godog/cmd/godog && mv /go/bin/godog /usr/local/bin/. - run: make unit - run: make build - run: mv cmd/clarity/build build diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..1264115 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,13 @@ +name: Greetings + +on: [pull_request, issues] + +jobs: + greeting: + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: 'Welcome and thank you for submitting this issue!! :)' + pr-message: 'Welcome and thank you for submitting this PR!! :)' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..b8e93ab --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue has been inactive for a while and is now considered stale' + stale-pr-message: 'This PRE has been inactive for a while and is now considered stale' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' diff --git a/.gitignore b/.gitignore index 2d4c299..499f083 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor build/clarity* cmd/clarity/build +.DS_Store diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index fe35949..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,110 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:276b1553a866a15eeb3817f8689b51b69f1903a38ad7692bb369ff5b1e171910" - name = "github.com/DATA-DOG/godog" - packages = [ - ".", - "colors", - "gherkin", - ] - pruneopts = "UT" - revision = "d1db163b8f115dc74e1f504fc6a5558bdb5b3256" - version = "v0.7.10" - -[[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:7a137fb7718928e473b7d805434ae563ec41790d3d227cdc64e8b14d1cab8a1f" - name = "github.com/onsi/gomega" - packages = [ - ".", - "format", - "internal/assertion", - "internal/asyncassertion", - "internal/oraclematcher", - "internal/testingtsupport", - "matchers", - "matchers/support/goraph/bipartitegraph", - "matchers/support/goraph/edge", - "matchers/support/goraph/node", - "matchers/support/goraph/util", - "types", - ] - pruneopts = "UT" - revision = "65fb64232476ad9046e57c26cd0bff3d3a8dc6cd" - version = "v1.4.3" - -[[projects]] - branch = "master" - digest = "1:03a8ff54c1a62710fe21a08ad9ccd554ac90232c4fd34c2731f65dd47e1f2077" - name = "golang.org/x/net" - packages = [ - "html", - "html/atom", - "html/charset", - ] - pruneopts = "UT" - revision = "3a22650c66bd7f4fb6d1e8072ffd7b75c8a27898" - -[[projects]] - digest = "1:aa4d6967a3237f8367b6bf91503964a77183ecf696f1273e8ad3551bb4412b5f" - name = "golang.org/x/text" - packages = [ - "encoding", - "encoding/charmap", - "encoding/htmlindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/gen", - "internal/tag", - "internal/utf8internal", - "language", - "runes", - "transform", - "unicode/cldr", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/DATA-DOG/godog", - "github.com/hashicorp/hcl", - "github.com/onsi/gomega", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index f99b566..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,34 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - name = "github.com/DATA-DOG/godog" - version = "0.7.10" - -[prune] - go-tests = true - unused-packages = true diff --git a/README.md b/README.md index 1a64938..4b19987 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # Clarity [![CircleCI](https://circleci.com/gh/xchapter7x/clarity.svg?style=svg)](https://circleci.com/gh/xchapter7x/clarity) -## A declaritive test framewark for Terraform +## A declaritive test framework for Terraform - reason: B/c unit testing terraform needs to be a thing ## Info: - its a single binary, thats it. no need to write steps or go code. just write a declarative feature file. +- good for writing fitness function tests on your HCL - its gherkin bdd inspired - provides its own matchers and hcl parser - must be run from the directory where your terraform files live @@ -14,9 +15,8 @@ ## Install ```bash -$ export VERSION="v0.5.0" $ export OS="osx" #(osx | unix) -$ curl -sL https://github.com/xchapter7x/clarity/releases/download/${VERSION}/clarity_${OS} -o /usr/local/bin/clarity && chmod +x /usr/local/bin/clarity +$ curl -sL https://github.com/xchapter7x/clarity/releases/latest/download/clarity_${OS} -o /usr/local/bin/clarity && chmod +x /usr/local/bin/clarity ``` ### Download Binaries @@ -155,43 +155,44 @@ Feature: We should have a LB for our control plane and its components and as ``` ### gherkin step matchers available - | '([^"]*)' | noop to insert context into behavior def | - | 'Terraform' | parses the terraform from your local dir | - | 'pending "([^"]*)"$' | mark pending and skip all following steps in the scenario | - | 'a "([^"]*)" of type "([^"]*)"' | matches on types such as resource,data and the resource name | - | 'a "([^"]*)" of type "([^"]*)" named "([^"]*)"' | matches on types, resource names and instance names | - | 'the value of "([^"]*)" always equals (\d+)' | matches on the value given and the value of the attribute | - | 'the value of "([^"]*)" never equals (\d+)' | inverse match on attr value and given value | - | 'the value of "([^"]*)" always equals "([^"]*)"' | matches on the value given and the value of the attribute | - | 'the value of "([^"]*)" never equals "([^"]*)"' | inverse match on attr value and given value | - | 'the value of "([^"]*)" always matches regex "([^"]*)"' | matches the attributes value on the given regex | - | 'the value of "([^"]*)" is always greater than (\d+)' | matches on gt against the given value and attr value | - | 'the value of "([^"]*)" is always less than (\d+)' | matches on lt against the given value and attr value | - | 'attribute "([^"]*)" always equals (\d+)' | matches on the value given and the value of the attribute | - | 'attribute "([^"]*)" never equals (\d+)' | inverse match on attr value and given value | - | 'attribute "([^"]*)" always equals "([^"]*)"' | matches on the value given and the value of the attribute | - | 'attribute "([^"]*)" never equals "([^"]*)"' | inverse match on attr value and given value | - | 'attribute "([^"]*)" always matches regex "([^"]*)"' | matches the attributes value on the given regex | - | 'attribute "([^"]*)" is always greater than (\d+)' | matches on gt against the given value and attr value | - | 'attribute "([^"]*)" is always less than (\d+)' | matches on lt against the given value and attr value | - | 'the value of "([^"]*)" equals (\d+)' | matches on the value given and the value of the attribute | - | 'the value of "([^"]*)" does not equal (\d+)' | inverse match on attr value and given value | - | 'the value of "([^"]*)" equals "([^"]*)"' | matches on the value given and the value of the attribute | - | 'the value of "([^"]*)" does not equal "([^"]*)"' | inverse match on attr value and given value | - | 'the value of "([^"]*)" matches regex "([^"]*)"' | matches the attributes value on the given regex | - | 'the value of "([^"]*)" is greater than (\d+)' | matches on gt against the given value and attr value | - | 'the value of "([^"]*)" is less than (\d+)' | matches on lt against the given value and attr value | - | 'attribute "([^"]*)" equals (\d+)' | matches on the value given and the value of the attribute | - | 'attribute "([^"]*)" does not equal (\d+)' | inverse match on attr value and given value | - | 'attribute "([^"]*)" equals "([^"]*)"' | matches on the value given and the value of the attribute | - | 'attribute "([^"]*)" does not equal "([^"]*)"' | inverse match on attr value and given value | - | 'attribute "([^"]*)" matches regex "([^"]*)"' | matches the attributes value on the given regex | - | 'attribute "([^"]*)" is greater than (\d+)' | matches on gt against the given value and attr value | - | 'attribute "([^"]*)" is less than (\d+)' | matches on lt against the given value and attr value | - | 'attribute "([^"]*)" exists' | if the given attribute exists in the matching objects | - | 'it occurs at least (\d+) times' | if the match set contains at least the given number | - | 'it occurs at most (\d+) times' | if the match set contains at most the given number | - | 'it occurs exactly (\d+) times' | if the match set continas exactly the given number | + | '([^"]*)' | noop to insert context into behavior def | + | 'HCL2' | parses the hcl2 from your local dir | + | 'Terraform' | parses the terraform <=.11 from your local dir | + | 'pending "([^"]*)"$' | mark pending and skip all following steps in the scenario | + | 'a "([^"]*)" of type "([^"]*)"' | matches on types such as resource,data and the resource name | + | 'a "([^"]*)" of type "([^"]*)" named "([^"]*)"' | matches on types, resource names and instance names | + | 'the value of "([^"]*)" always equals (\d+)' | matches on the value given and the value of the attribute | + | 'the value of "([^"]*)" never equals (\d+)' | inverse match on attr value and given value | + | 'the value of "([^"]*)" always equals "([^"]*)"' | matches on the value given and the value of the attribute | + | 'the value of "([^"]*)" never equals "([^"]*)"' | inverse match on attr value and given value | + | 'the value of "([^"]*)" always matches regex "([^"]*)"' | matches the attributes value on the given regex | + | 'the value of "([^"]*)" is always greater than (\d+)' | matches on gt against the given value and attr value | + | 'the value of "([^"]*)" is always less than (\d+)' | matches on lt against the given value and attr value | + | 'attribute "([^"]*)" always equals (\d+)' | matches on the value given and the value of the attribute | + | 'attribute "([^"]*)" never equals (\d+)' | inverse match on attr value and given value | + | 'attribute "([^"]*)" always equals "([^"]*)"' | matches on the value given and the value of the attribute | + | 'attribute "([^"]*)" never equals "([^"]*)"' | inverse match on attr value and given value | + | 'attribute "([^"]*)" always matches regex "([^"]*)"' | matches the attributes value on the given regex | + | 'attribute "([^"]*)" is always greater than (\d+)' | matches on gt against the given value and attr value | + | 'attribute "([^"]*)" is always less than (\d+)' | matches on lt against the given value and attr value | + | 'the value of "([^"]*)" equals (\d+)' | matches on the value given and the value of the attribute | + | 'the value of "([^"]*)" does not equal (\d+)' | inverse match on attr value and given value | + | 'the value of "([^"]*)" equals "([^"]*)"' | matches on the value given and the value of the attribute | + | 'the value of "([^"]*)" does not equal "([^"]*)"' | inverse match on attr value and given value | + | 'the value of "([^"]*)" matches regex "([^"]*)"' | matches the attributes value on the given regex | + | 'the value of "([^"]*)" is greater than (\d+)' | matches on gt against the given value and attr value | + | 'the value of "([^"]*)" is less than (\d+)' | matches on lt against the given value and attr value | + | 'attribute "([^"]*)" equals (\d+)' | matches on the value given and the value of the attribute | + | 'attribute "([^"]*)" does not equal (\d+)' | inverse match on attr value and given value | + | 'attribute "([^"]*)" equals "([^"]*)"' | matches on the value given and the value of the attribute | + | 'attribute "([^"]*)" does not equal "([^"]*)"' | inverse match on attr value and given value | + | 'attribute "([^"]*)" matches regex "([^"]*)"' | matches the attributes value on the given regex | + | 'attribute "([^"]*)" is greater than (\d+)' | matches on gt against the given value and attr value | + | 'attribute "([^"]*)" is less than (\d+)' | matches on lt against the given value and attr value | + | 'attribute "([^"]*)" exists' | if the given attribute exists in the matching objects | + | 'it occurs at least (\d+) times' | if the match set contains at least the given number | + | 'it occurs at most (\d+) times' | if the match set contains at most the given number | + | 'it occurs exactly (\d+) times' | if the match set continas exactly the given number | ## Development & Contributions: - all issues and PRs welcome. @@ -206,3 +207,9 @@ $ make unit ```bash $ make build ``` + + + + +## For more complete testing support using OPA policies check out: +https://github.com/instrumenta/conftest diff --git a/cmd/clarity/clarity_test.go b/cmd/clarity/clarity_test.go index f4f5371..8804717 100644 --- a/cmd/clarity/clarity_test.go +++ b/cmd/clarity/clarity_test.go @@ -1,26 +1,23 @@ package main import ( - "github.com/DATA-DOG/godog" + "github.com/cucumber/godog" + "github.com/cucumber/messages-go/v10" "github.com/xchapter7x/clarity/pkg/matchers" ) -func noopComment(arg1 string) error { - return nil -} - -func markPending(arg1 string) error { - return godog.ErrPending -} - func FeatureContext(s *godog.Suite) { match := matchers.NewMatch() - s.Step(`^"([^"]*)"$`, noopComment) - s.Step(`^pending "([^"]*)"$`, markPending) + s.BeforeScenario(func(*messages.Pickle) { + match = matchers.NewMatch() + }) s.Step(`^Terraform$`, match.Terraform) + s.Step(`^HCL2$`, match.HCL2) s.Step(`^a "([^"]*)" of type "([^"]*)"$`, match.AOfType) s.Step(`^a "([^"]*)" of type "([^"]*)" named "([^"]*)"$`, match.AOfTypeNamed) + s.Step(`^"([^"]*)"$`, noopComment) + s.Step(`^pending "([^"]*)"$`, markPending) for _, phrasePrefix := range []string{ "the value of", "attribute", @@ -32,13 +29,26 @@ func FeatureContext(s *godog.Suite) { s.Step(`^`+phrasePrefix+` "([^"]*)" matches regex "([^"]*)"$`, match.AttributeRegex) s.Step(`^`+phrasePrefix+` "([^"]*)" is greater than (\d+)$`, match.AttributeGreaterThan) s.Step(`^`+phrasePrefix+` "([^"]*)" is less than (\d+)$`, match.AttributeLessThan) + + s.Step(`^`+phrasePrefix+` "([^"]*)" always equals (\d+)$`, match.AlwaysAttributeEqualsInt) + s.Step(`^`+phrasePrefix+` "([^"]*)" never equals (\d+)$`, match.AlwaysAttributeDoesNotEqualInt) + s.Step(`^`+phrasePrefix+` "([^"]*)" always equals "([^"]*)"$`, match.AlwaysAttributeEquals) + s.Step(`^`+phrasePrefix+` "([^"]*)" never equals "([^"]*)"$`, match.AlwaysAttributeDoesNotEqual) + s.Step(`^`+phrasePrefix+` "([^"]*)" always matches regex "([^"]*)"$`, match.AlwaysAttributeRegex) + s.Step(`^`+phrasePrefix+` "([^"]*)" is always greater than (\d+)$`, match.AlwaysAttributeGreaterThan) + s.Step(`^`+phrasePrefix+` "([^"]*)" is always less than (\d+)$`, match.AlwaysAttributeLessThan) } s.Step(`^attribute "([^"]*)" exists$`, match.AttributeExists) s.Step(`^it occurs at least (\d+) times$`, match.ItOccursAtLeastTimes) s.Step(`^it occurs at most (\d+) times$`, match.ItOccursAtMostTimes) s.Step(`^it occurs exactly (\d+) times$`, match.ItOccursExactlyTimes) - s.BeforeScenario(func(interface{}) { - match = matchers.NewMatch() - }) +} + +func noopComment(arg1 string) error { + return nil +} + +func markPending(arg1 string) error { + return godog.ErrPending } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..08b7d9e --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/xchapter7x/clarity + +go 1.13 + +require ( + github.com/cucumber/godog v0.10.0 + github.com/cucumber/messages-go/v10 v10.0.3 + github.com/hashicorp/hcl v1.0.0 + github.com/hashicorp/hcl/v2 v2.4.0 // indirect + github.com/instrumenta/conftest v0.18.1 + github.com/onsi/gomega v1.10.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bf7d49a --- /dev/null +++ b/go.sum @@ -0,0 +1,485 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cuelang.org/go v0.0.15/go.mod h1:gehQASsTv+lFZknWIG0hANGVSBiHD7HyKWmAdEZL3No= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/KeisukeYamashita/go-vcl v0.4.0/go.mod h1:af2qGlXbsHDQN5abN7hyGNKtGhcFSaDdbLl4sfud+AU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cucumber/gherkin-go/v11 v11.0.0 h1:cwVwN1Qn2VRSfHZNLEh5x00tPBmZcjATBWDpxsR5Xug= +github.com/cucumber/gherkin-go/v11 v11.0.0/go.mod h1:CX33k2XU2qog4e+TFjOValoq6mIUq0DmVccZs238R9w= +github.com/cucumber/godog v0.9.0 h1:QOb8wyC7f+FVFXzY3RdgowwJUb4WeJfqbnQqaH4jp+A= +github.com/cucumber/godog v0.9.0/go.mod h1:roWCHkpeK6UTOyIRRl7IR+fgfBeZ4vZR7OSq2J/NbM4= +github.com/cucumber/godog v0.10.0 h1:W01u1+o8bRpgqJRLrclN3iAanU1jAao+TwOMoSV9g1Y= +github.com/cucumber/godog v0.10.0/go.mod h1:0Q+MOUg8Z9AhzLV+nNMbThQ2x1b17yYwGyahApTLjJA= +github.com/cucumber/messages-go/v10 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg= +github.com/cucumber/messages-go/v10 v10.0.3 h1:m/9SD/K/A15WP7i1aemIv7cwvUw+viS51Ui5HBw1cdE= +github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deislabs/oras v0.8.0/go.mod h1:v1Vmv0oHch7HS/gK7CGIdIp7s5lEiLlKvwMxZ34sQDs= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20191008191456-ae2e973db936/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/docker v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-akka/configuration v0.0.0-20190919102339-a31c845c4b1b/go.mod h1:19bUnum2ZAeftfwwLZ/wRe7idyfoW2MfmXO464Hrfbw= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.51.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6/go.mod h1:+tyhT8jBF8E0XvdlSXOSL7Iko7DlNiongHq3q+wcsPs= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20181025225059-d3de96c4c28e/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v0.0.0-20181024020800-521ea7b17d02/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= +github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8= +github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.2.1 h1:wI9btDjYUOJJHTCnRlAG/TkRyD/ij7meJMrLK9X31Cc= +github.com/hashicorp/go-memdb v1.2.1/go.mod h1:OSvLJ662Jim8hMM+gWGyhktyWk2xPCnWMc7DWIqtkGA= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.2.0/go.mod h1:MD4q2LOluJ5pRwTVkCXmJOY7ODWDXVXGVB8LY0t7wig= +github.com/hashicorp/hcl/v2 v2.4.0 h1:xwVa1aj4nCSoAjUnFPBAIfqlzPgSZEVMdkJv/mgj4jY= +github.com/hashicorp/hcl/v2 v2.4.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/instrumenta/conftest v0.18.1 h1:uQ/yiuFNziLRTR3ttzOimR+uX416VVcOYpPFd9SnUKQ= +github.com/instrumenta/conftest v0.18.1/go.mod h1:mwEMPaIgyqKjw4/suqZ21YEXdeCp2Kx51n4wosbM9JE= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae/go.mod h1:Iym28+kJVnC1hfQvv5MUtI6AiFFzvQjHcvI4RFTG/04= +github.com/moby/buildkit v0.3.3/go.mod h1:nnELdKPRkUAQR6pAB3mRU3+IlbqL3SSaAWqQL8k/K+4= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/open-policy-agent/opa v0.18.0/go.mod h1:6pC1cMYDI92i9EY/GoA2m+HcZlcCrh3jbfny5F7JVTA= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.0.0-20181025174421-f30f42803563/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/testscript v1.1.0/go.mod h1:lzMlnW8LS56mcdJoQYkrlzqOoTFCOemzt5LusJ93bDM= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20181024212040-082b515c9490/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zclconf/go-cty v1.1.1/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181023182221-1baf3a9d7d67/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +olympos.io/encoding/edn v0.0.0-20191103180435-78e1aef28b15/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/makefile b/makefile index c474552..5ded3d9 100644 --- a/makefile +++ b/makefile @@ -13,13 +13,13 @@ BINARY_DARWIN=$(BINARY_NAME)_osx all: test build build: build-darwin build-win build-linux -test: gen unit integration +test: gen unit unit: $(GOTEST) ./pkg/... -v integration: - GOCACHE=off $(GOTEST) ./test/integration/... -v + $(GOTEST) ./test/integration/... -v e2e: - GOCACHE=off $(GOTEST) ./test/e2e/... -v + $(GOTEST) ./test/e2e/... -v clean: $(GOCLEAN) find . -name "*.test" | xargs rm diff --git a/pkg/matchers/terraform.go b/pkg/matchers/terraform.go index aa02622..b6c5a68 100644 --- a/pkg/matchers/terraform.go +++ b/pkg/matchers/terraform.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/hashicorp/hcl" + hcl2 "github.com/instrumenta/conftest/parser/hcl2" ) func NewMatch() *Match { @@ -29,11 +30,17 @@ type HCLEntry struct { Attributes interface{} } -func parseHCL(rawHCL map[string]interface{}) []HCLEntry { +func parseHCL2(rawHCL map[string]interface{}) []HCLEntry { hclEntries := make([]HCLEntry, 0) for hclType, componentsArrayMap := range rawHCL { - for _, componentsMap := range componentsArrayMap.([]map[string]interface{}) { - hclEntries = addComponentsToEntries(hclEntries, hclType, componentsMap) + switch components := componentsArrayMap.(type) { + case map[string]interface{}: + hclEntries = addComponentsToEntries(hclEntries, hclType, components) + + case []map[string]interface{}: + for _, componentsMap := range components { + hclEntries = addComponentsToEntries(hclEntries, hclType, componentsMap) + } } } return hclEntries @@ -59,6 +66,15 @@ func addComponentsToEntries(hclEntries []HCLEntry, hclType string, componentsMap Attributes: attributes, }) } + case map[string]interface{}: + for instanceName, instance := range instancesCast { + hclEntries = append(hclEntries, HCLEntry{ + HCLType: hclType, + ComponentName: componentName, + InstanceName: instanceName, + Attributes: instance, + }) + } default: hclEntries = append(hclEntries, HCLEntry{ HCLType: hclType, @@ -78,7 +94,17 @@ func (m *Match) Terraform() error { if err != nil { log.Panic(err) } - return m.ReadTerraform(pwd) + return m.ReadTerraform(pwd, GetUnmarshallerVersion(1)) +} + +// HCL2 a simple matcher to show intent to init from a hcl2 terraform in +// current directory +func (m *Match) HCL2() error { + pwd, err := os.Getwd() + if err != nil { + log.Panic(err) + } + return m.ReadTerraform(pwd, GetUnmarshallerVersion(2)) } // AlwaysAttributeEqualsInt - requires all elements to have an exact match on attributes or it fails @@ -177,9 +203,20 @@ func (m *Match) AlwaysAttributeLessThan(searchKey string, searchValue int) error return nil } +type Unmarshaller func(p []byte, v interface{}) error + +func GetUnmarshallerVersion(version int) Unmarshaller { + if version == 1 { + return hcl.Unmarshal + } + + p := new(hcl2.Parser) + return p.Unmarshal +} + // ReadTerrraform a simple matcher to init from terraform in // a given directory -func (m *Match) ReadTerraform(tpath string) error { +func (m *Match) ReadTerraform(tpath string, unmarshal Unmarshaller) error { baseHCL := make(map[string]interface{}, 0) dirContents := "" files, err := ioutil.ReadDir(tpath) @@ -195,15 +232,16 @@ func (m *Match) ReadTerraform(tpath string) error { } dirContents += string(contents) + dirContents += "\n" } } - err = hcl.Unmarshal([]byte(dirContents), &baseHCL) + err = unmarshal([]byte(dirContents), &baseHCL) if err != nil { return fmt.Errorf("hcl Unmarshal failed: %v", err) } - m.HCLEntries = parseHCL(baseHCL) + m.HCLEntries = parseHCL2(baseHCL) return nil } @@ -245,7 +283,6 @@ func (m *Match) AOfType(providerFeature, providerFeatureType string) error { entries, _ := json.Marshal(m.HCLEntries) return fmt.Errorf("no matches found for attribute %v \n %v", providerFeature, string(entries)) } - m.MatchingEntries = append(m.MatchingEntries, matchingFeature...) return nil } @@ -296,6 +333,12 @@ func attributeExists(searchName string, attributes interface{}) (bool, interface } } } + case map[string]interface{}: + for k, v := range attributesCast { + if k == searchName { + return true, v + } + } } return false, nil } @@ -310,7 +353,15 @@ func (m *Match) AttributeEqualsInt(searchKey string, searchValue int) error { for _, entry := range m.MatchingEntries { exists, attributeValue := attributeExists(searchKey, entry.Attributes) - if exists && attributeValue == searchValue { + var attributeInteger int + switch v := attributeValue.(type) { + case int: + attributeInteger = v + case float64: + attributeInteger = int(v) + } + + if exists && attributeInteger == searchValue { tmpEntries = append(tmpEntries, entry) } } @@ -333,7 +384,15 @@ func (m *Match) AttributeDoesNotEqualInt(searchKey string, searchValue int) erro for _, entry := range m.MatchingEntries { exists, attributeValue := attributeExists(searchKey, entry.Attributes) - if exists && attributeValue != searchValue { + var attributeInteger int + switch v := attributeValue.(type) { + case int: + attributeInteger = v + case float64: + attributeInteger = int(v) + } + + if exists && attributeInteger != searchValue { tmpEntries = append(tmpEntries, entry) } } @@ -414,6 +473,10 @@ func (m *Match) AttributeGreaterThan(searchKey string, searchValue int) error { } case int: actualValue = attributeValue + case float64: + actualValue = int(attributeValue) + default: + return fmt.Errorf("could not translate to int") } if exists && actualValue > searchValue { diff --git a/pkg/matchers/terraform_test.go b/pkg/matchers/terraform_test.go index 4389dc4..469fb4d 100644 --- a/pkg/matchers/terraform_test.go +++ b/pkg/matchers/terraform_test.go @@ -1,6 +1,7 @@ package matchers_test import ( + "sort" "testing" . "github.com/onsi/gomega" @@ -9,360 +10,444 @@ import ( func TestMatchers(t *testing.T) { RegisterTestingT(t) - t.Run("m.ReadTerraform", func(t *testing.T) { - m := &matchers.Match{} - controlHCL := multiTFControl() - t.Run("should read all the terraform files into the matcher", func(t *testing.T) { - err := m.ReadTerraform("testdata/multitf") - Expect(err).NotTo(HaveOccurred()) - Expect(m.HCLEntries).To(Equal(controlHCL)) + for _, tt := range []struct { + unmarshal matchers.Unmarshaller + name string + skip bool + }{ + {name: "hcl1", unmarshal: matchers.GetUnmarshallerVersion(1)}, + {name: "hcl2", unmarshal: matchers.GetUnmarshallerVersion(2)}, + } { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.Skip("skipping", tt.name) + } + unmarshal := tt.unmarshal + t.Run("m.ReadTerraform", func(t *testing.T) { + m := &matchers.Match{} + controlHCL := multiTFControl() + t.Run("should read all the terraform files into the matcher", func(t *testing.T) { + for _, dirname := range []string{"testdata/multitf", "testdata/multitf_newline"} { + unmarshal := tt.unmarshal + err := m.ReadTerraform(dirname, unmarshal) + Expect(err).NotTo(HaveOccurred()) + sort.SliceStable(controlHCL, func(i, j int) bool { return controlHCL[i].InstanceName < controlHCL[j].InstanceName }) + sort.SliceStable(m.HCLEntries, func(i, j int) bool { return m.HCLEntries[i].InstanceName < m.HCLEntries[j].InstanceName }) + Expect(len(m.HCLEntries)).To(Equal(len(controlHCL))) + attributesMatch(m.HCLEntries, controlHCL) + Expect(m.HCLEntries[0].HCLType).To(BeEquivalentTo(controlHCL[0].HCLType)) + Expect(m.HCLEntries[0].ComponentName).To(BeEquivalentTo(controlHCL[0].ComponentName)) + Expect(m.HCLEntries[0].InstanceName).To(BeEquivalentTo(controlHCL[0].InstanceName)) + } + }) + }) + + t.Run("m.AOfTypeNamed", func(t *testing.T) { + t.Run("should return a filtered list of definitions", func(t *testing.T) { + m := &matchers.Match{} + m.ReadTerraform("testdata", unmarshal) + err := m.AOfTypeNamed("google_compute_network", "resource", "my-custom-network") + Expect(err).NotTo(HaveOccurred()) + control := controlInstanceMatch() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AlwaysAttributeEqualsInt", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeEqualsInt("name", 5)).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that dont match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} + Expect(m.AlwaysAttributeEqualsInt("name", 5)).To(HaveOccurred()) + }) + + t.Run("should succeed all elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} + Expect(m.AlwaysAttributeEqualsInt("name", 5)).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) + }) + }) + + t.Run("m.AlwaysAttributeEquals", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that dont match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} + Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("should succeed all elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} + Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatch())) + }) + }) + + t.Run("m.AlwaysAttributeDoesNotEqual", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeDoesNotEqual("name", "def-doesnt-exist")).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} + Expect(m.AlwaysAttributeDoesNotEqual("name", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("should succeed NO elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} + Expect(m.AlwaysAttributeDoesNotEqual("name", "def-doesnt-exist")).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMiss())) + }) + }) + + t.Run("m.AlwaysAttributeDoesNotEqualInt", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeDoesNotEqualInt("name", 5)).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} + Expect(m.AlwaysAttributeDoesNotEqualInt("name", 5)).To(HaveOccurred()) + }) + + t.Run("should succeed NO elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} + Expect(m.AlwaysAttributeDoesNotEqualInt("name", 24)).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMissInt())) + }) + }) + + t.Run("m.AlwaysAttributeRegex", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeRegex("name", "off")).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that dont match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} + Expect(m.AlwaysAttributeRegex("name", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("should succeed when all elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} + Expect(m.AlwaysAttributeRegex("name", "my-custom-network")).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatch())) + }) + }) + + t.Run("m.AlwaysAttributeGreaterThan", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeGreaterThan("name", 5)).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that dont match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} + Expect(m.AlwaysAttributeGreaterThan("name", 4)).To(HaveOccurred()) + }) + + t.Run("should succeed when all elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} + Expect(m.AlwaysAttributeGreaterThan("name", 2)).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) + }) + }) + + t.Run("m.AlwaysAttributeLessThan", func(t *testing.T) { + t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlMissingAttribute()} + Expect(m.AlwaysAttributeLessThan("name", 5)).To(HaveOccurred()) + }) + + t.Run("should fail if there are any elements that dont match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} + Expect(m.AlwaysAttributeLessThan("name", 4)).To(HaveOccurred()) + }) + + t.Run("should succeed when all elements match", func(t *testing.T) { + m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} + Expect(m.AlwaysAttributeLessThan("name", 6)).NotTo(HaveOccurred()) + Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) + }) + }) + + t.Run("m.AOfType", func(t *testing.T) { + t.Run("should return a filtered list of definitions", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + control := controlInstanceMatch() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeExists", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("google_compute_subnetwork", unmarshal) + Expect(m.AttributeExists("bbbbbasdf")).To(HaveOccurred()) + }) + + t.Run("when there are matches", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeExists("name")).NotTo(HaveOccurred()) + control := controlInstanceMatch() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + + t.Run("when attributes is not derived from a output block", func(t *testing.T) { + m := &matchers.Match{} + control := controlOutputMatch() + err := m.ReadTerraform("testdata/outputs", unmarshal) + Expect(err).NotTo(HaveOccurred()) + err = m.AOfType("rds_password", "output") + Expect(err).NotTo(HaveOccurred()) + Expect(m.AttributeExists("sensitive")).NotTo(HaveOccurred()) + Expect(m.AttributeExists("value")).NotTo(HaveOccurred()) + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeGreaterThan", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeGreaterThan("nadafield", 81)).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non gt value", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeGreaterThan("port", 81)).To(HaveOccurred()) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeGreaterThan("port", 79)).NotTo(HaveOccurred()) + control := controlInstanceMatchIntString() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + + t.Run("when there are matches with a correct value and the attr value is a int type", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + Expect(m.AttributeGreaterThan("port", 79)).NotTo(HaveOccurred()) + control := controlInstanceMatchInt() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.ItOccursAtLeastTimes", func(t *testing.T) { + t.Run("when we dont have enough to satisfy at least", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursAtLeastTimes(5)).To(HaveOccurred()) + }) + + t.Run("when we have enough to satisfy at least", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursAtLeastTimes(1)).NotTo(HaveOccurred()) + }) + }) + + t.Run("m.ItOccursAtMostTimes", func(t *testing.T) { + t.Run("when we have too many to satisfy at most", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursAtMostTimes(0)).To(HaveOccurred()) + }) + + t.Run("when we have enough to satisfy at most", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursAtMostTimes(3)).NotTo(HaveOccurred()) + }) + }) + + t.Run("m.ItOccursExactlyTimes", func(t *testing.T) { + t.Run("when we dont have exactly the count", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursExactlyTimes(2)).To(HaveOccurred()) + }) + + t.Run("when we have exactly the count", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.ItOccursExactlyTimes(1)).NotTo(HaveOccurred()) + }) + }) + + t.Run("m.AttributeRegex", func(t *testing.T) { + t.Run("when we do not have a attr value which satisfies the regex", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeRegex("port", "abc")).To(HaveOccurred()) + }) + + t.Run("when our attribute value satisfies the regex", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeRegex("port", "8*")).NotTo(HaveOccurred()) + }) + + t.Run("when our attribute is a complex object and it satisfies the regex", func(t *testing.T) { + m := &matchers.Match{} + err := m.ReadTerraform("testdata", unmarshal) + Expect(err).NotTo(HaveOccurred()) + err = m.AOfTypeNamed("google_compute_firewall", "resource", "allow-all-internal") + Expect(err).NotTo(HaveOccurred()) + Expect(m.AttributeExists("allow")).NotTo(HaveOccurred()) + Expect(m.AttributeRegex("allow", "protocol.*tcp")).NotTo(HaveOccurred()) + }) + }) + + t.Run("m.AttributeLessThan", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeLessThan("nadafield", 79)).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non gt value", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeLessThan("port", 79)).To(HaveOccurred()) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("blah_blah", unmarshal) + Expect(m.AttributeLessThan("port", 81)).NotTo(HaveOccurred()) + control := controlInstanceMatchIntString() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + + t.Run("when there are matches with a correct value and attr value is type int", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + control := controlInstanceMatchInt() + Expect(m.AttributeLessThan("port", 81)).NotTo(HaveOccurred()) + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeEqualsInt", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + Expect(m.AttributeEqualsInt("bbbbbasdf", 0)).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non equal value", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + Expect(m.AttributeEqualsInt("port", 43)).To(HaveOccurred()) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + control := controlInstanceMatchInt() + Expect(m.AttributeEqualsInt("port", 80)).NotTo(HaveOccurred()) + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeDoesNotEqualInt", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + Expect(m.AttributeDoesNotEqualInt("bbbbbasdf", 0)).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non equal value", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + Expect(m.AttributeDoesNotEqualInt("port", 80)).To(HaveOccurred()) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("foo", unmarshal) + control := controlInstanceMatchInt() + Expect(m.AttributeDoesNotEqualInt("port", 81)).NotTo(HaveOccurred()) + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeEquals", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeEquals("bbbbbasdf", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non equal value", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeEquals("name", "'wrong-name'")).To(HaveOccurred()) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeEquals("name", "my-custom-network")).NotTo(HaveOccurred()) + control := controlInstanceMatch() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + }) + + t.Run("m.AttributeDoesNotEqual", func(t *testing.T) { + t.Run("when there are no matches", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeDoesNotEqual("bbbbbasdf", "my-custom-network")).To(HaveOccurred()) + }) + + t.Run("when a valid attribute has non equal value", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeDoesNotEqual("name", "wrong-name")).NotTo(HaveOccurred()) + control := controlInstanceMatch() + Expect(len(m.MatchingEntries)).To(Equal(len(control))) + attributesMatch(m.MatchingEntries, control) + Expect(m.MatchingEntries[0].HCLType).To(BeEquivalentTo(control[0].HCLType)) + Expect(m.MatchingEntries[0].ComponentName).To(BeEquivalentTo(control[0].ComponentName)) + Expect(m.MatchingEntries[0].InstanceName).To(BeEquivalentTo(control[0].InstanceName)) + }) + + t.Run("when there are matches with a correct value", func(t *testing.T) { + m := resourceMatcherByType("google_compute_network", unmarshal) + Expect(m.AttributeDoesNotEqual("name", "my-custom-network")).To(HaveOccurred()) + }) + }) }) - }) - - t.Run("m.AOfTypeNamed", func(t *testing.T) { - t.Run("should return a filtered list of definitions", func(t *testing.T) { - m := &matchers.Match{} - m.ReadTerraform("testdata") - err := m.AOfTypeNamed("google_compute_network", "resource", "my-custom-network") - Expect(err).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatch())) - }) - }) - - t.Run("m.AlwaysAttributeEqualsInt", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeEqualsInt("name", 5)).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that dont match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} - Expect(m.AlwaysAttributeEqualsInt("name", 5)).To(HaveOccurred()) - }) - - t.Run("should succeed all elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} - Expect(m.AlwaysAttributeEqualsInt("name", 5)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) - }) - }) - - t.Run("m.AlwaysAttributeEquals", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that dont match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} - Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("should succeed all elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} - Expect(m.AlwaysAttributeEquals("name", "my-custom-network")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatch())) - }) - }) - - t.Run("m.AlwaysAttributeDoesNotEqual", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeDoesNotEqual("name", "def-doesnt-exist")).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} - Expect(m.AlwaysAttributeDoesNotEqual("name", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("should succeed NO elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} - Expect(m.AlwaysAttributeDoesNotEqual("name", "def-doesnt-exist")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMiss())) - }) - }) - - t.Run("m.AlwaysAttributeDoesNotEqualInt", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeDoesNotEqualInt("name", 5)).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} - Expect(m.AlwaysAttributeDoesNotEqualInt("name", 5)).To(HaveOccurred()) - }) - - t.Run("should succeed NO elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} - Expect(m.AlwaysAttributeDoesNotEqualInt("name", 24)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMissInt())) - }) - }) - - t.Run("m.AlwaysAttributeRegex", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeRegex("name", "off")).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that dont match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMiss()} - Expect(m.AlwaysAttributeRegex("name", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("should succeed when all elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatch()} - Expect(m.AlwaysAttributeRegex("name", "my-custom-network")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatch())) - }) - }) - - t.Run("m.AlwaysAttributeGreaterThan", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeGreaterThan("name", 5)).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that dont match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} - Expect(m.AlwaysAttributeGreaterThan("name", 4)).To(HaveOccurred()) - }) - - t.Run("should succeed when all elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} - Expect(m.AlwaysAttributeGreaterThan("name", 2)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) - }) - }) - - t.Run("m.AlwaysAttributeLessThan", func(t *testing.T) { - t.Run("should fail but not panic if the attribute doesnt exist", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlMissingAttribute()} - Expect(m.AlwaysAttributeLessThan("name", 5)).To(HaveOccurred()) - }) - - t.Run("should fail if there are any elements that dont match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMissInt()} - Expect(m.AlwaysAttributeLessThan("name", 4)).To(HaveOccurred()) - }) - - t.Run("should succeed when all elements match", func(t *testing.T) { - m := &matchers.Match{MatchingEntries: controlAlwaysMatchInt()} - Expect(m.AlwaysAttributeLessThan("name", 6)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlAlwaysMatchInt())) - }) - }) - - t.Run("m.AOfType", func(t *testing.T) { - t.Run("should return a filtered list of definitions", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatch())) - }) - }) - - t.Run("m.AttributeExists", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("google_compute_subnetwork") - Expect(m.AttributeExists("bbbbbasdf")).To(HaveOccurred()) - }) - - t.Run("when there are matches", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeExists("name")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatch())) - }) - - t.Run("when attributes is not derived from a output block", func(t *testing.T) { - m := &matchers.Match{} - err := m.ReadTerraform("testdata/outputs") - Expect(err).NotTo(HaveOccurred()) - err = m.AOfType("rds_password", "output") - Expect(err).NotTo(HaveOccurred()) - Expect(m.AttributeExists("sensitive")).NotTo(HaveOccurred()) - Expect(m.AttributeExists("value")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlOutputMatch())) - }) - }) - - t.Run("m.AttributeGreaterThan", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeGreaterThan("nadafield", 81)).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non gt value", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeGreaterThan("port", 81)).To(HaveOccurred()) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeGreaterThan("port", 79)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchIntString())) - }) - - t.Run("when there are matches with a correct value and the attr value is a int type", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeGreaterThan("port", 79)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchInt())) - }) - }) - - t.Run("m.ItOccursAtLeastTimes", func(t *testing.T) { - t.Run("when we dont have enough to satisfy at least", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursAtLeastTimes(5)).To(HaveOccurred()) - }) - - t.Run("when we have enough to satisfy at least", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursAtLeastTimes(1)).NotTo(HaveOccurred()) - }) - }) - - t.Run("m.ItOccursAtMostTimes", func(t *testing.T) { - t.Run("when we have too many to satisfy at most", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursAtMostTimes(0)).To(HaveOccurred()) - }) - - t.Run("when we have enough to satisfy at most", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursAtMostTimes(3)).NotTo(HaveOccurred()) - }) - }) - - t.Run("m.ItOccursExactlyTimes", func(t *testing.T) { - t.Run("when we dont have exactly the count", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursExactlyTimes(2)).To(HaveOccurred()) - }) - - t.Run("when we have exactly the count", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.ItOccursExactlyTimes(1)).NotTo(HaveOccurred()) - }) - }) - - t.Run("m.AttributeRegex", func(t *testing.T) { - t.Run("when we do not have a attr value which satisfies the regex", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeRegex("port", "abc")).To(HaveOccurred()) - }) - - t.Run("when our attribute value satisfies the regex", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeRegex("port", "8*")).NotTo(HaveOccurred()) - }) - - t.Run("when our attribute is a complex object and it satisfies the regex", func(t *testing.T) { - m := &matchers.Match{} - err := m.ReadTerraform("testdata") - Expect(err).NotTo(HaveOccurred()) - err = m.AOfTypeNamed("google_compute_firewall", "resource", "allow-all-internal") - Expect(err).NotTo(HaveOccurred()) - Expect(m.AttributeExists("allow")).NotTo(HaveOccurred()) - Expect(m.AttributeRegex("allow", "protocol.*tcp")).NotTo(HaveOccurred()) - }) - }) - - t.Run("m.AttributeLessThan", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeLessThan("nadafield", 79)).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non gt value", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeLessThan("port", 79)).To(HaveOccurred()) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("blah_blah") - Expect(m.AttributeLessThan("port", 81)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchIntString())) - }) - - t.Run("when there are matches with a correct value and attr value is type int", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeLessThan("port", 81)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchInt())) - }) - }) - - t.Run("m.AttributeEqualsInt", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeEqualsInt("bbbbbasdf", 0)).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non equal value", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeEqualsInt("port", 43)).To(HaveOccurred()) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeEqualsInt("port", 80)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchInt())) - }) - }) - - t.Run("m.AttributeDoesNotEqualInt", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeDoesNotEqualInt("bbbbbasdf", 0)).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non equal value", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeDoesNotEqualInt("port", 80)).To(HaveOccurred()) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("foo") - Expect(m.AttributeDoesNotEqualInt("port", 81)).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatchInt())) - }) - }) - - t.Run("m.AttributeEquals", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeEquals("bbbbbasdf", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non equal value", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeEquals("name", "'wrong-name'")).To(HaveOccurred()) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeEquals("name", "my-custom-network")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatch())) - }) - }) - - t.Run("m.AttributeDoesNotEqual", func(t *testing.T) { - t.Run("when there are no matches", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeDoesNotEqual("bbbbbasdf", "my-custom-network")).To(HaveOccurred()) - }) - - t.Run("when a valid attribute has non equal value", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeDoesNotEqual("name", "wrong-name")).NotTo(HaveOccurred()) - Expect(m.MatchingEntries).To(BeEquivalentTo(controlInstanceMatch())) - }) - - t.Run("when there are matches with a correct value", func(t *testing.T) { - m := resourceMatcherByType("google_compute_network") - Expect(m.AttributeDoesNotEqual("name", "my-custom-network")).To(HaveOccurred()) - }) - }) + } } func controlOutputMatch() []matchers.HCLEntry { @@ -560,9 +645,27 @@ func multiTFControl() []matchers.HCLEntry { } } -func resourceMatcherByType(resourceType string) *matchers.Match { +func attributesMatch(object1, object2 interface{}) { + var attr1, attr2 []map[string]interface{} + switch attr := object1.(type) { + case map[string]interface{}: + attr1 = []map[string]interface{}{attr} + case []map[string]interface{}: + attr1 = attr + } + + switch attr := object2.(type) { + case map[string]interface{}: + attr2 = []map[string]interface{}{attr} + case []map[string]interface{}: + attr2 = attr + } + Expect(attr1).To(BeEquivalentTo(attr2)) +} + +func resourceMatcherByType(resourceType string, unmarshal matchers.Unmarshaller) *matchers.Match { m := &matchers.Match{} - err := m.ReadTerraform("testdata") + err := m.ReadTerraform("testdata", unmarshal) Expect(err).NotTo(HaveOccurred()) err = m.AOfType(resourceType, "resource") Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/matchers/testdata/multitf_newline/1.tf b/pkg/matchers/testdata/multitf_newline/1.tf new file mode 100644 index 0000000..770ff73 --- /dev/null +++ b/pkg/matchers/testdata/multitf_newline/1.tf @@ -0,0 +1,3 @@ +resource "aws" "blah" { + +} \ No newline at end of file diff --git a/pkg/matchers/testdata/multitf_newline/2.tf b/pkg/matchers/testdata/multitf_newline/2.tf new file mode 100644 index 0000000..f446d7c --- /dev/null +++ b/pkg/matchers/testdata/multitf_newline/2.tf @@ -0,0 +1,3 @@ +resource "aws" "bar" { + +}