diff --git a/.goreleaser.yml b/.goreleaser.yml index 14a44b38..c18794c6 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,7 +24,7 @@ builds: - darwin - windows ldflags: - - -s -w -a -extldflags "-static" -X main.Version={{.Tag}} -X main.Git={{.Commit}} + - -s -w -a -extldflags "-static" -X main.Version={{.Tag}} -X main.Git={{.Commit}} -trimpath archives: - id: apigeecli format: zip diff --git a/cmd/apis/apiproxydef/apiproxydef.go b/cmd/apis/apiproxydef/apiproxydef.go new file mode 100644 index 00000000..1cc43ab3 --- /dev/null +++ b/cmd/apis/apiproxydef/apiproxydef.go @@ -0,0 +1,110 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiproxydef + +import ( + "encoding/xml" + "strconv" + "time" +) + +type policiesType struct { + XMLName xml.Name `xml:"Policies"` + Policy []string `xml:"Policy,omitempty"` +} + +type proxyEndpointsType struct { + XMLName xml.Name `xml:"ProxyEndpoints"` + ProxyEndpoint []string `xml:"ProxyEndpoint,omitempty"` +} + +type targetEndpointsType struct { + XMLName xml.Name `xml:"TargetEndpoints"` + TargetEndpoint []string `xml:"TargetEndpoint,omitempty"` +} + +type configurationVersionType struct { + XMLName xml.Name `xml:"ConfigurationVersion,omitempty"` + MajorVersion string `xml:"majorVersion,attr"` + MinorVersion string `xml:"minorVersion,attr"` +} + +type apiProxyType struct { + XMLName xml.Name `xml:"APIProxy"` + Name string `xml:"name,attr"` + Revision string `xml:"revision,attr"` + BasePaths string `xml:"Basepaths,omitempty"` + ConfigurationVersion configurationVersionType `xml:"ConfigurationVersion,omitempty"` + CreatedAt string `xml:"CreatedAt,omitempty"` + Description string `xml:"Description,omitempty"` + DisplayName string `xml:"DisplayName,omitempty"` + LastModifiedAt string `xml:"LastModifiedAt,omitempty"` + Policies policiesType `xml:"Policies,omitempty"` + ProxyEndpoints proxyEndpointsType `xml:"ProxyEndpoints,omitempty"` + Resources string `xml:"Resources,omitempty"` + Spec string `xml:"Spec,omitempty"` + TargetServers string `xml:"TargetServers,omitempty"` + TargetEndpoints targetEndpointsType `xml:"TargetEndpoints,omitempty"` + Validate string `xml:"validate,omitempty"` +} + +var apiProxy apiProxyType + +func SetDisplayName(name string) { + apiProxy.DisplayName = name + apiProxy.Name = name +} + +func AddProxyEndpoint(name string) { + apiProxy.ProxyEndpoints.ProxyEndpoint = append(apiProxy.ProxyEndpoints.ProxyEndpoint, name) +} + +func AddTargetEndpoint(name string) { + apiProxy.TargetEndpoints.TargetEndpoint = append(apiProxy.TargetEndpoints.TargetEndpoint, name) +} + +func SetCreatedAt() { + apiProxy.CreatedAt = strconv.FormatInt((time.Now().UTC().UnixNano())/1000000, 10) +} + +func SetLastModifiedAt() { + apiProxy.LastModifiedAt = strconv.FormatInt((time.Now().UTC().UnixNano())/1000000, 10) +} + +func AddPolicy(name string) { + apiProxy.Policies.Policy = append(apiProxy.Policies.Policy, name) +} + +func SetBasePath(basePath string) { + apiProxy.BasePaths = basePath +} + +func SetRevision(revision string) { + apiProxy.Revision = revision +} + +func SetDescription(description string) { + apiProxy.Description = description +} + +func GetAPIProxy() string { + proxyBody, _ := xml.MarshalIndent(apiProxy, "", " ") + return string(proxyBody) +} + +func SetConfigurationVersion() { + apiProxy.ConfigurationVersion.MajorVersion = "4" + apiProxy.ConfigurationVersion.MinorVersion = "0" +} diff --git a/cmd/apis/crtapi.go b/cmd/apis/crtapi.go index f382159e..5613d49d 100644 --- a/cmd/apis/crtapi.go +++ b/cmd/apis/crtapi.go @@ -18,6 +18,7 @@ import ( "github.com/spf13/cobra" "github.com/srinandan/apigeecli/apiclient" "github.com/srinandan/apigeecli/client/apis" + proxybundle "github.com/srinandan/apigeecli/cmd/apis/proxybundle" ) //CreateCmd to create api @@ -30,12 +31,33 @@ var CreateCmd = &cobra.Command{ return nil }, RunE: func(cmd *cobra.Command, args []string) (err error) { - _, err = apis.CreateProxy(name, proxy) + if proxy != "" { + _, err = apis.CreateProxy(name, proxy) + } else if oasDoc != "" { + err = GenerateAPIProxyDefFromOAS(name, oasDoc) + if err != nil { + return err + } + + err = proxybundle.GenerateAPIProxyBundle(name) + if err != nil { + return err + } + + if importProxy { + _, err = apis.CreateProxy(name, name+".zip") + } + + } else { + _, err = apis.CreateProxy(name, "") + } + return }, } -var proxy string +var proxy, oasDoc string +var importProxy bool func init() { @@ -43,6 +65,10 @@ func init() { "", "API Proxy name") CreateCmd.Flags().StringVarP(&proxy, "proxy", "p", "", "API Proxy Bundle path") + CreateCmd.Flags().StringVarP(&oasDoc, "oas", "f", + "", "Open API 3.0 Specification file") + CreateCmd.Flags().BoolVarP(&importProxy, "import", "", + true, "Import API Proxy after generation from spec") _ = CreateCmd.MarkFlagRequired("name") } diff --git a/cmd/apis/generateapi.go b/cmd/apis/generateapi.go new file mode 100644 index 00000000..1c51c809 --- /dev/null +++ b/cmd/apis/generateapi.go @@ -0,0 +1,165 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apis + +import ( + "fmt" + "net/url" + "regexp" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + apiproxy "github.com/srinandan/apigeecli/cmd/apis/apiproxydef" + proxies "github.com/srinandan/apigeecli/cmd/apis/proxies" + target "github.com/srinandan/apigeecli/cmd/apis/targetendpoint" +) + +func LoadDocument(filePath string) (doc *openapi3.Swagger, err error) { + doc, err = openapi3.NewSwaggerLoader().LoadSwaggerFromFile(filePath) + return doc, err +} + +func GenerateAPIProxyDefFromOAS(name string, filePath string) (err error) { + doc, err := LoadDocument(filePath) + if err != nil { + return err + } + + apiproxy.SetDisplayName(name) + if doc.Info != nil { + if doc.Info.Description != "" { + apiproxy.SetDescription(doc.Info.Description) + } + } + + apiproxy.SetCreatedAt() + apiproxy.SetLastModifiedAt() + apiproxy.SetConfigurationVersion() + apiproxy.AddTargetEndpoint("default") + apiproxy.AddProxyEndpoint("default") + + u, err := GetEndpoint(doc) + if err != nil { + return err + } + + apiproxy.SetBasePath(u.Path) + + target.NewTargetEndpoint(u.Scheme + "://" + u.Hostname()) + + proxies.NewProxyEndpoint(u.Path) + + GenerateFlows(doc.Paths) + + return nil +} + +func GetEndpoint(doc *openapi3.Swagger) (u *url.URL, err error) { + if doc.Servers == nil { + return nil, fmt.Errorf("at least one server must be present") + } + + return url.Parse(doc.Servers[0].URL) +} + +func GetHTTPMethod(pathItem *openapi3.PathItem, keyPath string) map[string]string { + + pathMap := make(map[string]string) + alternateOperationId := strings.ReplaceAll(keyPath, "\\", "_") + + if pathItem.Get != nil { + if pathItem.Get.OperationID != "" { + pathMap["get"] = pathItem.Get.OperationID + } else { + pathMap["get"] = "get_" + alternateOperationId + } + } + + if pathItem.Post != nil { + if pathItem.Post.OperationID != "" { + pathMap["post"] = pathItem.Post.OperationID + } else { + pathMap["post"] = "post_" + alternateOperationId + } + } + + if pathItem.Put != nil { + if pathItem.Put.OperationID != "" { + pathMap["put"] = pathItem.Put.OperationID + } else { + pathMap["put"] = "put_" + alternateOperationId + } + } + + if pathItem.Patch != nil { + if pathItem.Patch.OperationID != "" { + pathMap["patch"] = pathItem.Patch.OperationID + } else { + pathMap["patch"] = "patch_" + alternateOperationId + } + } + + if pathItem.Delete != nil { + if pathItem.Delete.OperationID != "" { + pathMap["delete"] = pathItem.Delete.OperationID + } else { + pathMap["delete"] = "delete_" + alternateOperationId + } + } + + if pathItem.Options != nil { + if pathItem.Options.OperationID != "" { + pathMap["options"] = pathItem.Options.OperationID + } else { + pathMap["options"] = "options_" + alternateOperationId + } + } + + if pathItem.Trace != nil { + if pathItem.Trace.OperationID != "" { + pathMap["trace"] = pathItem.Trace.OperationID + } else { + pathMap["trace"] = "trace_" + alternateOperationId + } + } + + if pathItem.Head != nil { + if pathItem.Head.OperationID != "" { + pathMap["head"] = pathItem.Head.OperationID + } else { + pathMap["head"] = "head_" + alternateOperationId + } + } + + return pathMap +} + +func GenerateFlows(paths openapi3.Paths) { + for keyPath := range paths { + pathMap := GetHTTPMethod(paths[keyPath], keyPath) + for method, operationId := range pathMap { + proxies.AddFlow(operationId, replacePathWithWildCard(keyPath), method) + } + } +} + +func replacePathWithWildCard(keyPath string) string { + re := regexp.MustCompile(`{(.*?)}`) + if strings.ContainsAny(keyPath, "{") { + return re.ReplaceAllLiteralString(keyPath, "*") + } else { + return keyPath + } +} diff --git a/cmd/apis/proxies/proxies.go b/cmd/apis/proxies/proxies.go new file mode 100644 index 00000000..50494ddb --- /dev/null +++ b/cmd/apis/proxies/proxies.go @@ -0,0 +1,110 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxies + +import ( + "encoding/xml" +) + +type proxyEndpointDef struct { + XMLName xml.Name `xml:"ProxyEndpoint"` + Name string `xml:"name,attr"` + Description string `xml:"Description,omitempty"` + FaultRules string `xml:"FaultRules,omitempty"` + PreFlow preFlowDef `xml:"PreFlow,omitempty"` + PostFlow postFlowDef `xml:"PostFlow,omitempty"` + Flows flowsDef `xml:"Flows,omitempty"` + HTTPProxyConnection httpProxyConnectionDef `xml:"HTTPProxyConnection,omitempty"` + RouteRule routeRuleDef `xml:"RouteRule,omitempty"` +} + +type preFlowDef struct { + XMLName xml.Name `xml:"PreFlow"` + Name string `xml:"name,attr"` + Request requestFlowDef `xml:"Request"` + Response responseFlowDef `xml:"Response"` +} + +type postFlowDef struct { + XMLName xml.Name `xml:"PostFlow"` + Name string `xml:"name,attr"` + Request requestFlowDef `xml:"Request"` + Response responseFlowDef `xml:"Response"` +} + +type requestFlowDef struct { + Step stepDef `xml:"Step"` +} + +type responseFlowDef struct { + Step stepDef `xml:"Step"` +} + +type stepDef struct { + Name string `xml:"name"` +} + +type routeRuleDef struct { + XMLName xml.Name `xml:"RouteRule"` + Name string `xml:"name,attr"` + TargetEndpoint string `xml:"TargetEndpoint"` +} + +type flowsDef struct { + XMLName xml.Name `xml:"Flows"` + Flow []flowDef `xml:"Flow"` +} + +type flowDef struct { + XMLName xml.Name `xml:"Flow"` + Name string `xml:"name,attr"` + Request requestFlowDef `xml:"Request"` + Response responseFlowDef `xml:"Response"` + Condition conditionDef `xml:"Condition"` +} + +type conditionDef struct { + ConditionData string `xml:",innerxml"` +} + +type httpProxyConnectionDef struct { + XMLName xml.Name `xml:"HTTPProxyConnection"` + BasePath string `xml:"BasePath"` + Properties string `xml:"Properties"` + VirtualHost []string `xml:"VirtualHost"` +} + +var proxyEndpoint proxyEndpointDef + +func GetProxyEndpoint() string { + proxyBody, _ := xml.MarshalIndent(proxyEndpoint, "", " ") + return string(proxyBody) +} + +func NewProxyEndpoint(basePath string) { + proxyEndpoint.Name = "default" + proxyEndpoint.PreFlow.Name = "PreFlow" + proxyEndpoint.PostFlow.Name = "PostFlow" + proxyEndpoint.HTTPProxyConnection.BasePath = basePath + proxyEndpoint.RouteRule.Name = "default" + proxyEndpoint.RouteRule.TargetEndpoint = "default" +} + +func AddFlow(operationId string, keyPath string, method string) { + flow := flowDef{} + flow.Name = operationId + flow.Condition.ConditionData = "(proxy.pathsuffix MatchesPath \"" + keyPath + "\") and (request.verb = \"" + method + "\")" + proxyEndpoint.Flows.Flow = append(proxyEndpoint.Flows.Flow, flow) +} diff --git a/cmd/apis/proxybundle/proxybundle.go b/cmd/apis/proxybundle/proxybundle.go new file mode 100644 index 00000000..9a49dfad --- /dev/null +++ b/cmd/apis/proxybundle/proxybundle.go @@ -0,0 +1,125 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxybundle + +import ( + "archive/zip" + "io" + "os" + "path/filepath" + "strings" + + apiproxy "github.com/srinandan/apigeecli/cmd/apis/apiproxydef" + proxies "github.com/srinandan/apigeecli/cmd/apis/proxies" + target "github.com/srinandan/apigeecli/cmd/apis/targetendpoint" +) + +func GenerateAPIProxyBundle(name string) (err error) { + const rootDir = "apiproxy" + + err = os.Mkdir(rootDir, os.ModePerm) + if err != nil { + return err + } + + // write API Proxy file + err = writeXMLData(rootDir+string(os.PathSeparator)+name+".xml", apiproxy.GetAPIProxy()) + if err != nil { + return err + } + + proxiesDirPath := rootDir + string(os.PathSeparator) + "proxies" + targetDirPath := rootDir + string(os.PathSeparator) + "targets" + + err = os.Mkdir(proxiesDirPath, os.ModePerm) + if err != nil { + return err + } + + err = writeXMLData(proxiesDirPath+string(os.PathSeparator)+"default.xml", proxies.GetProxyEndpoint()) + if err != nil { + return err + } + + err = os.Mkdir(targetDirPath, os.ModePerm) + if err != nil { + return err + } + + err = writeXMLData(targetDirPath+string(os.PathSeparator)+"default.xml", target.GetTargetEndpoint()) + if err != nil { + return err + } + + err = archiveBundle(rootDir, name+".zip") + if err != nil { + return err + } + + defer os.RemoveAll(rootDir) // clean up + return nil +} + +func writeXMLData(fileName string, data string) error { + fileWriter, err := os.Create(fileName) + if err != nil { + return err + } + _, err = fileWriter.WriteString(data) + if err != nil { + return err + } + + fileWriter.Close() + return nil +} + +func archiveBundle(pathToZip, destinationPath string) error { + destinationFile, err := os.Create(destinationPath) + if err != nil { + return err + } + myZip := zip.NewWriter(destinationFile) + err = filepath.Walk(pathToZip, func(filePath string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + if err != nil { + return err + } + relPath := strings.TrimPrefix(filePath, filepath.Dir(pathToZip)) + zipFile, err := myZip.Create(relPath) + if err != nil { + return err + } + fsFile, err := os.Open(filePath) + if err != nil { + return err + } + _, err = io.Copy(zipFile, fsFile) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + err = myZip.Close() + if err != nil { + return err + } + return nil +} diff --git a/cmd/apis/targetendpoint/targetendpoint.go b/cmd/apis/targetendpoint/targetendpoint.go new file mode 100644 index 00000000..c2b7e8e5 --- /dev/null +++ b/cmd/apis/targetendpoint/targetendpoint.go @@ -0,0 +1,71 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetendpoint + +import ( + "encoding/xml" +) + +type preFlowDef struct { + XMLName xml.Name `xml:"PreFlow"` + Name string `xml:"name,attr"` + Request requestFlowDef `xml:"Request,omitempty"` + Response responseFlowDef `xml:"Response,omitempty"` +} + +type postFlowDef struct { + XMLName xml.Name `xml:"PostFlow"` + Name string `xml:"name,attr"` + Request requestFlowDef `xml:"Request,omitempty"` + Response responseFlowDef `xml:"Response,omitempty"` +} + +type httpTargetConnectionDef struct { + URL string `xml:"URL"` +} + +type requestFlowDef struct { + Step stepDef `xml:"Step,omitempty"` +} + +type responseFlowDef struct { + Step stepDef `xml:"Step,omitempty"` +} + +type stepDef struct { + Name string `xml:"name"` +} + +type targetEndpointDef struct { + XMLName xml.Name `xml:"TargetEndpoint"` + Name string `xml:"name,attr"` + PreFlow preFlowDef `xml:"PreFlow,omitempty"` + PostFlow postFlowDef `xml:"PostFlow,omitempty"` + HTTPTargetConnection httpTargetConnectionDef `xml:"HTTPTargetConnection,omitempty"` +} + +var targetEndpoint targetEndpointDef + +func GetTargetEndpoint() string { + targetBody, _ := xml.MarshalIndent(targetEndpoint, "", " ") + return string(targetBody) +} + +func NewTargetEndpoint(endpoint string) { + targetEndpoint.Name = "default" + targetEndpoint.PreFlow.Name = "PreFlow" + targetEndpoint.PostFlow.Name = "PostFlow" + targetEndpoint.HTTPTargetConnection.URL = endpoint +} diff --git a/cmd/apis/targets/default.xml b/cmd/apis/targets/default.xml new file mode 100644 index 00000000..8647bf11 --- /dev/null +++ b/cmd/apis/targets/default.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + petstore.swagger.io + + \ No newline at end of file diff --git a/go.mod b/go.mod index 2cadc153..a365da8a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( 9fans.net/go v0.0.2 + github.com/getkin/kin-openapi v0.3.1 github.com/lestrrat/go-jwx v0.0.0-20180221005942-b7d4802280ae github.com/lestrrat/go-pdebug v0.0.0-20180220043741-569c97477ae8 // indirect github.com/spf13/cobra v0.0.5 diff --git a/go.sum b/go.sum index 64e2a264..790287ed 100644 --- a/go.sum +++ b/go.sum @@ -17,12 +17,16 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee 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/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/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/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/getkin/kin-openapi v0.3.1 h1:9mLtayAmieqUnNACL0HqHbxkTc+z1+15sxXpLoJOGEQ= +github.com/getkin/kin-openapi v0.3.1/go.mod h1:W8dhxZgpE84ciM+VIItFqkmZ4eHtuomrdIHtASQIqi0= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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= @@ -99,9 +103,11 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +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.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 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/thedevsaddam/gojsonq v2.2.2+incompatible h1:IDBN1FNzhv9p83n8JEnuu0rrlXIVFlf8K9lWuXPJHfI= github.com/thedevsaddam/gojsonq v2.2.2+incompatible/go.mod h1:RBcQaITThgJAAYKH7FNp2onYodRz8URfsuEGpAch0NA= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -152,4 +158,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=