Merge "CI: Add silent prescan SonarCloud job"
authorJessica Wagantall <jwagantall@linuxfoundation.org>
Wed, 24 Apr 2024 03:59:57 +0000 (03:59 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Wed, 24 Apr 2024 03:59:57 +0000 (03:59 +0000)
32 files changed:
.gitignore
capifcore/Dockerfile
capifcore/capifcore.go [moved from capifcore/main.go with 75% similarity]
capifcore/cmd/main.go [new file with mode: 0644]
capifcore/cmd/main_test.go [moved from capifcore/main_test.go with 93% similarity]
capifcore/container-tag.yaml
capifcore/go.mod
capifcore/go.sum
capifcore/internal/invokermanagement/invokermanagement.go
capifcore/internal/publishservice/publishservice.go
capifcore/internal/securityservice/security.go
servicemanager/Dockerfile
servicemanager/README.md
servicemanager/build-servicemanager-ubuntu.sh
servicemanager/container-tag.yaml
servicemanager/deploy/manifests/capifcore.yaml
servicemanager/deploy/manifests/servicemanager.yaml
servicemanager/deploy/src/delete-from-k8s.sh
servicemanager/deploy/src/deploy-to-k8s.sh
servicemanager/generate.sh
servicemanager/go.mod
servicemanager/go.sum
servicemanager/internal/discoverservice/discoverservice_test.go
servicemanager/internal/envreader/envreader.go
servicemanager/internal/invokermanagement/invokermanagement_test.go
servicemanager/internal/kongclearup.go
servicemanager/internal/providermanagement/providermanagement_test.go
servicemanager/internal/publishservice/publishservice_test.go
servicemanager/internal/publishserviceapi/typeupdate.go
servicemanager/main.go
servicemanager/main_test.go
servicemanager/mockkong/kong_mock.go [new file with mode: 0644]

index 8d0a84f..f57aefb 100644 (file)
@@ -14,6 +14,4 @@ coverage.txt
 # Ignore all .env files
 .env*
 # Except for .env.example
-!.env.example
-# Ignore all files in folders named "mocks"
-**/mocks/*
+!.env.example
\ No newline at end of file
index 7c05e4a..1c0c9e4 100644 (file)
@@ -1,5 +1,6 @@
 #==================================================================================
-#   Copyright (C) 2022: Nordix Foundation
+#   Copyright (C) 2022-2023: Nordix Foundation. All rights reserved.
+#   Copyright (C) 2024: OpenInfra Foundation Europe. All rights reserved.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -24,7 +25,7 @@ COPY go.mod .
 COPY go.sum .
 RUN go mod download
 COPY . .
-RUN go build -o /capifcore
+RUN go build -o /capifcore cmd/main.go
 ##
 ## Deploy
 ##
similarity index 75%
rename from capifcore/main.go
rename to capifcore/capifcore.go
index b0fa1dc..64a540e 100644 (file)
@@ -2,8 +2,8 @@
 //   ========================LICENSE_START=================================
 //   O-RAN-SC
 //   %%
-//   Copyright (C) 2022: Nordix Foundation. All rights reserved.
-//   Copyright (C) 2023 OpenInfra Foundation Europe. All rights reserved.
+//   Copyright (C) 2022-2023: Nordix Foundation. All rights reserved.
+//   Copyright (C) 2023-2024 OpenInfra Foundation Europe. All rights reserved.
 //   %%
 //   Licensed under the Apache License, Version 2.0 (the "License");
 //   you may not use this file except in compliance with the License.
 //   ========================LICENSE_END===================================
 //
 
-package main
+package capifcore
 
 import (
-       "flag"
-       "fmt"
        "net/http"
 
        "github.com/getkin/kin-openapi/openapi3"
        "github.com/labstack/echo/v4"
-       "helm.sh/helm/v3/pkg/cli"
        "oransc.org/nonrtric/capifcore/internal/common29122"
        "oransc.org/nonrtric/capifcore/internal/discoverserviceapi"
        "oransc.org/nonrtric/capifcore/internal/eventsapi"
        "oransc.org/nonrtric/capifcore/internal/invokermanagementapi"
-       "oransc.org/nonrtric/capifcore/internal/keycloak"
        "oransc.org/nonrtric/capifcore/internal/providermanagementapi"
        "oransc.org/nonrtric/capifcore/internal/securityapi"
 
        "github.com/deepmap/oapi-codegen/pkg/middleware"
        echomiddleware "github.com/labstack/echo/v4/middleware"
        log "github.com/sirupsen/logrus"
-       config "oransc.org/nonrtric/capifcore/internal/config"
+       "oransc.org/nonrtric/capifcore/internal/helmmanagement"
+
        "oransc.org/nonrtric/capifcore/internal/discoverservice"
        "oransc.org/nonrtric/capifcore/internal/eventservice"
-       "oransc.org/nonrtric/capifcore/internal/helmmanagement"
        "oransc.org/nonrtric/capifcore/internal/invokermanagement"
        "oransc.org/nonrtric/capifcore/internal/providermanagement"
        "oransc.org/nonrtric/capifcore/internal/publishservice"
        "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
        security "oransc.org/nonrtric/capifcore/internal/securityservice"
+       "oransc.org/nonrtric/capifcore/internal/keycloak"
 )
 
-var url string
-var helmManager helmmanagement.HelmManager
-var repoName string
-
-func main() {
-       var port = flag.Int("port", 8090, "Port for CAPIF Core Function HTTP server")
-       var secPort = flag.Int("secPort", 4433, "Port for CAPIF Core Function HTTPS server")
-       flag.StringVar(&url, "chartMuseumUrl", "", "ChartMuseum URL")
-       flag.StringVar(&repoName, "repoName", "capifcore", "Repository name")
-       var logLevelStr = flag.String("loglevel", "Info", "Log level")
-       var certPath = flag.String("certPath", "certs/cert.pem", "Path for server certificate")
-       var keyPath = flag.String("keyPath", "certs/key.pem", "Path for server private key")
-
-       flag.Parse()
-
-       if loglevel, err := log.ParseLevel(*logLevelStr); err == nil {
-               log.SetLevel(loglevel)
-       }
-
-       // Add repo
-       helmManager = helmmanagement.NewHelmManager(cli.New())
-       err := helmManager.SetUpRepo(repoName, url)
-       if err != nil {
-               log.Warnf("No Helm repo added due to: %s", err.Error())
-       }
-
-       go startWebServer(getEcho(), *port)
-       go startHttpsWebServer(getEcho(), *secPort, *certPath, *keyPath)
-
-       log.Info("Server started and listening on port: ", *port)
-
-       keepServerAlive()
-}
-
-func getEcho() *echo.Echo {
-       e := echo.New()
+func RegisterHandlers(e *echo.Echo, helmManager helmmanagement.HelmManager, km *keycloak.KeycloakManager) {
        // Log all requests
        e.Use(echomiddleware.Logger())
 
-       // Read configuration file
-       cfg, err := config.ReadKeycloakConfigFile("configs")
-       if err != nil {
-               log.Fatalf("Error loading configuration file\n: %s", err)
-       }
-       km := keycloak.NewKeycloakManager(cfg, &http.Client{})
-
        var group *echo.Group
        // Register ProviderManagement
        providerManagerSwagger, err := providermanagementapi.GetSwagger()
@@ -168,21 +123,6 @@ func getEcho() *echo.Echo {
        e.GET("/", hello)
 
        e.GET("/swagger/:apiName", getSwagger)
-
-       return e
-}
-
-func startWebServer(e *echo.Echo, port int) {
-       e.Logger.Fatal(e.Start(fmt.Sprintf("0.0.0.0:%d", port)))
-}
-
-func startHttpsWebServer(e *echo.Echo, port int, certPath string, keyPath string) {
-       e.Logger.Fatal(e.StartTLS(fmt.Sprintf("0.0.0.0:%d", port), certPath, keyPath))
-}
-
-func keepServerAlive() {
-       forever := make(chan int)
-       <-forever
 }
 
 func hello(c echo.Context) error {
diff --git a/capifcore/cmd/main.go b/capifcore/cmd/main.go
new file mode 100644 (file)
index 0000000..48ce77d
--- /dev/null
@@ -0,0 +1,96 @@
+// -
+//   ========================LICENSE_START=================================
+//   O-RAN-SC
+//   %%
+//   Copyright (C) 2022: Nordix Foundation. All rights reserved.
+//   Copyright (C) 2023-2024 OpenInfra Foundation Europe. All rights reserved.
+//   %%
+//   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.
+//   ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+       "flag"
+       "fmt"
+       "net/http"
+
+       "github.com/labstack/echo/v4"
+       "helm.sh/helm/v3/pkg/cli"
+       log "github.com/sirupsen/logrus"
+
+       "oransc.org/nonrtric/capifcore/internal/helmmanagement"
+       config "oransc.org/nonrtric/capifcore/internal/config"
+       "oransc.org/nonrtric/capifcore/internal/keycloak"
+
+       "oransc.org/nonrtric/capifcore"
+)
+
+func main() {
+       var url string
+       var helmManager helmmanagement.HelmManager
+       var repoName string
+
+       var port = flag.Int("port", 8090, "Port for CAPIF Core Function HTTP server")
+       var secPort = flag.Int("secPort", 4433, "Port for CAPIF Core Function HTTPS server")
+       flag.StringVar(&url, "chartMuseumUrl", "", "ChartMuseum URL")
+       flag.StringVar(&repoName, "repoName", "capifcore", "Repository name")
+       var logLevelStr = flag.String("loglevel", "Info", "Log level")
+       var certPath = flag.String("certPath", "certs/cert.pem", "Path for server certificate")
+       var keyPath = flag.String("keyPath", "certs/key.pem", "Path for server private key")
+
+       flag.Parse()
+
+       if loglevel, err := log.ParseLevel(*logLevelStr); err == nil {
+               log.SetLevel(loglevel)
+       }
+
+       // Add Helm repo
+       helmManager = helmmanagement.NewHelmManager(cli.New())
+       err := helmManager.SetUpRepo(repoName, url)
+       if err != nil {
+               log.Warnf("No Helm repo added due to: %s", err.Error())
+       }
+
+       // Read configuration file
+       cfg, err := config.ReadKeycloakConfigFile("configs")
+       if err != nil {
+               log.Fatalf("Error loading configuration file\n: %s", err)
+       }
+       km := keycloak.NewKeycloakManager(cfg, &http.Client{})
+
+       eWeb := echo.New()
+       capifcore.RegisterHandlers(eWeb, helmManager, km)
+       go startWebServer(eWeb, *port)
+
+       eHttpsWeb := echo.New()
+       capifcore.RegisterHandlers(eHttpsWeb, helmManager, km)
+       go startHttpsWebServer(eHttpsWeb, *secPort, *certPath, *keyPath)
+
+       log.Info("Server started and listening on port: ", *port)
+       keepServerAlive()
+}
+
+func startWebServer(e *echo.Echo, port int) {
+       e.Logger.Fatal(e.Start(fmt.Sprintf("0.0.0.0:%d", port)))
+}
+
+func startHttpsWebServer(e *echo.Echo, port int, certPath string, keyPath string) {
+       e.Logger.Fatal(e.StartTLS(fmt.Sprintf("0.0.0.0:%d", port), certPath, keyPath))
+}
+
+func keepServerAlive() {
+       forever := make(chan int)
+       <-forever
+}
similarity index 93%
rename from capifcore/main_test.go
rename to capifcore/cmd/main_test.go
index f174fe3..8b39c11 100644 (file)
@@ -33,13 +33,13 @@ import (
        "github.com/getkin/kin-openapi/openapi3"
        "github.com/labstack/echo/v4"
        "github.com/stretchr/testify/assert"
+       "oransc.org/nonrtric/capifcore"
        "oransc.org/nonrtric/capifcore/internal/common29122"
 )
 
-var e *echo.Echo
-
 func Test_routing(t *testing.T) {
-       e = getEcho()
+       e := echo.New()
+       capifcore.RegisterHandlers(e, nil, nil)
 
        type args struct {
                url          string
@@ -122,7 +122,8 @@ func Test_routing(t *testing.T) {
 }
 
 func TestGetSwagger(t *testing.T) {
-       e = getEcho()
+       e := echo.New()
+       capifcore.RegisterHandlers(e, nil, nil)
 
        type args struct {
                apiPath string
@@ -196,9 +197,11 @@ func TestGetSwagger(t *testing.T) {
 }
 
 func TestHTTPSServer(t *testing.T) {
-       e = getEcho()
+       e := echo.New()
+       capifcore.RegisterHandlers(e, nil, nil)
+
        var port = 44333
-       go startHttpsWebServer(e, 44333, "certs/cert.pem", "certs/key.pem") //"certs/test/cert.pem", "certs/test/key.pem"
+       go startHttpsWebServer(e, 44333, "../certs/cert.pem", "../certs/key.pem") //"certs/test/cert.pem", "certs/test/key.pem"
 
        time.Sleep(100 * time.Millisecond)
 
index c056d71..f17eeca 100644 (file)
@@ -20,4 +20,4 @@
 # By default this file is in the docker build directory,
 # but the location can configured in the JJB template.
 ---
-tag: 1.3.0
+tag: 1.3.1
index fbdc016..4a68ca0 100644 (file)
@@ -8,20 +8,13 @@ require (
        github.com/labstack/echo/v4 v4.10.2
        github.com/sirupsen/logrus v1.9.0
        github.com/stretchr/testify v1.8.1
-       github.com/swaggo/swag v1.8.12
        gopkg.in/yaml.v2 v2.4.0
        helm.sh/helm/v3 v3.9.0
        k8s.io/cli-runtime v0.25.3-rc.0
        k8s.io/client-go v0.25.3-rc.0
 )
 
-require (
-       github.com/KyleBanks/depth v1.2.1 // indirect
-       github.com/go-openapi/spec v0.20.8 // indirect
-       github.com/pkg/errors v0.9.1 // indirect
-       github.com/swaggo/files/v2 v2.0.0 // indirect
-       golang.org/x/tools v0.8.0 // indirect
-)
+require github.com/pkg/errors v0.9.1 // indirect
 
 require (
        github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
@@ -113,7 +106,6 @@ require (
        github.com/spf13/cobra v1.5.0 // indirect
        github.com/spf13/pflag v1.0.5 // indirect
        github.com/stretchr/objx v0.5.0 // indirect
-       github.com/swaggo/echo-swagger v1.4.0
        github.com/valyala/bytebufferpool v1.0.0 // indirect
        github.com/valyala/fasttemplate v1.2.2 // indirect
        github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
index c8d757d..b04095a 100644 (file)
@@ -44,8 +44,6 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I
 github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
-github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
-github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
 github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@@ -186,17 +184,12 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
 github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
-github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
 github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
 github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
-github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU=
-github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
@@ -594,12 +587,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/swaggo/echo-swagger v1.4.0 h1:RCxLKySw1SceHLqnmc41pKyiIeE+OiD7NSI7FUOBlLo=
-github.com/swaggo/echo-swagger v1.4.0/go.mod h1:Wh3VlwjZGZf/LH0s81tz916JokuPG7y/ZqaqnckYqoQ=
-github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
-github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
-github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
-github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
@@ -700,7 +687,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -920,8 +906,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
-golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
index fe0c2cb..0dea817 100644 (file)
@@ -155,7 +155,15 @@ func (im *InvokerManager) prepareNewInvoker(newInvoker *invokerapi.APIInvokerEnr
        defer im.lock.Unlock()
 
        newInvoker.PrepareNewInvoker()
-       im.addClientInKeycloak(newInvoker)
+
+       if im.keycloak != nil {
+               // The type assertion fails when unit testing from ServiceManager where we use Capif as a library, and we are not using Keycloak.
+               if _, ok := im.keycloak.(*keycloak.KeycloakManager); !ok {
+                       // im.keycloak is not nil and its dynamic value is not nil.
+                       im.addClientInKeycloak(newInvoker)
+        }
+       }
+
        im.onboardedInvokers[*newInvoker.ApiInvokerId] = *newInvoker
 }
 
index 09c4ef7..54a7f67 100644 (file)
@@ -204,7 +204,7 @@ func (ps *PublishService) isServicePublished(newService publishapi.ServiceAPIDes
 
 func (ps *PublishService) installHelmChart(newServiceAPIDescription publishapi.ServiceAPIDescription, ctx echo.Context) (bool, error) {
        info := strings.Split(*newServiceAPIDescription.Description, ",")
-       if len(info) == 5 {
+       if (len(info) == 5) && (ps.helmManager != nil) {
                err := ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4])
                if err != nil {
                        return true, sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf("Unable to install Helm chart %s due to: %s", info[3], err.Error()))
@@ -221,7 +221,7 @@ func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, a
                pos, description := getServiceDescription(serviceApiId, serviceDescriptions)
                if description != nil {
                        info := strings.Split(*description.Description, ",")
-                       if len(info) == 5 {
+                       if (len(info) == 5) && (ps.helmManager != nil) {
                                ps.helmManager.UninstallHelmChart(info[1], info[3])
                                log.Debug("Deleted service: ", serviceApiId)
                        }
index 4437123..b3f1d30 100644 (file)
@@ -92,9 +92,17 @@ func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId st
                }
        }
        data := url.Values{"grant_type": {"client_credentials"}, "client_id": {accessTokenReq.ClientId}, "client_secret": {*accessTokenReq.ClientSecret}}
-       jwtToken, err := s.keycloak.GetToken("invokerrealm", data)
-       if err != nil {
-               return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, err.Error())
+
+       var jwtToken keycloak.Jwttoken
+       var err error
+
+       if s.keycloak != nil {
+               jwtToken, err = s.keycloak.GetToken("invokerrealm", data)
+               if err != nil {
+                       return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, err.Error())
+               }
+       } else {
+               return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, "keycloak is nil")
        }
 
        accessTokenResp := securityapi.AccessTokenRsp{
index 85ef603..61f0598 100644 (file)
 ##
 FROM nexus3.o-ran-sc.org:10001/golang:1.19.2-bullseye AS build
 
-WORKDIR /app
+WORKDIR /app/capifcore
 
-COPY go.mod .
-COPY go.sum .
+COPY capifcore/go.mod .
+COPY capifcore/go.sum .
 RUN go mod download
 
-COPY . .
-RUN go build -o /servicemanager
+COPY ./capifcore/. .
 
-WORKDIR "/app/internal"
+WORKDIR /app/servicemanager
+
+COPY servicemanager/go.mod .
+COPY servicemanager/go.sum .
+RUN go mod download
+
+COPY ./servicemanager/. .
+
+RUN go build
+
+WORKDIR /app/servicemanager/internal
 RUN go build kongclearup.go
 
 ##
@@ -38,12 +47,11 @@ RUN go build kongclearup.go
 
 FROM ubuntu:22.04
 
-# We use the /app directory as this is where our servicemanager expects to find its .env file.
-RUN mkdir -p /app
-WORKDIR /app
+# We use the /app/servicemanager directory as this is where our servicemanager build expects to find its .env file.
+WORKDIR /app/servicemanager
 
 ## Copy from "build" stage
-COPY --from=build /servicemanager .
-COPY --from=build /app/internal/kongclearup .
+COPY --from=build /app/servicemanager/servicemanager .
+COPY --from=build /app/servicemanager/internal/kongclearup .
 
-ENTRYPOINT ["/app/servicemanager"]
+ENTRYPOINT ["/app/servicemanager/servicemanager"]
index 4db3c8d..74c92aa 100644 (file)
@@ -40,20 +40,19 @@ specifications from 3GPP, fixes them and then generates the APIs. While these fi
 ```
 
 The specifications are downloaded from the following site; https://www.3gpp.org/ftp/Specs/archive/29_series. To see
-the APIs in swagger format, see the following link; https://github.com/jdegre/5GC_APIs/tree/Rel-16#common-api-framework-capif.
-**NOTE!** The documentation in this link is for release 16 of CAPIF, the downloaded specifications are for release 17.
+the APIs in swagger format, see the following link; https://github.com/jdegre/5GC_APIs/tree/Rel-17#common-api-framework-capif.
 
 To fix the specifications there are three tools.
 - `commoncollector`, collects type definitions from peripheral specifications to keep down the number of dependencies to
-  other specifications. The types to collect are listed in the `definitions.txt`file. Some fixes are hard coded.
+  other specifications. The types to collect are listed in the `definitions.txt` file. Some fixes are hard-coded.
 - `enumfixer`, fixes enumeration definitions so they can be properly generated.
-- `specificationfixer`, fixes flaws in the specifications so they can be properly generated. All fixes are hard coded.
+- `specificationfixer`, fixes flaws in the specifications so they can be properly generated. All fixes are hard-coded.
 
 ## Set Up
 
 First, we need to run `generate.sh` as described above to generate our API code from the 3GPP spec.
 
-Before we can test or run R1-SME-Manager, we need to configure a .env file with the required parameters. Please see the template .env.example in the servicemanager directory.
+Before we can test or run Service Manager, we need to configure a .env file with the required parameters. Please see the template .env.example in the servicemanager directory.
 
 You can set the environmental variable SERVICE_MANAGER_ENV to specify the .env file. For example, the following command specifies to use the config file
 .env.development. If this flag is not set, first we try .env.development and then .env.
@@ -62,9 +61,9 @@ You can set the environmental variable SERVICE_MANAGER_ENV to specify the .env f
 export SERVICE_MANAGER_ENV=development
 ```
 
-### Capifcore and Kong
+### CAPIFcore and Kong
 
-We also need Kong and Capifcore to be running. Please see the examples in the `configs` folder.
+We also need Kong and CAPIFcore to be running. Please see the examples in the `deploy` folder. You can also use https://gerrit.o-ran-sc.org/r/it/dep for deployment. Please see the notes at https://wiki.o-ran-sc.org/display/RICNR/%5BWIP%5D+Service+Manager.
 
 ## Build
 
@@ -76,11 +75,11 @@ go build
 
 ## Unit Tests
 
-To run the unit tests for the application, first ensure that the .env file is configured. In the following example, we specify `.env.test`. For now, we need to disable parallelism in the unit tests with -p=1.
+To run the unit tests for the application, first ensure that the .env file is configured. In the following example, we specify `.env.test`.
 
 ```sh
 export SERVICE_MANAGER_ENV=test
-go test -p=1 -count=1 ./...
+go test ./...
 ```
 
 ## Run Locally
@@ -92,39 +91,35 @@ export SERVICE_MANAGER_ENV=development
 ./servicemanager
 ```
 
-R1-SME-Manager is then available on the port configured in .env.
+Service Manager is then available on the port configured in .env.
 
 ## Building the Docker Image
 
-The application can also be built as a Docker image, by using the following command. We build the image without a .env file. This is supplied by volume mounting at container run-time.
+The application can also be built as a Docker image, by using the following command. We build the image without a .env file. This is supplied by volume mounting at container run-time. Because we need to include CAPIFcore in the Docker build context, we build from the git repo's root directory, sme.
 
 ```sh
-docker image build . -t servicemanager
+docker build -t servicemanager -f servicemanager/Dockerfile .
 ```
 
 ## Kongclearup
 
-Please note that a special executable has been provided for deleting Kong routes and services that have been created ServiceManager in Kong. This executable is called `kongclearup` and is found in the working directory of the ServiceManger Docker image, at `/app/kongclearup`. When we create a Kong route or service, we add Kong tags with information as follows.
+Please note that a special executable has been provided for deleting Kong routes and services that have been created by Service Manager in Kong. This executable is called `kongclearup` and is found in the working directory of the Service Manger Docker image, at `/app/servicemanager`. When we create a Kong route or service, we add Kong tags with information as follows.
   * apfId
   * aefId
   * apiId
   * apiVersion
   * resourceName
 
-When we delete Kong routes and services using `kongclearup`, we check for the existance of these tags, specifically, apfId, apiId and aefId. Only if these tags exist and have values do we proceed to delete the Kong service or route.
-
-The executable `kongclearup` uses the volume-mounted .env file to load the configuration giving the location of Kong.
-
-Please refer to `sme/servicemanager/internal/kongclearup.go`.
+When we delete Kong routes and services using `kongclearup`, we check for the existance of these tags, specifically, apfId, apiId and aefId. Only if these tags exist and have values do we proceed to delete the Kong service or route. The executable `kongclearup` uses the volume-mounted .env file to load the configuration giving the location of Kong. Please refer to `sme/servicemanager/internal/kongclearup.go`.
 
 ## Stand-alone Deployment on Kubernetes
 
-For a stand-alone deployment, please see the `deploy` folder for configurations to deploy to R1-SME-Manager to Kubernetes. We need the following steps.
- - Deploy a PV for Kong's Postgres database (depends on your Kubernetes cluster)
+For a stand-alone deployment, please see the `deploy` folder for configurations to deploy to Service Manager to Kubernetes. We need the following steps.
+ - Deploy a PV for Kong's Postgres database (depends on your Kubernetes cluster, not needed for Minikube)
  - Deploy a PVC for Kong's Postgres database
  - Deploy Kong with Postgres
- - Deploy Capifcore
- - Deploy R1-SME-Manager
+ - Deploy CAPIFcore
+ - Deploy Service Manager
 
 We consolidate the above steps into the script `deploy-to-k8s.sh`. To delete the full deployment, you can use `delete-from-k8s.sh`. The deploy folder has the following structure.
 
@@ -136,10 +131,14 @@ We consolidate the above steps into the script `deploy-to-k8s.sh`. To delete the
 
 We store the Kubernetes manifests files in the manifests in the subfolder. We store the shell scripts in the src folder.
 
-In `deploy-to-k8s.sh`, we copy .env.example and use sed to replace the template values with values for testing/production. You will need to update this part of the script with your own values. There is an example sed replacement in function `substitute_manifest()` in `deploy-to-k8s.sh`. Here, you can substitute your own Docker images for Capifcore and Service Manager for local development.
+In `deploy-to-k8s.sh`, we copy .env.example and use `sed` to replace the template values with values for running the Service Manager container. You will need to update this part of the script with your own values. There is an example sed replacement in function `substitute_manifest()` in `deploy-to-k8s.sh`. Here, you can substitute your own Docker images for CAPIFcore and Service Manager for local development.
 
 In addition there are 2 switches that are added for developer convenience.
- * --repo # allow you to specify your own docker repo
- * --env  # allow you to specify an additional env file, and set SERVICE_MANAGER_ENV to point to this file.
+ * --repo # allows you to specify your own docker repo, e.g. your Docker Hub id
+ * --env  # allows you to specify an additional env file, and sets SERVICE_MANAGER_ENV in the Docker environment to point to this file.
+
+The additional env file needs to exist in the sme/servicemanager folder so that kongclearup can access is. It is specified by its filename. The relative path ../.. is added in the `deploy-to-k8s.sh` script. For example, to use
+
+`./deploy-to-k8s.sh --env .env.development`
 
-`./deploy-to-k8s.sh --repo your-docker-repo-id --env ../../.env.minikube`
+ ../../.env.development needs to exist.
\ No newline at end of file
index 404e7a2..963e3aa 100755 (executable)
@@ -29,7 +29,7 @@ go version
 # Get the go coverage tool helper
 go install github.com/ory/go-acc
 go get github.com/stretchr/testify/mock@v1.7.1
-go-acc ./... --ignore gentools,mocks,common,discoverserviceapi,invokermanagementapi,publishserviceapi,providermanagementapi
+go-acc ./... --ignore mockkong,common,discoverserviceapi,invokermanagementapi,publishserviceapi,providermanagementapi
 
 sed -i 's/oransc\.org\/nonrtric\/servicemanager/servicemanager/' coverage.txt
 
index 2480abf..8f5dc8d 100644 (file)
@@ -19,4 +19,4 @@
 # By default this file is in the docker build directory,
 # but the location can configured in the JJB template.
 ---
-tag: 0.1.0
+tag: 0.1.1
index 7efc107..0c98592 100644 (file)
@@ -36,7 +36,7 @@ spec:
     spec:
       containers:
       - name: capifcore
-        image: o-ran-sc.org/nonrtric/plt/capifcore
+        image: "nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-plt-capifcore:1.3.1"
         imagePullPolicy: IfNotPresent
         ports:
         - containerPort: 8090
index 3f937ff..c49a9a6 100644 (file)
@@ -36,7 +36,7 @@ spec:
     spec:
       containers:
       - name: servicemanager
-        image: o-ran-sc.org/nonrtric/plt/servicemanager
+        image: "nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-plt-servicemanager:0.1.1"
         imagePullPolicy: IfNotPresent
         env:
             - name: SERVICE_MANAGER_ENV
@@ -52,7 +52,7 @@ spec:
             cpu: "80m"
         volumeMounts:
         - name: config-volume
-          mountPath: /app/.env
+          mountPath: /app/servicemanager/.env
           subPath: .env
       volumes:
       - name: config-volume
index fd5289c..f86af1f 100755 (executable)
 echo $(date -u) "delete-from-k8s started"
 
 # Delete R1-SME-Manager with Capifcore
+echo "Warning - deleting Kong routes and services for ServiceManager"
+SERVICEMANAGER_POD=$(kubectl get pods -o custom-columns=NAME:.metadata.name -l app=servicemanager --no-headers -n servicemanager)
+if [[ -n $SERVICEMANAGER_POD ]]; then
+    kubectl exec $SERVICEMANAGER_POD -n servicemanager -- ./kongclearup
+else
+    echo "Error - Servicemanager pod not found, didn't delete Kong routes and services for ServiceManager"
+fi
+
 kubectl delete -f ../manifests/servicemanager.yaml
 kubectl delete configmap env-configmap -n servicemanager
 kubectl delete -f ../manifests/capifcore.yaml
+
 kubectl delete ns servicemanager
 
 # Delete Kong
-go run ../../internal/kongclearup.go
 helm uninstall kong -n kong
 helm repo remove kong
-kubectl wait deploy/kong-kong --for=delete --timeout=-300s -n kong 
+kubectl wait deploy/kong-kong --for=delete --timeout=-300s -n kong
 
 # Delete storage for the Postgres used by Kong
 kubectl delete -f ../manifests/kong-postgres-pvc.yaml
index 9e81d61..be890dd 100755 (executable)
 create_env_from_template(){
     # Set up .env file for Kubernetes Config Map
     echo "create_env_from_template"
-    cp -v ../../.env.example ./.env
-    sed -i 's/KONG_DOMAIN=<string>/KONG_DOMAIN=kong/' .env
-    sed -i 's/KONG_PROTOCOL=<http or https protocol scheme>/KONG_PROTOCOL=http/' .env
-    sed -i 's/KONG_IPV4=<host string>/KONG_IPV4=10.101.1.101/' .env
-    sed -i 's/KONG_DATA_PLANE_PORT=<port number>/KONG_DATA_PLANE_PORT=32080/' .env
-    sed -i 's/KONG_CONTROL_PLANE_PORT=<port number>/KONG_CONTROL_PLANE_PORT=32081/' .env
-    sed -i 's/CAPIF_PROTOCOL=<http or https protocol scheme>/CAPIF_PROTOCOL=http/' .env
-    sed -i 's/CAPIF_IPV4=<host>/CAPIF_IPV4=10.101.1.101/' .env
-    sed -i 's/CAPIF_PORT=<port number>/CAPIF_PORT=31570/' .env
-    sed -i 's/LOG_LEVEL=<Trace, Debug, Info, Warning, Error, Fatal or Panic>/LOG_LEVEL=Info/' .env
-    sed -i 's/SERVICE_MANAGER_PORT=<port number>/SERVICE_MANAGER_PORT=8095/' .env
-    sed -i 's/TEST_SERVICE_IPV4=<host string>/TEST_SERVICE_IPV4=10.101.1.101/' .env
-    sed -i 's/TEST_SERVICE_PORT=<port number>/TEST_SERVICE_PORT=30951/' .env
+    if [ ! -f ../../.env ]; then
+        cp -v ../../.env.example ../../.env
+        sed -i 's/KONG_DOMAIN=<string>/KONG_DOMAIN=kong/' ../../.env
+        sed -i 's/KONG_PROTOCOL=<http or https protocol scheme>/KONG_PROTOCOL=http/' ../../.env
+        sed -i 's/KONG_IPV4=<host string>/KONG_IPV4=10.101.1.101/' ../../.env
+        sed -i 's/KONG_DATA_PLANE_PORT=<port number>/KONG_DATA_PLANE_PORT=32080/' ../../.env
+        sed -i 's/KONG_CONTROL_PLANE_PORT=<port number>/KONG_CONTROL_PLANE_PORT=32081/' ../../.env
+        sed -i 's/CAPIF_PROTOCOL=<http or https protocol scheme>/CAPIF_PROTOCOL=http/' ../../.env
+        sed -i 's/CAPIF_IPV4=<host>/CAPIF_IPV4=10.101.1.101/' ../../.env
+        sed -i 's/CAPIF_PORT=<port number>/CAPIF_PORT=31570/' ../../.env
+        sed -i 's/LOG_LEVEL=<Trace, Debug, Info, Warning, Error, Fatal or Panic>/LOG_LEVEL=Info/' ../../.env
+        sed -i 's/SERVICE_MANAGER_PORT=<port number>/SERVICE_MANAGER_PORT=8095/' ../../.env
+        sed -i 's/TEST_SERVICE_IPV4=<host string>/TEST_SERVICE_IPV4=10.101.1.101/' ../../.env
+        sed -i 's/TEST_SERVICE_PORT=<port number>/TEST_SERVICE_PORT=30951/' ../../.env
+        echo "created .env"
+    else
+        echo "found .env"
+    fi
 }
 
 substitute_repo(){
@@ -48,28 +53,34 @@ substitute_repo(){
 
 add_env(){
     echo "add_env"
-    additional_env=$1
+    # Our additional .env has to exist in the project root folder
+    additional_env="../../$1"
+
     # Add our own .env file
     if [ -f $additional_env ]; then
-        echo "found additional env"
-        kubectl create configmap env-configmap --from-file=.env --from-file=$additional_env -n servicemanager
+        echo "found additional env $1"
+        kubectl create configmap env-configmap --from-file=../../.env --from-file=$additional_env -n servicemanager
 
         # Add additional env file to volume mounting
         env_filename=$(basename "$additional_env")
         echo "env_filename $env_filename"
 
-        mount_path_wc=$(grep "mountPath: /app/$env_filename" ../manifests/servicemanager.yaml | wc -l)
+        mount_path_wc=$(grep "mountPath: /app/servicemanager/$env_filename" ../manifests/servicemanager.yaml | wc -l)
         env_path_count=$((mount_path_wc))
         if [ $env_path_count -eq 0 ]; then
             echo "Adding mount path"
-            sed -i -e '/subPath: .env/a\' -e "        - name: config-volume\n          mountPath: /app/$env_filename\n          subPath: $env_filename" ../manifests/servicemanager.yaml
+            sed -i -e '/subPath: .env/a\' \
+                -e "        - name: config-volume\n          mountPath: /app/servicemanager/$env_filename\n          subPath: $env_filename" ../manifests/servicemanager.yaml
         fi
 
         # Update SERVICE_MANAGER_ENV to point to additional env
         env_extension=$(basename "$additional_env" | awk -F. '{print $NF}')
-        echo "env_extension $env_extension"
-
+        echo "SERVICE_MANAGER_ENV=$env_extension"
         sed -i "/- name: SERVICE_MANAGER_ENV/{n;s/              value: \"\"/              value: \"$env_extension\"/}" ../manifests/servicemanager.yaml
+        return 0  # Return zero for success
+    else
+        echo "additional env $additional_env NOT found"
+        return 1  # Return non-zero for failure
     fi
 }
 
@@ -136,8 +147,12 @@ kubectl create ns servicemanager
 
 if [ "$ADD_ENV" = true ]; then
     add_env $ENV_PATH
+    # Check if the function failed
+    if [ $? -ne 0 ]; then
+        kubectl create configmap env-configmap --from-file=../../.env -n servicemanager
+    fi
 else
-    kubectl create configmap env-configmap --from-file=.env -n servicemanager
+    kubectl create configmap env-configmap --from-file=../../.env -n servicemanager
 fi
 
 # Create the Kubernetes resources
index efe7a1a..3292703 100755 (executable)
@@ -170,10 +170,10 @@ generate_apis_from_spec () {
 }
 
 run_tests () {
-    # Make sure that SERVICE_MANAGER_ENV is configured with the required .env file, e.g. 
+    # Make sure that SERVICE_MANAGER_ENV is configured with the required .env file, e.g.
     # export SERVICE_MANAGER_ENV=development
     cd "$cwd"
-    go test -p=1 -count=1 ./...
+    go test ./...
 }
 
 # Main code block
index 6be0d03..0efcbaa 100644 (file)
@@ -7,34 +7,142 @@ require (
        github.com/getkin/kin-openapi v0.106.0
        github.com/go-resty/resty/v2 v2.10.0
        github.com/joho/godotenv v1.5.1
-       github.com/labstack/echo/v4 v4.9.1
+       github.com/labstack/echo/v4 v4.10.2
        github.com/sirupsen/logrus v1.9.3
-       github.com/stretchr/testify v1.7.1
+       github.com/stretchr/testify v1.8.1
+       oransc.org/nonrtric/capifcore v0.0.0-00010101000000-000000000000
 )
 
-require gopkg.in/yaml.v2 v2.4.0 // indirect
-
 require (
+       github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
+       github.com/BurntSushi/toml v1.1.0 // indirect
+       github.com/MakeNowJust/heredoc v1.0.0 // indirect
+       github.com/Masterminds/goutils v1.1.1 // indirect
+       github.com/Masterminds/semver/v3 v3.1.1 // indirect
+       github.com/Masterminds/sprig/v3 v3.2.2 // indirect
+       github.com/Masterminds/squirrel v1.5.3 // indirect
+       github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
+       github.com/beorn7/perks v1.0.1 // indirect
+       github.com/cespare/xxhash/v2 v2.1.2 // indirect
+       github.com/chai2010/gettext-go v1.0.2 // indirect
+       github.com/containerd/containerd v1.6.6 // indirect
+       github.com/cyphar/filepath-securejoin v0.2.3 // indirect
        github.com/davecgh/go-spew v1.1.1 // indirect
-       github.com/go-openapi/jsonpointer v0.19.5 // indirect
-       github.com/go-openapi/swag v0.21.1 // indirect
+       github.com/docker/cli v20.10.17+incompatible // indirect
+       github.com/docker/distribution v2.8.1+incompatible // indirect
+       github.com/docker/docker v20.10.17+incompatible // indirect
+       github.com/docker/docker-credential-helpers v0.6.4 // indirect
+       github.com/docker/go-connections v0.4.0 // indirect
+       github.com/docker/go-metrics v0.0.1 // indirect
+       github.com/docker/go-units v0.4.0 // indirect
+       github.com/emicklei/go-restful/v3 v3.8.0 // indirect
+       github.com/evanphx/json-patch v5.6.0+incompatible // indirect
+       github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
+       github.com/fatih/color v1.13.0 // indirect
+       github.com/go-errors/errors v1.0.1 // indirect
+       github.com/go-gorp/gorp/v3 v3.0.2 // indirect
+       github.com/go-logr/logr v1.2.3 // indirect
+       github.com/go-openapi/jsonpointer v0.19.6 // indirect
+       github.com/go-openapi/jsonreference v0.20.2 // indirect
+       github.com/go-openapi/swag v0.22.3 // indirect
+       github.com/gobwas/glob v0.2.3 // indirect
+       github.com/gogo/protobuf v1.3.2 // indirect
        github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
+       github.com/golang/protobuf v1.5.2 // indirect
+       github.com/google/btree v1.0.1 // indirect
+       github.com/google/gnostic v0.5.7-v3refs // indirect
+       github.com/google/go-cmp v0.5.8 // indirect
+       github.com/google/gofuzz v1.2.0 // indirect
+       github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
        github.com/google/uuid v1.3.0 // indirect
        github.com/gorilla/mux v1.8.0 // indirect
+       github.com/gosuri/uitable v0.0.4 // indirect
+       github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
+       github.com/huandu/xstrings v1.3.2 // indirect
+       github.com/imdario/mergo v0.3.12 // indirect
+       github.com/inconshreveable/mousetrap v1.0.0 // indirect
        github.com/invopop/yaml v0.1.0 // indirect
+       github.com/jmoiron/sqlx v1.3.5 // indirect
        github.com/josharian/intern v1.0.0 // indirect
+       github.com/json-iterator/go v1.1.12 // indirect
+       github.com/klauspost/compress v1.13.6 // indirect
        github.com/labstack/gommon v0.4.0 // indirect
+       github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
+       github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
+       github.com/lib/pq v1.10.6 // indirect
+       github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
        github.com/mailru/easyjson v0.7.7 // indirect
        github.com/mattn/go-colorable v0.1.13 // indirect
        github.com/mattn/go-isatty v0.0.18 // indirect
+       github.com/mattn/go-runewidth v0.0.9 // indirect
+       github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
+       github.com/mitchellh/copystructure v1.2.0 // indirect
+       github.com/mitchellh/go-wordwrap v1.0.0 // indirect
+       github.com/mitchellh/reflectwalk v1.0.2 // indirect
+       github.com/moby/locker v1.0.1 // indirect
+       github.com/moby/spdystream v0.2.0 // indirect
+       github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
+       github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+       github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+       github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
+       github.com/morikuni/aec v1.0.0 // indirect
+       github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+       github.com/opencontainers/go-digest v1.0.0 // indirect
+       github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
+       github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
+       github.com/pkg/errors v0.9.1 // indirect
        github.com/pmezard/go-difflib v1.0.0 // indirect
+       github.com/prometheus/client_golang v1.12.1 // indirect
+       github.com/prometheus/client_model v0.2.0 // indirect
+       github.com/prometheus/common v0.32.1 // indirect
+       github.com/prometheus/procfs v0.7.3 // indirect
+       github.com/rubenv/sql-migrate v1.1.2 // indirect
+       github.com/russross/blackfriday v1.5.2 // indirect
+       github.com/shopspring/decimal v1.2.0 // indirect
+       github.com/spf13/cast v1.4.1 // indirect
+       github.com/spf13/cobra v1.5.0 // indirect
+       github.com/spf13/pflag v1.0.5 // indirect
        github.com/valyala/bytebufferpool v1.0.0 // indirect
        github.com/valyala/fasttemplate v1.2.2 // indirect
+       github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+       github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+       github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+       github.com/xlab/treeprint v1.1.0 // indirect
+       go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
        golang.org/x/crypto v0.14.0 // indirect
        golang.org/x/net v0.17.0 // indirect
+       golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
+       golang.org/x/sync v0.1.0 // indirect
        golang.org/x/sys v0.13.0 // indirect
+       golang.org/x/term v0.13.0 // indirect
        golang.org/x/text v0.13.0 // indirect
        golang.org/x/time v0.3.0 // indirect
+       google.golang.org/appengine v1.6.7 // indirect
+       google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
+       google.golang.org/grpc v1.47.0 // indirect
+       google.golang.org/protobuf v1.28.0 // indirect
+       gopkg.in/inf.v0 v0.9.1 // indirect
+       gopkg.in/yaml.v2 v2.4.0 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
+       helm.sh/helm/v3 v3.9.0 // indirect
+       k8s.io/api v0.25.3-rc.0 // indirect
+       k8s.io/apiextensions-apiserver v0.25.2 // indirect
+       k8s.io/apimachinery v0.25.3-rc.0 // indirect
+       k8s.io/apiserver v0.25.2 // indirect
+       k8s.io/cli-runtime v0.25.3-rc.0 // indirect
+       k8s.io/client-go v0.25.3-rc.0 // indirect
+       k8s.io/component-base v0.25.2 // indirect
+       k8s.io/klog/v2 v2.70.1 // indirect
+       k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
+       k8s.io/kubectl v0.25.2 // indirect
+       k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
+       oras.land/oras-go v1.1.1 // indirect
+       sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
+       sigs.k8s.io/kustomize/api v0.12.1 // indirect
+       sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
+       sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
+       sigs.k8s.io/yaml v1.3.0 // indirect
 )
+
+replace oransc.org/nonrtric/capifcore => ../capifcore
index 99a1740..9b2f69f 100644 (file)
@@ -1,5 +1,118 @@
+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 v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
+github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
+github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
+github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
+github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
+github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
+github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
+github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
+github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
+github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
+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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
+github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
+github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4=
+github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0=
+github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
+github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
+github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
+github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
 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=
@@ -8,6 +121,45 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
 github.com/deepmap/oapi-codegen v1.11.0 h1:f/X2NdIkaBKsSdpeuwLnY/vDI0AtPUrmB5LMgc7YD+A=
 github.com/deepmap/oapi-codegen v1.11.0/go.mod h1:k+ujhoQGxmQYBZBbxhOZNZf4j08qv5mC+OH+fFTnKxM=
+github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY=
+github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M=
+github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
+github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
+github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
+github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
+github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
+github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
+github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
+github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
+github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac1BUPam4aw6Q=
 github.com/getkin/kin-openapi v0.106.0 h1:hrqfqJPAvWvuO/V0lCr/xyQOq4Gy21mcr28JJOSRcEI=
 github.com/getkin/kin-openapi v0.106.0/go.mod h1:9Dhr+FasATJZjS4iOLvB0hkaxgYdulrNYm2e9epLWOo=
@@ -15,11 +167,32 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
 github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
-github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
+github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
 github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
@@ -29,28 +202,181 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
 github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
 github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo=
 github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
+github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
+github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0=
+github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY=
+github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY=
+github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc=
+github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
+github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
+github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
+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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
+github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
+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/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+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/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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 v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
+github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
 github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
+github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
+github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
+github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@@ -59,11 +385,15 @@ 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/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
-github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
-github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
+github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
+github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
 github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
+github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
+github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
+github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
+github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
@@ -74,45 +404,193 @@ github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbq
 github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
 github.com/lestrrat-go/jwx v1.2.24/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
+github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
+github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
+github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
+github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
+github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
+github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
 github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/go-homedir v1.0.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 v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
+github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
+github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
+github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
+github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
+github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
+github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
+github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
+github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
+github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg=
+github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
+github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+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/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
+github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ=
+github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
+github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
+github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
+github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
+github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
+github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+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/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
@@ -122,10 +600,55 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
 github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
+github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
+github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
+go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -133,12 +656,84 @@ golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
 golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+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/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/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/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/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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -147,22 +742,99 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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-20181026203630-95b1ffbd15a5/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-20190215142949-d0b11bdaac8a/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-20190507160741-ecd444e8653b/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -177,22 +849,81 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-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-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/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-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@@ -201,16 +932,134 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
+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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/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/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I=
+google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
+google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@@ -221,3 +1070,52 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
+helm.sh/helm/v3 v3.9.0 h1:qDSWViuF6SzZX5s5AB/NVRGWmdao7T5j4S4ebIkMGag=
+helm.sh/helm/v3 v3.9.0/go.mod h1:fzZfyslcPAWwSdkXrXlpKexFeE2Dei8N27FFQWt+PN0=
+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=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.25.3-rc.0 h1:abmQ96RwK/xVgFrDxIkxdCoQ4qr7Uh+sPfjIpecpJIg=
+k8s.io/api v0.25.3-rc.0/go.mod h1:KTkuGVHV7vKlkf1FHDtbF687n22h3pQ6ovWs/Eobm78=
+k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo=
+k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8=
+k8s.io/apimachinery v0.25.3-rc.0 h1:nhztVhLsZuw2xENfIrVXcsNUiemnlH/r+Bb8hbl/1gA=
+k8s.io/apimachinery v0.25.3-rc.0/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
+k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw=
+k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I=
+k8s.io/cli-runtime v0.25.3-rc.0 h1:hTBm1tPNY4uXfpbeOZjT18AlzaEIN0CgfB6rz1XLuKA=
+k8s.io/cli-runtime v0.25.3-rc.0/go.mod h1:o3qnMjKIbAT8hrc5gByX+Pi3As4bqVxDtr6bkysAhWw=
+k8s.io/client-go v0.25.3-rc.0 h1:RDSXTIbzXU8jB1dElqSiF1nyMhiZgEzqVeGA874Hkm4=
+k8s.io/client-go v0.25.3-rc.0/go.mod h1:qkx9IzH1FM7r01F4vWZEYHXTPZadHtJpmzZQZyrrWdM=
+k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY=
+k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
+k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
+k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
+k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA=
+k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+oras.land/oras-go v1.1.1 h1:gI00ftziRivKXaw1BdMeEoIA4uBgga33iVlOsEwefFs=
+oras.land/oras-go v1.1.1/go.mod h1:n2TE1ummt9MUyprGhT+Q7kGZUF4kVUpYysPFxeV2IpQ=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM=
+sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s=
+sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk=
+sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
index 2424658..7c4b9f8 100644 (file)
@@ -23,6 +23,8 @@ package discoverservice
 import (
        "fmt"
        "net/http"
+       "net/http/httptest"
+       "net/url"
        "os"
        "reflect"
        "sort"
@@ -46,13 +48,24 @@ import (
 
        "oransc.org/nonrtric/servicemanager/internal/envreader"
        "oransc.org/nonrtric/servicemanager/internal/kongclear"
-       "oransc.org/nonrtric/servicemanager/internal/providermanagement"
 
+       "oransc.org/nonrtric/servicemanager/internal/providermanagement"
        provapi "oransc.org/nonrtric/servicemanager/internal/providermanagementapi"
        "oransc.org/nonrtric/servicemanager/internal/publishservice"
+
+       "oransc.org/nonrtric/capifcore"
+       "oransc.org/nonrtric/servicemanager/mockkong"
 )
 
-var requestHandler *echo.Echo
+var (
+       eServiceManager          *echo.Echo
+       eCapifWeb                *echo.Echo
+       eKong                    *echo.Echo
+       mockConfigReader         *envreader.MockConfigReader
+       serviceManagerServer *httptest.Server
+       capifServer              *httptest.Server
+       mockKongServer           *httptest.Server
+)
 
 func TestMain(m *testing.M) {
        err := setupTest()
@@ -68,22 +81,69 @@ func TestMain(m *testing.M) {
 }
 
 func setupTest() error {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       // Start the mock Kong server
+       eKong = echo.New()
+       mockKong.RegisterHandlers(eKong)
+       mockKongServer = httptest.NewServer(eKong)
+
+       // Parse the server URL
+       parsedMockKongURL, err := url.Parse(mockKongServer.URL)
        if err != nil {
-               log.Fatal("error loading environment file on setupTest")
+               log.Fatalf("error parsing mock Kong URL: %v", err)
                return err
        }
 
-       requestHandler, err = getEcho(myEnv, myPorts)
+       // Extract the host and port
+       mockKongHost := parsedMockKongURL.Hostname()
+       mockKongControlPlanePort := parsedMockKongURL.Port()
+
+       eCapifWeb = echo.New()
+       capifcore.RegisterHandlers(eCapifWeb, nil, nil)
+       capifServer = httptest.NewServer(eCapifWeb)
+
+       // Parse the server URL
+       parsedCapifURL, err := url.Parse(capifServer.URL)
+       if err != nil {
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       capifHost := parsedCapifURL.Hostname()
+       capifPort := parsedCapifURL.Port()
+
+       // Set up the mock config reader with the desired configuration for testing
+       mockConfigReader = &envreader.MockConfigReader{
+               MockedConfig: map[string]string{
+                       "KONG_DOMAIN":             "kong",
+                       "KONG_PROTOCOL":           "http",
+                       "KONG_IPV4":               mockKongHost,
+                       "KONG_DATA_PLANE_PORT":    "32080",
+                       "KONG_CONTROL_PLANE_PORT": mockKongControlPlanePort,
+                       "CAPIF_PROTOCOL":          "http",
+                       "CAPIF_IPV4":              capifHost,
+                       "CAPIF_PORT":              capifPort,
+                       "LOG_LEVEL":               "Info",
+                       "SERVICE_MANAGER_PORT":    "8095",
+                       "TEST_SERVICE_IPV4":       "10.101.1.101",
+                       "TEST_SERVICE_PORT":       "30951",
+               },
+       }
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
-               log.Fatal("getEcho fatal error on setupTest")
+               log.Fatal("error loading environment file on setupTest")
                return err
        }
-       err = teardown()
+
+       eServiceManager = echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
        if err != nil {
-               log.Fatal("getEcho fatal error on teardown")
+               log.Fatal("registerHandlers fatal error on setupTest")
                return err
        }
+       serviceManagerServer = httptest.NewServer(eServiceManager)
+       capifCleanUp()
 
        return err
 }
@@ -118,16 +178,15 @@ func getProvider() provapi.APIProviderEnrolmentDetails {
        }
 }
 
-func teardown() error {
-       log.Trace("entering teardown")
 
-       t := new(testing.T) // Create a new testing.T instance for teardown
+func capifCleanUp()  {
+       t := new(testing.T) // Create a new testing.T instance for capifCleanUp
 
        // Delete the invoker
        invokerInfo := "invoker a"
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
-       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the original published service
@@ -135,7 +194,7 @@ func teardown() error {
        apiName := "apiName"
        apiId := "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the first published service
@@ -143,22 +202,26 @@ func teardown() error {
        apiName = "apiName1"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the second published service
        apiName = "apiName2"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the provider
        domainID := "domain_id_Kong"
-       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
+}
+
+func teardown() error {
+       log.Trace("entering teardown")
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file")
                return err
@@ -168,15 +231,20 @@ func teardown() error {
        if err != nil {
                log.Fatal("error clearing Kong on teardown")
        }
+
+       mockKongServer.Close()
+       capifServer.Close()
+       serviceManagerServer.Close()
+
        return err
 }
 
 func TestRegisterValidProvider(t *testing.T) {
-       teardown()
+       capifCleanUp()
        newProvider := getProvider()
 
        // Register a valid provider
-       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultProvider provapi.APIProviderEnrolmentDetails
@@ -189,7 +257,7 @@ func TestPublishUnpublishService(t *testing.T) {
        apiName := "apiName1"
        apiId := "api_id_" + apiName
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        assert.Nil(t, err, "error reading env file")
 
        testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
@@ -199,7 +267,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
 
        // Check no services published
-       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -226,7 +294,7 @@ func TestPublishUnpublishService(t *testing.T) {
        newServiceDescription := getServiceAPIDescription(aefId, apiName, apiCategory, apiVersion, &protocolHTTP11, &dataFormatJSON, description, testServiceIpv4, testServicePort, publishapi.CommunicationTypeREQUESTRESPONSE)
 
        // Publish a service for provider
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        if result.Code() != http.StatusCreated {
@@ -241,7 +309,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.Equal(t, "http://example.com/published-apis/v1/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
 
        // Check that the service is published for the provider
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        err = result.UnmarshalJsonToObject(&resultService)
@@ -264,7 +332,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.Equal(t, kongDataPlanePort, resultServicePort)
 
        // Check one service published
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -284,7 +352,7 @@ func TestPublishUnpublishService(t *testing.T) {
 
        newServiceDescription2 := getServiceAPIDescription(aefId, apiName2, apiCategory, apiVersion, &protocolHTTP1, &dataFormatOther, description, testServiceIpv4, testServicePort, publishapi.CommunicationTypeSUBSCRIBENOTIFY)
 
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription2).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription2).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        if result.Code() != http.StatusCreated {
@@ -297,7 +365,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.Equal(t, apiId2, *resultService.ApiId)
 
        // Check no services published
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -313,7 +381,7 @@ func TestOnboardInvoker(t *testing.T) {
        newInvoker := getInvoker(invokerInfo)
 
        // Onboard a valid invoker
-       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultInvoker invokermanagementapi.APIInvokerEnrolmentDetails
@@ -336,7 +404,7 @@ func TestGetAllServiceAPIs(t *testing.T) {
        apiName2 := "apiName2"
 
        // Get all APIs, without any filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -364,7 +432,7 @@ func TestGetAllServiceAPIsWhenMissingProvider(t *testing.T) {
        invokerId := "unregistered"
 
        // Get all APIs, without any filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNotFound, result.Code())
 
        var problemDetails common29122.ProblemDetails
@@ -383,7 +451,7 @@ func TestFilterApiName(t *testing.T) {
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-name="+apiName).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-name="+apiName).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -408,7 +476,7 @@ func TestFilterAefId(t *testing.T) {
        aefId := "AEF_id_rApp_Kong_as_AEF"
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&aef-id="+aefId).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&aef-id="+aefId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -438,7 +506,7 @@ func TestFilterVersion(t *testing.T) {
        apiVersion := "v1"
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-version="+apiVersion).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-version="+apiVersion).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -463,7 +531,7 @@ func TestFilterCommType(t *testing.T) {
        commType := publishapi.CommunicationTypeREQUESTRESPONSE
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&comm-type="+string(commType)).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&comm-type="+string(commType)).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -488,7 +556,7 @@ func TestFilterVersionAndCommType(t *testing.T) {
        commType := publishapi.CommunicationTypeREQUESTRESPONSE
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-version="+apiVersion+"&comm-type="+string(commType)).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-version="+apiVersion+"&comm-type="+string(commType)).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -512,7 +580,7 @@ func TestFilterAPICategory(t *testing.T) {
        apiCategory := "apiCategory"
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-cat="+apiCategory).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&api-cat="+apiCategory).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -536,7 +604,7 @@ func TestFilterProtocol(t *testing.T) {
        var protocolHTTP11 = publishapi.ProtocolHTTP11
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&protocol="+string(protocolHTTP11)).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&protocol="+string(protocolHTTP11)).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -561,7 +629,7 @@ func TestFilterDataFormat(t *testing.T) {
        var dataFormatJSON = publishapi.DataFormatJSON
 
        // Get APIs with filter
-       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&data-format="+string(dataFormatJSON)).Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/service-apis/v1/allServiceAPIs?api-invoker-id="+invokerId+"&data-format="+string(dataFormatJSON)).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultDiscovery discoverserviceapi.DiscoveredAPIs
@@ -576,10 +644,10 @@ func TestFilterDataFormat(t *testing.T) {
                        assert.Equal(t, apiName, (*resultDiscovery.ServiceAPIDescriptions)[0].ApiName)
                }
        }
-       teardown()
+       capifCleanUp()
 }
 
-func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
+func registerHandlers(e *echo.Echo, myEnv map[string]string, myPorts map[string]int) (err error) {
        capifProtocol := myEnv["CAPIF_PROTOCOL"]
        capifIPv4 := common29122.Ipv4Addr(myEnv["CAPIF_IPV4"])
        capifPort := common29122.Port(myPorts["CAPIF_PORT"])
@@ -589,13 +657,11 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
        kongControlPlanePort := common29122.Port(myPorts["KONG_CONTROL_PLANE_PORT"])
 
-       e := echo.New()
-
        // Register ProviderManagement
        providerManagerSwagger, err := provapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading ProviderManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        providerManagerSwagger.Servers = nil
        providerManager := providermanagement.NewProviderManager(capifProtocol, capifIPv4, capifPort)
@@ -610,7 +676,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        publishServiceSwagger, err := publishapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading PublishService swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        publishServiceSwagger.Servers = nil
@@ -626,7 +692,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        invokerServiceSwagger, err := invokermanagementapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading InvokerManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        invokerServiceSwagger.Servers = nil
@@ -654,7 +720,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        group.Use(middleware.OapiRequestValidator(discoverySeviceSwagger))
        discoverserviceapi.RegisterHandlersWithBaseURL(e, ds, "service-apis/v1")
 
-       return e, err
+       return err
 }
 
 func getServiceAPIDescription(aefId string, apiName string, apiCategory string, apiVersion string, protocol *publishapi.Protocol, dataFormat *publishapi.DataFormat, description string, testServiceIpv4 common29122.Ipv4Addr, testServicePort common29122.Port, commType publishapi.CommunicationType) publishapi.ServiceAPIDescription {
index 5d1b6de..0bdbee7 100644 (file)
@@ -30,10 +30,19 @@ import (
        log "github.com/sirupsen/logrus"
 )
 
-func ReadDotEnv() (map[string]string, map[string]int, error) {
+type ConfigReader interface {
+    ReadDotEnv() (map[string]string, map[string]int, error)
+}
+
+// RealConfigReader implements ConfigReader
+type RealConfigReader struct {
+}
+
+func (r *RealConfigReader) ReadDotEnv() (map[string]string, map[string]int, error) {
        log.SetFormatter(&log.TextFormatter{
                FullTimestamp: true,
        })
+       setLogLevel("Info")
 
        env := os.Getenv("SERVICE_MANAGER_ENV")
        log.Infof("read SERVICE_MANAGER_ENV: %s", env)
@@ -61,14 +70,30 @@ func ReadDotEnv() (map[string]string, map[string]int, error) {
                        return nil, nil, err
                }
        }
-       log.Infof("imported .env: %s", envFile)
 
-       loglevel, err := log.ParseLevel(myEnv["LOG_LEVEL"])
+       setLogLevel(myEnv["LOG_LEVEL"])
+       logConfig(myEnv, envFile)
+
+       myPorts, err := createMapPorts(myEnv)
+       return myEnv, myPorts, err
+}
+
+func setLogLevel(logLevel string) error {
+       log.SetFormatter(&log.TextFormatter{
+               FullTimestamp: true,
+       })
+
+       loglevel, err := log.ParseLevel(logLevel)
        if err != nil {
-               log.Fatalf("error loading LOG_LEVEL from .env file: %s", err)
-               return nil, nil, err
+               log.Fatalf("error loading LOG_LEVEL from .env file: %v", err)
+               return err
        }
        log.SetLevel(loglevel)
+       return nil
+}
+
+func logConfig(myEnv map[string]string, envFile string) {
+       log.Infof("imported .env: %s", envFile)
 
        log.Infof("KONG_DOMAIN %s", myEnv["KONG_DOMAIN"])
        log.Infof("KONG_PROTOCOL %s", myEnv["KONG_PROTOCOL"])
@@ -82,31 +107,34 @@ func ReadDotEnv() (map[string]string, map[string]int, error) {
        log.Infof("SERVICE_MANAGER_PORT %s", myEnv["SERVICE_MANAGER_PORT"])
        log.Infof("TEST_SERVICE_IPV4 %s", myEnv["TEST_SERVICE_IPV4"])
        log.Infof("TEST_SERVICE_PORT %s", myEnv["TEST_SERVICE_PORT"])
+}
 
-       var myPorts = make(map[string]int)
+func createMapPorts(myEnv map[string]string) (map[string]int, error) {
+    myPorts := make(map[string]int)
+       var err error
 
        myPorts["KONG_DATA_PLANE_PORT"], err = strconv.Atoi(myEnv["KONG_DATA_PLANE_PORT"])
        if err != nil {
                log.Fatalf("error loading KONG_DATA_PLANE_PORT from .env file: %s", err)
-               return nil, nil, err
+               return nil, err
        }
 
        myPorts["KONG_CONTROL_PLANE_PORT"], err = strconv.Atoi(myEnv["KONG_CONTROL_PLANE_PORT"])
        if err != nil {
                log.Fatalf("error loading KONG_CONTROL_PLANE_PORT from .env file: %s", err)
-               return nil, nil, err
+               return nil, err
        }
 
        myPorts["CAPIF_PORT"], err = strconv.Atoi(myEnv["CAPIF_PORT"])
        if err != nil {
                log.Fatalf("error loading CAPIF_PORT from .env file: %s", err)
-               return nil, nil, err
+               return nil, err
        }
 
        myPorts["SERVICE_MANAGER_PORT"], err = strconv.Atoi(myEnv["SERVICE_MANAGER_PORT"])
        if err != nil {
                log.Fatalf("error loading SERVICE_MANAGER_PORT from .env file: %s", err)
-               return nil, nil, err
+               return nil, err
        }
 
        // TEST_SERVICE_PORT is required for unit testing, but not required for production
@@ -114,9 +142,25 @@ func ReadDotEnv() (map[string]string, map[string]int, error) {
                myPorts["TEST_SERVICE_PORT"], err = strconv.Atoi(myEnv["TEST_SERVICE_PORT"])
                if err != nil {
                        log.Fatalf("error loading TEST_SERVICE_PORT from .env file: %s", err)
-                       return nil, nil, err
+                       return nil, err
                }
        }
 
-       return myEnv, myPorts, err
+       return myPorts, err
+}
+
+// MockConfigReader is a mock implementation for testing
+type MockConfigReader struct {
+    MockedConfig map[string]string
 }
+
+func (m *MockConfigReader) ReadDotEnv() (map[string]string, map[string]int, error) {
+       const envFile = "mock"
+
+       setLogLevel(m.MockedConfig["LOG_LEVEL"])
+       logConfig(m.MockedConfig, envFile)
+
+       // Return the mocked configuration for testing
+       myPorts, err := createMapPorts(m.MockedConfig)
+    return m.MockedConfig, myPorts, err
+}
\ No newline at end of file
index bca6c7f..0096c49 100644 (file)
@@ -22,6 +22,8 @@ package invokermanagement
 import (
        "fmt"
        "net/http"
+       "net/http/httptest"
+       "net/url"
        "os"
        "strings"
        "testing"
@@ -30,6 +32,7 @@ import (
        "oransc.org/nonrtric/servicemanager/internal/envreader"
        "oransc.org/nonrtric/servicemanager/internal/invokermanagementapi"
        "oransc.org/nonrtric/servicemanager/internal/kongclear"
+
        "oransc.org/nonrtric/servicemanager/internal/providermanagement"
        provapi "oransc.org/nonrtric/servicemanager/internal/providermanagementapi"
        "oransc.org/nonrtric/servicemanager/internal/publishservice"
@@ -41,9 +44,20 @@ import (
        echomiddleware "github.com/labstack/echo/v4/middleware"
        log "github.com/sirupsen/logrus"
        "github.com/stretchr/testify/assert"
+
+       "oransc.org/nonrtric/capifcore"
+       "oransc.org/nonrtric/servicemanager/mockkong"
 )
 
-var requestHandler *echo.Echo
+var (
+       eServiceManager          *echo.Echo
+       eCapifWeb                *echo.Echo
+       eKong                    *echo.Echo
+       mockConfigReader         *envreader.MockConfigReader
+       serviceManagerServer *httptest.Server
+       capifServer              *httptest.Server
+       mockKongServer           *httptest.Server
+)
 
 func TestMain(m *testing.M) {
        err := setupTest()
@@ -59,22 +73,69 @@ func TestMain(m *testing.M) {
 }
 
 func setupTest() error {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       // Start the mock Kong server
+       eKong = echo.New()
+       mockKong.RegisterHandlers(eKong)
+       mockKongServer = httptest.NewServer(eKong)
+
+       // Parse the server URL
+       parsedMockKongURL, err := url.Parse(mockKongServer.URL)
        if err != nil {
-               log.Fatal("error loading environment file on setupTest")
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       mockKongHost := parsedMockKongURL.Hostname()
+       mockKongControlPlanePort := parsedMockKongURL.Port()
+
+       eCapifWeb = echo.New()
+       capifcore.RegisterHandlers(eCapifWeb, nil, nil)
+       capifServer = httptest.NewServer(eCapifWeb)
+
+       // Parse the server URL
+       parsedCapifURL, err := url.Parse(capifServer.URL)
+       if err != nil {
+               log.Fatalf("error parsing mock Kong URL: %v", err)
                return err
        }
 
-       requestHandler, err = getEcho(myEnv, myPorts)
+       // Extract the host and port
+       capifHost := parsedCapifURL.Hostname()
+       capifPort := parsedCapifURL.Port()
+
+       // Set up the mock config reader with the desired configuration for testing
+       mockConfigReader = &envreader.MockConfigReader{
+               MockedConfig: map[string]string{
+                       "KONG_DOMAIN":             "kong",
+                       "KONG_PROTOCOL":           "http",
+                       "KONG_IPV4":               mockKongHost,
+                       "KONG_DATA_PLANE_PORT":    "32080",
+                       "KONG_CONTROL_PLANE_PORT": mockKongControlPlanePort,
+                       "CAPIF_PROTOCOL":          "http",
+                       "CAPIF_IPV4":              capifHost,
+                       "CAPIF_PORT":              capifPort,
+                       "LOG_LEVEL":               "Info",
+                       "SERVICE_MANAGER_PORT":    "8095",
+                       "TEST_SERVICE_IPV4":       "10.101.1.101",
+                       "TEST_SERVICE_PORT":       "30951",
+               },
+       }
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
-               log.Fatal("getEcho fatal error on setupTest")
+               log.Fatal("error loading environment file on setupTest")
                return err
        }
-       err = teardown()
+
+       eServiceManager = echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
        if err != nil {
-               log.Fatal("getEcho fatal error on teardown")
+               log.Fatal("registerHandlers fatal error on setupTest")
                return err
        }
+       serviceManagerServer = httptest.NewServer(eServiceManager)
+       capifCleanUp()
 
        return err
 }
@@ -109,16 +170,14 @@ func getProvider() provapi.APIProviderEnrolmentDetails {
        }
 }
 
-func teardown() error {
-       log.Trace("entering teardown")
-
-       t := new(testing.T) // Create a new testing.T instance for teardown
+func capifCleanUp()  {
+       t := new(testing.T) // Create a new testing.T instance for capifCleanUp
 
        // Delete the invoker
        invokerInfo := "invoker a"
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
-       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the original published service
@@ -126,7 +185,7 @@ func teardown() error {
        apiName := "apiName"
        apiId := "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the first published service
@@ -134,22 +193,26 @@ func teardown() error {
        apiName = "apiName1"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the second published service
        apiName = "apiName2"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the provider
        domainID := "domain_id_Kong"
-       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
+}
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+func teardown() error {
+       log.Trace("entering teardown")
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file")
                return err
@@ -159,16 +222,21 @@ func teardown() error {
        if err != nil {
                log.Fatal("error clearing Kong on teardown")
        }
-       return err
+
+       mockKongServer.Close()
+       capifServer.Close()
+       serviceManagerServer.Close()
+
+       return nil
 }
 
 func TestRegisterValidProvider(t *testing.T) {
-       teardown()
+       capifCleanUp()
 
        newProvider := getProvider()
 
        // Register a valid provider
-       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultProvider provapi.APIProviderEnrolmentDetails
@@ -181,7 +249,7 @@ func TestPublishUnpublishService(t *testing.T) {
        apiName := "apiName"
        newApiId := "api_id_" + apiName
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        assert.Nil(t, err, "error reading env file")
 
        testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
@@ -191,7 +259,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
 
        // Check no services published
-       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -212,7 +280,7 @@ func TestPublishUnpublishService(t *testing.T) {
        newServiceDescription := getServiceAPIDescription(aefId, apiName, description, testServiceIpv4, testServicePort)
 
        // Publish a service for provider
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        if result.Code() != http.StatusCreated {
@@ -228,7 +296,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.Equal(t, "http://example.com/published-apis/v1/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
 
        // Check that the service is published for the provider
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        err = result.UnmarshalJsonToObject(&resultService)
@@ -256,7 +324,7 @@ func TestOnboardInvoker(t *testing.T) {
        newInvoker := getInvoker(invokerInfo)
 
        // Onboard a valid invoker
-       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultInvoker invokermanagementapi.APIInvokerEnrolmentDetails
@@ -273,7 +341,7 @@ func TestOnboardInvoker(t *testing.T) {
        assert.Equal(t, "http://example.com/api-invoker-management/v1/onboardedInvokers/"+*resultInvoker.ApiInvokerId, result.Recorder.Header().Get(echo.HeaderLocation))
 
        // Onboarding the same invoker should result in Forbidden
-       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(newInvoker).Go(t, eServiceManager)
 
        assert.Equal(t, http.StatusForbidden, result.Code())
 
@@ -290,7 +358,7 @@ func TestOnboardInvoker(t *testing.T) {
                        ApiInvokerPublicKey: "newKey",
                },
        }
-       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invalidInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invalidInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
        err = result.UnmarshalBodyToObject(&problemDetails)
@@ -305,7 +373,7 @@ func TestOnboardInvoker(t *testing.T) {
                NotificationDestination: "http://golang.cafe/",
        }
 
-       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invalidInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invalidInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
        err = result.UnmarshalBodyToObject(&problemDetails)
@@ -321,7 +389,7 @@ func TestDeleteInvoker(t *testing.T) {
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
        // Delete the invoker
-       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 }
 
@@ -331,7 +399,7 @@ func TestUpdateInvoker(t *testing.T) {
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
        // Onboard a valid invoker
-       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invoker).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-invoker-management/v1/onboardedInvokers").WithJsonBody(invoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        // Update the invoker with valid invoker, should return 200 with updated invoker details
@@ -342,7 +410,7 @@ func TestUpdateInvoker(t *testing.T) {
 
        invoker.ApiInvokerId = &invokerId
 
-       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        var resultInvoker invokermanagementapi.APIInvokerEnrolmentDetails
@@ -362,7 +430,7 @@ func TestUpdateInvoker(t *testing.T) {
                ApiInvokerId:          &invokerId,
                OnboardingInformation: validOnboardingInfo,
        }
-       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
        var problemDetails common29122.ProblemDetails
@@ -376,7 +444,7 @@ func TestUpdateInvoker(t *testing.T) {
        // Update with an invoker missing required OnboardingInformation.ApiInvokerPublicKey, should get 400 with problem details
        invalidInvoker.NotificationDestination = "http://golang.org/"
        invalidInvoker.OnboardingInformation = invokermanagementapi.OnboardingInformation{}
-       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
        err = result.UnmarshalBodyToObject(&problemDetails)
@@ -390,7 +458,7 @@ func TestUpdateInvoker(t *testing.T) {
        invalidId := "1"
        invalidInvoker.ApiInvokerId = &invalidId
        invalidInvoker.OnboardingInformation = validOnboardingInfo
-       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+invokerId).WithJsonBody(invalidInvoker).Go(t, eServiceManager)
 
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
@@ -403,7 +471,7 @@ func TestUpdateInvoker(t *testing.T) {
        // Update an invoker that has not been onboarded, should get 404 with problem details
        missingId := "1"
        invoker.ApiInvokerId = &missingId
-       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+missingId).WithJsonBody(invoker).Go(t, requestHandler)
+       result = testutil.NewRequest().Put("/api-invoker-management/v1/onboardedInvokers/"+missingId).WithJsonBody(invoker).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNotFound, result.Code())
 
        err = result.UnmarshalBodyToObject(&problemDetails)
@@ -412,10 +480,10 @@ func TestUpdateInvoker(t *testing.T) {
        assert.Equal(t, http.StatusNotFound, *problemDetails.Status)
        assert.Contains(t, *problemDetails.Cause, "not been onboarded")
        assert.Contains(t, *problemDetails.Cause, "invoker")
-       teardown()
+       capifCleanUp()
 }
 
-func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
+func registerHandlers(e *echo.Echo, myEnv map[string]string, myPorts map[string]int) (err error) {
        capifProtocol := myEnv["CAPIF_PROTOCOL"]
        capifIPv4 := common29122.Ipv4Addr(myEnv["CAPIF_IPV4"])
        capifPort := common29122.Port(myPorts["CAPIF_PORT"])
@@ -425,13 +493,11 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
        kongControlPlanePort := common29122.Port(myPorts["KONG_CONTROL_PLANE_PORT"])
 
-       e := echo.New()
-
        // Register ProviderManagement
        providerManagerSwagger, err := provapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading ProviderManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        providerManagerSwagger.Servers = nil
        providerManager := providermanagement.NewProviderManager(capifProtocol, capifIPv4, capifPort)
@@ -445,7 +511,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        publishServiceSwagger, err := publishapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading PublishService swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        publishServiceSwagger.Servers = nil
@@ -460,7 +526,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        invokerServiceSwagger, err := invokermanagementapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading InvokerManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        invokerServiceSwagger.Servers = nil
@@ -472,7 +538,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        group.Use(middleware.OapiRequestValidator(invokerServiceSwagger))
        invokermanagementapi.RegisterHandlersWithBaseURL(e, im, "api-invoker-management/v1")
 
-       return e, err
+       return err
 }
 
 func getServiceAPIDescription(aefId, apiName, description string, testServiceIpv4 common29122.Ipv4Addr, testServicePort common29122.Port) publishapi.ServiceAPIDescription {
index c4bd2a2..da64c04 100644 (file)
@@ -28,7 +28,8 @@ import (
 )
 
 func main() {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       realConfigReader := &envreader.RealConfigReader{}
+    myEnv, myPorts, err := realConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file")
        }
index f00afd0..3d07f90 100644 (file)
@@ -21,8 +21,9 @@
 package providermanagement
 
 import (
-       "fmt"
        "net/http"
+       "net/http/httptest"
+       "net/url"
        "os"
        "strings"
        "testing"
@@ -32,19 +33,22 @@ import (
        "oransc.org/nonrtric/servicemanager/internal/common29122"
        "oransc.org/nonrtric/servicemanager/internal/envreader"
        "oransc.org/nonrtric/servicemanager/internal/kongclear"
+
        provapi "oransc.org/nonrtric/servicemanager/internal/providermanagementapi"
-       "oransc.org/nonrtric/servicemanager/internal/publishservice"
-       publishapi "oransc.org/nonrtric/servicemanager/internal/publishserviceapi"
 
        "github.com/deepmap/oapi-codegen/pkg/middleware"
        "github.com/deepmap/oapi-codegen/pkg/testutil"
        echomiddleware "github.com/labstack/echo/v4/middleware"
        log "github.com/sirupsen/logrus"
+
        "github.com/stretchr/testify/assert"
+
+       "oransc.org/nonrtric/capifcore"
+       "oransc.org/nonrtric/servicemanager/mockkong"
 )
 
 var (
-       domainID = "domain_id_Kong"
+       domainID    = "domain_id_Kong"
        domainInfo  = "Kong"
        funcInfoAPF = "rApp Kong as APF"
        funcIdAPF   = "APF_id_rApp_Kong_as_APF"
@@ -52,7 +56,15 @@ var (
        funcIdAEF   = "AEF_id_rApp_Kong_as_AEF"
 )
 
-var requestHandler *echo.Echo
+var (
+       eServiceManager          *echo.Echo
+       eCapifWeb                *echo.Echo
+       eKong                    *echo.Echo
+       mockConfigReader         *envreader.MockConfigReader
+       serviceManagerServer *httptest.Server
+       capifServer              *httptest.Server
+       mockKongServer           *httptest.Server
+)
 
 func TestMain(m *testing.M) {
        err := setupTest()
@@ -68,30 +80,81 @@ func TestMain(m *testing.M) {
 }
 
 func setupTest() error {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       // Start the mock Kong server
+       eKong = echo.New()
+       mockKong.RegisterHandlers(eKong)
+       mockKongServer = httptest.NewServer(eKong)
+
+       // Parse the server URL
+       parsedMockKongURL, err := url.Parse(mockKongServer.URL)
+       if err != nil {
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       mockKongHost := parsedMockKongURL.Hostname()
+       mockKongControlPlanePort := parsedMockKongURL.Port()
+
+       eCapifWeb = echo.New()
+       capifcore.RegisterHandlers(eCapifWeb, nil, nil)
+       capifServer = httptest.NewServer(eCapifWeb)
+
+       // Parse the server URL
+       parsedCapifURL, err := url.Parse(capifServer.URL)
+       if err != nil {
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       capifHost := parsedCapifURL.Hostname()
+       capifPort := parsedCapifURL.Port()
+
+       // Set up the mock config reader with the desired configuration for testing
+       mockConfigReader = &envreader.MockConfigReader{
+               MockedConfig: map[string]string{
+                       "KONG_DOMAIN":             "kong",
+                       "KONG_PROTOCOL":           "http",
+                       "KONG_IPV4":               mockKongHost,
+                       "KONG_DATA_PLANE_PORT":    "32080",
+                       "KONG_CONTROL_PLANE_PORT": mockKongControlPlanePort,
+                       "CAPIF_PROTOCOL":          "http",
+                       "CAPIF_IPV4":              capifHost,
+                       "CAPIF_PORT":              capifPort,
+                       "LOG_LEVEL":               "Info",
+                       "SERVICE_MANAGER_PORT":    "8095",
+                       "TEST_SERVICE_IPV4":       "10.101.1.101",
+                       "TEST_SERVICE_PORT":       "30951",
+               },
+       }
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file on setupTest")
                return err
        }
 
-       requestHandler, err = getEcho(myEnv, myPorts)
+       eServiceManager = echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
        if err != nil {
-               log.Fatal("getEcho fatal error on setupTest")
+               log.Fatal("registerHandlers fatal error on setupTest")
                return err
        }
+       serviceManagerServer = httptest.NewServer(eServiceManager)
+
        return err
 }
 
-func teardown() error {
-       log.Trace("entering teardown")
 
-       t := new(testing.T) // Create a new testing.T instance for teardown
+func capifCleanUp() {
+       t := new(testing.T) // Create a new testing.T instance for capifCleanUp
 
        // Delete the invoker
        invokerInfo := "invoker a"
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
-       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the original published service
@@ -99,7 +162,7 @@ func teardown() error {
        apiName := "apiName"
        apiId := "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the first published service
@@ -107,21 +170,26 @@ func teardown() error {
        apiName = "apiName1"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the second published service
        apiName = "apiName2"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the provider
-       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
+}
+
+
+func teardown() error {
+       log.Trace("entering teardown")
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file")
                return err
@@ -131,15 +199,20 @@ func teardown() error {
        if err != nil {
                log.Fatal("error clearing Kong on teardown")
        }
-       return err
+
+       mockKongServer.Close()
+       capifServer.Close()
+       serviceManagerServer.Close()
+
+       return nil
 }
 
 func TestRegisterValidProvider(t *testing.T) {
-       teardown()
+       capifCleanUp()
 
        // Register a valid provider
        newProvider := getProvider()
-       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultProvider provapi.APIProviderEnrolmentDetails
@@ -164,11 +237,11 @@ func TestRegisterValidProvider(t *testing.T) {
                assert.Equal(t, "http://example.com/api-provider-management/v1/registrations/"+*resultProvider.ApiProvDomId, result.Recorder.Header().Get(echo.HeaderLocation))
 
                // Register same provider again should result in Forbidden
-               result = testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
+               result = testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, eServiceManager)
 
-               var errorObj common29122.ProblemDetails
                assert.Equal(t, http.StatusForbidden, result.Code())
 
+               var errorObj common29122.ProblemDetails
                err = result.UnmarshalBodyToObject(&errorObj)
                assert.NoError(t, err, "error unmarshaling response")
                assert.Equal(t, http.StatusForbidden, *errorObj.Status)
@@ -196,7 +269,7 @@ func TestUpdateValidProviderWithNewFunction(t *testing.T) {
        })
        updatedProvider.ApiProvFuncs = &testFuncs
 
-       result := testutil.NewRequest().Put("/api-provider-management/v1/registrations/"+domainID).WithJsonBody(updatedProvider).Go(t, requestHandler)
+       result := testutil.NewRequest().Put("/api-provider-management/v1/registrations/"+domainID).WithJsonBody(updatedProvider).Go(t, eServiceManager)
 
        var resultProvider provapi.APIProviderEnrolmentDetails
        assert.Equal(t, http.StatusOK, result.Code())
@@ -214,9 +287,9 @@ func TestDeleteProvider(t *testing.T) {
        provider := getProvider()
        provider.ApiProvDomId = &domainID
        (*provider.ApiProvFuncs)[0].ApiProvFuncId = &funcIdAPF
-       result := testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
-       teardown()
+       capifCleanUp()
 }
 
 func getProvider() provapi.APIProviderEnrolmentDetails {
@@ -243,48 +316,24 @@ func getProvider() provapi.APIProviderEnrolmentDetails {
        }
 }
 
-func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
+func registerHandlers(e *echo.Echo, myEnv map[string]string, myPorts map[string]int) (err error) {
        capifProtocol := myEnv["CAPIF_PROTOCOL"]
        capifIPv4 := common29122.Ipv4Addr(myEnv["CAPIF_IPV4"])
        capifPort := common29122.Port(myPorts["CAPIF_PORT"])
-       kongDomain := myEnv["KONG_DOMAIN"]
-       kongProtocol := myEnv["KONG_PROTOCOL"]
-       kongIPv4 := common29122.Ipv4Addr(myEnv["KONG_IPV4"])
-       kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
-       kongControlPlanePort := common29122.Port(myPorts["KONG_CONTROL_PLANE_PORT"])
-
-       e := echo.New()
 
        // Register ProviderManagement
        providerManagerSwagger, err := provapi.GetSwagger()
        if err != nil {
                log.Fatalf("Error loading ProviderManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        providerManagerSwagger.Servers = nil
        providerManager := NewProviderManager(capifProtocol, capifIPv4, capifPort)
 
-       var group *echo.Group
-
-       group = e.Group("/api-provider-management/v1")
+       group := e.Group("/api-provider-management/v1")
+       group.Use(echomiddleware.Logger())
        group.Use(middleware.OapiRequestValidator(providerManagerSwagger))
        provapi.RegisterHandlersWithBaseURL(e, providerManager, "/api-provider-management/v1")
 
-       publishServiceSwagger, err := publishapi.GetSwagger()
-       if err != nil {
-               fmt.Fprintf(os.Stderr, "Error loading PublishService swagger spec\n: %s", err)
-               return nil, err
-       }
-
-       publishServiceSwagger.Servers = nil
-
-       ps := publishservice.NewPublishService(kongDomain, kongProtocol, kongIPv4, kongDataPlanePort, kongControlPlanePort, capifProtocol, capifIPv4, capifPort)
-
-       group = e.Group("/published-apis/v1")
-       group.Use(echomiddleware.Logger())
-       group.Use(middleware.OapiRequestValidator(publishServiceSwagger))
-       publishapi.RegisterHandlersWithBaseURL(e, ps, "/published-apis/v1")
-
-       // return ps, eventChannel, e
-       return e, err
+       return err
 }
index bd5264d..7bec335 100644 (file)
@@ -23,6 +23,8 @@ package publishservice
 import (
        "fmt"
        "net/http"
+       "net/http/httptest"
+       "net/url"
        "os"
        "strings"
        "testing"
@@ -34,15 +36,27 @@ import (
        log "github.com/sirupsen/logrus"
        "github.com/stretchr/testify/assert"
 
-       "oransc.org/nonrtric/servicemanager/internal/common29122"
        "oransc.org/nonrtric/servicemanager/internal/envreader"
        "oransc.org/nonrtric/servicemanager/internal/kongclear"
+
+       "oransc.org/nonrtric/servicemanager/internal/common29122"
        "oransc.org/nonrtric/servicemanager/internal/providermanagement"
        provapi "oransc.org/nonrtric/servicemanager/internal/providermanagementapi"
        publishapi "oransc.org/nonrtric/servicemanager/internal/publishserviceapi"
+
+       "oransc.org/nonrtric/capifcore"
+       "oransc.org/nonrtric/servicemanager/mockkong"
 )
 
-var requestHandler *echo.Echo
+var (
+       eServiceManager          *echo.Echo
+       eCapifWeb                *echo.Echo
+       eKong                    *echo.Echo
+       mockConfigReader         *envreader.MockConfigReader
+       serviceManagerServer *httptest.Server
+       capifServer              *httptest.Server
+       mockKongServer           *httptest.Server
+)
 
 func TestMain(m *testing.M) {
        err := setupTest()
@@ -58,23 +72,69 @@ func TestMain(m *testing.M) {
 }
 
 func setupTest() error {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       // Start the mock Kong server
+       eKong = echo.New()
+       mockKong.RegisterHandlers(eKong)
+       mockKongServer = httptest.NewServer(eKong)
+
+       // Parse the server URL
+       parsedMockKongURL, err := url.Parse(mockKongServer.URL)
        if err != nil {
-               log.Fatal("error loading environment file on setupTest")
+               log.Fatalf("error parsing mock Kong URL: %v", err)
                return err
        }
 
-       requestHandler, err = getEcho(myEnv, myPorts)
+       // Extract the host and port
+       mockKongHost := parsedMockKongURL.Hostname()
+       mockKongControlPlanePort := parsedMockKongURL.Port()
+
+       eCapifWeb = echo.New()
+       capifcore.RegisterHandlers(eCapifWeb, nil, nil)
+       capifServer = httptest.NewServer(eCapifWeb)
+
+       // Parse the server URL
+       parsedCapifURL, err := url.Parse(capifServer.URL)
        if err != nil {
-               log.Fatal("getEcho fatal error on setupTest")
+               log.Fatalf("error parsing mock Kong URL: %v", err)
                return err
        }
-       err = teardown()
+
+       // Extract the host and port
+       capifHost := parsedCapifURL.Hostname()
+       capifPort := parsedCapifURL.Port()
+
+       // Set up the mock config reader with the desired configuration for testing
+       mockConfigReader = &envreader.MockConfigReader{
+               MockedConfig: map[string]string{
+                       "KONG_DOMAIN":             "kong",
+                       "KONG_PROTOCOL":           "http",
+                       "KONG_IPV4":               mockKongHost,
+                       "KONG_DATA_PLANE_PORT":    "32080",
+                       "KONG_CONTROL_PLANE_PORT": mockKongControlPlanePort,
+                       "CAPIF_PROTOCOL":          "http",
+                       "CAPIF_IPV4":              capifHost,
+                       "CAPIF_PORT":              capifPort,
+                       "LOG_LEVEL":               "Info",
+                       "SERVICE_MANAGER_PORT":    "8095",
+                       "TEST_SERVICE_IPV4":       "10.101.1.101",
+                       "TEST_SERVICE_PORT":       "30951",
+               },
+       }
+
+       // Use the mock implementation for testing
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
-               log.Fatal("getEcho fatal error on teardown")
-               return err
+               log.Fatalf("error reading mock config: %v", err)
        }
 
+       eServiceManager = echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
+       if err != nil {
+               log.Fatal("registerHandlers fatal error on setupTest")
+               return err
+       }
+       serviceManagerServer = httptest.NewServer(eServiceManager)
+       capifCleanUp()
        return err
 }
 
@@ -108,16 +168,14 @@ func getProvider() provapi.APIProviderEnrolmentDetails {
        }
 }
 
-func teardown() error {
-       log.Trace("entering teardown")
-
-       t := new(testing.T) // Create a new testing.T instance for teardown
+func capifCleanUp()  {
+       t := new(testing.T) // Create a new testing.T instance for capifCleanUp
 
        // Delete the invoker
        invokerInfo := "invoker a"
        invokerId := "api_invoker_id_" + strings.Replace(invokerInfo, " ", "_", 1)
 
-       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, requestHandler)
+       result := testutil.NewRequest().Delete("/api-invoker-management/v1/onboardedInvokers/"+invokerId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the original published service
@@ -125,7 +183,7 @@ func teardown() error {
        apiName := "apiName"
        apiId := "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the first published service
@@ -133,22 +191,26 @@ func teardown() error {
        apiName = "apiName1"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the second published service
        apiName = "apiName2"
        apiId = "api_id_" + apiName
 
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+apiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Delete the provider
        domainID := "domain_id_Kong"
-       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/api-provider-management/v1/registrations/"+domainID).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
+}
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+func teardown() error {
+       log.Trace("entering teardown")
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        if err != nil {
                log.Fatal("error loading environment file")
                return err
@@ -158,17 +220,21 @@ func teardown() error {
        if err != nil {
                log.Fatal("error clearing Kong on teardown")
        }
-       return err
+
+       mockKongServer.Close()
+       capifServer.Close()
+       serviceManagerServer.Close()
+
+       return nil
 }
 
 func TestPostUnpublishedServiceWithUnregisteredPublisher(t *testing.T) {
-       teardown()
+       capifCleanUp()
 
        apfId := "APF_id_rApp_Kong_as_APF"
-       apiName := "apiName"
 
        // Check no services published
-       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusNotFound, result.Code())
 
        var resultError common29122.ProblemDetails
@@ -185,7 +251,7 @@ func TestPostUnpublishedServiceWithUnregisteredPublisher(t *testing.T) {
        releaseName := "releaseName"
        description := fmt.Sprintf("Description,%s,%s,%s,%s", namespace, repoName, chartName, releaseName)
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        assert.Nil(t, err, "error reading env file")
 
        testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
@@ -194,10 +260,11 @@ func TestPostUnpublishedServiceWithUnregisteredPublisher(t *testing.T) {
        assert.NotEmpty(t, testServiceIpv4, "TEST_SERVICE_IPV4 is required in .env file for unit testing")
        assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
 
+       apiName := "apiName"
        newServiceDescription := getServiceAPIDescription(aefId, apiName, description, testServiceIpv4, testServicePort)
 
        // Attempt to publish a service for provider
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusForbidden, result.Code())
 
        // var resultError common29122.ProblemDetails
@@ -212,7 +279,7 @@ func TestRegisterValidProvider(t *testing.T) {
        newProvider := getProvider()
 
        // Register a valid provider
-       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, requestHandler)
+       result := testutil.NewRequest().Post("/api-provider-management/v1/registrations").WithJsonBody(newProvider).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        var resultProvider provapi.APIProviderEnrolmentDetails
@@ -223,9 +290,8 @@ func TestRegisterValidProvider(t *testing.T) {
 func TestPublishUnpublishService(t *testing.T) {
        apfId := "APF_id_rApp_Kong_as_APF"
        apiName := "apiName"
-       newApiId := "api_id_" + apiName
 
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
        assert.Nil(t, err, "error reading env file")
 
        testServiceIpv4 := common29122.Ipv4Addr(myEnv["TEST_SERVICE_IPV4"])
@@ -235,7 +301,7 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.NotZero(t, testServicePort, "TEST_SERVICE_PORT is required in .env file for unit testing")
 
        // Check no services published
-       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result := testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -256,7 +322,7 @@ func TestPublishUnpublishService(t *testing.T) {
        newServiceDescription := getServiceAPIDescriptionMissingInterface(aefId, apiName, description)
 
        // Publish a service for provider
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
 
        var resultError common29122.ProblemDetails
@@ -268,7 +334,7 @@ func TestPublishUnpublishService(t *testing.T) {
        newServiceDescription = getServiceAPIDescription(aefId, apiName, description, testServiceIpv4, testServicePort)
 
        // Publish a service for provider
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusCreated, result.Code())
 
        if result.Code() != http.StatusCreated {
@@ -279,12 +345,13 @@ func TestPublishUnpublishService(t *testing.T) {
        var resultService publishapi.ServiceAPIDescription
        err = result.UnmarshalJsonToObject(&resultService)
        assert.NoError(t, err, "error unmarshaling response")
+       newApiId := "api_id_" + apiName
        assert.Equal(t, newApiId, *resultService.ApiId)
 
        assert.Equal(t, "http://example.com/published-apis/v1/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation))
 
        // Check that the service is published for the provider
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        err = result.UnmarshalJsonToObject(&resultService)
@@ -308,7 +375,7 @@ func TestPublishUnpublishService(t *testing.T) {
 
        // Publish the same service again should result in Forbidden
        newServiceDescription.ApiId = &newApiId
-       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
+       result = testutil.NewRequest().Post("/published-apis/v1/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, eServiceManager)
        assert.Equal(t, http.StatusForbidden, result.Code())
 
        err = result.UnmarshalBodyToObject(&resultError)
@@ -317,11 +384,11 @@ func TestPublishUnpublishService(t *testing.T) {
        assert.Equal(t, http.StatusForbidden, *resultError.Status)
 
        // Delete the service
-       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, requestHandler)
+       result = testutil.NewRequest().Delete("/published-apis/v1/"+apfId+"/service-apis/"+newApiId).Go(t, eServiceManager)
        assert.Equal(t, http.StatusNoContent, result.Code())
 
        // Check no services published
-       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, requestHandler)
+       result = testutil.NewRequest().Get("/published-apis/v1/"+apfId+"/service-apis").Go(t, eServiceManager)
        assert.Equal(t, http.StatusOK, result.Code())
 
        // Parse JSON from the response body
@@ -330,10 +397,10 @@ func TestPublishUnpublishService(t *testing.T) {
 
        // Check if the parsed array is empty
        assert.Zero(t, len(resultServices))
-       teardown()
+       capifCleanUp()
 }
 
-func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
+func registerHandlers(e *echo.Echo, myEnv map[string]string, myPorts map[string]int) (err error) {
        capifProtocol := myEnv["CAPIF_PROTOCOL"]
        capifIPv4 := common29122.Ipv4Addr(myEnv["CAPIF_IPV4"])
        capifPort := common29122.Port(myPorts["CAPIF_PORT"])
@@ -343,13 +410,11 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        kongDataPlanePort := common29122.Port(myPorts["KONG_DATA_PLANE_PORT"])
        kongControlPlanePort := common29122.Port(myPorts["KONG_CONTROL_PLANE_PORT"])
 
-       e := echo.New()
-
        // Register ProviderManagement
        providerManagerSwagger, err := provapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading ProviderManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        providerManagerSwagger.Servers = nil
        providerManager := providermanagement.NewProviderManager(capifProtocol, capifIPv4, capifPort)
@@ -363,7 +428,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        publishServiceSwagger, err := publishapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading PublishService swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        publishServiceSwagger.Servers = nil
@@ -375,8 +440,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        group.Use(middleware.OapiRequestValidator(publishServiceSwagger))
        publishapi.RegisterHandlersWithBaseURL(e, ps, "/published-apis/v1")
 
-       // return ps, eventChannel, e
-       return e, err
+       return err
 }
 
 func getServiceAPIDescription(aefId, apiName, description string, testServiceIpv4 common29122.Ipv4Addr, testServicePort common29122.Port) publishapi.ServiceAPIDescription {
index 451f54b..e8a1f9c 100644 (file)
@@ -145,7 +145,8 @@ func (sd *ServiceAPIDescription) createKongRoute(
        if resp.StatusCode() == http.StatusCreated {
                log.Infof("kong route %s created successfully", routeName)
        } else {
-               err = fmt.Errorf("the Kong service already exists. Status code: %d", resp.StatusCode())
+               log.Debugf("kongRoutesURL %s", kongRoutesURL)
+               err = fmt.Errorf("error creating Kong route. Status code: %d", resp.StatusCode())
                log.Error(err.Error())
                log.Errorf("response body: %s", resp.Body())
                return resp.StatusCode(), err
@@ -237,7 +238,7 @@ func (sd *ServiceAPIDescription) createKongService(kongControlPlaneURL string, k
        } else if resp.StatusCode() == http.StatusConflict {
                log.Errorf("kong service already exists. Status code: %d", resp.StatusCode())
                err = fmt.Errorf("service with identical apiName is already published") // for compatibilty with Capif error message on a duplicate service
-               statusCode = http.StatusForbidden // for compatibilty with the spec, TS29222_CAPIF_Publish_Service_API
+               statusCode = http.StatusForbidden                                       // for compatibilty with the spec, TS29222_CAPIF_Publish_Service_API
        } else {
                err = fmt.Errorf("error creating Kong service. Status code: %d", resp.StatusCode())
        }
@@ -350,6 +351,7 @@ func (sd *ServiceAPIDescription) deleteKongRoute(kongControlPlaneURL string, cli
        if resp.StatusCode() == http.StatusNoContent {
                log.Infof("kong route %s deleted successfully", routeName)
        } else {
+               log.Debugf("kongRoutesURL: %s", kongRoutesURL)
                log.Errorf("error deleting Kong route. Status code: %d", resp.StatusCode())
                log.Errorf("response body: %s", resp.Body())
                return resp.StatusCode(), err
@@ -386,6 +388,7 @@ func (sd *ServiceAPIDescription) deleteKongService(kongControlPlaneURL string, s
        if resp.StatusCode() == http.StatusNoContent {
                log.Infof("kong service %s deleted successfully", serviceName)
        } else {
+               log.Debugf("kongServicesURL: %s", kongServicesURL)
                log.Errorf("deleting Kong service, status code: %d", resp.StatusCode())
                log.Errorf("response body: %s", resp.Body())
        }
index 5d0cc55..a791314 100644 (file)
@@ -46,27 +46,29 @@ import (
 )
 
 func main() {
-       myEnv, myPorts, err := envreader.ReadDotEnv()
-       if err != nil {
+    realConfigReader := &envreader.RealConfigReader{}
+    myEnv, myPorts, err := realConfigReader.ReadDotEnv()
+    if err != nil {
                log.Fatal("error loading environment file")
-               return
-       }
+        return
+    }
+
+       eServiceManager := echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
 
-       e, err := getEcho(myEnv, myPorts)
        if err != nil {
-               log.Fatal("getEcho fatal error")
+               log.Fatal("registerHandlers fatal error")
                return
        }
 
        port := myPorts["SERVICE_MANAGER_PORT"]
 
-       go startWebServer(e, port)
+       go startWebServer(eServiceManager, port)
        log.Info("server started and listening on port: ", port)
        keepServerAlive()
 }
 
-func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error) {
-       e := echo.New()
+func registerHandlers(e *echo.Echo, myEnv map[string]string, myPorts map[string]int) (err error) {
        // Log all requests
        e.Use(echomiddleware.Logger())
 
@@ -85,7 +87,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        providerManagerSwagger, err := providermanagementapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading ProviderManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        providerManagerSwagger.Servers = nil
        providerManager := providermanagement.NewProviderManager(capifProtocol, capifIPv4, capifPort)
@@ -97,7 +99,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        publishServiceSwagger, err := publishserviceapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading PublishService swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        publishServiceSwagger.Servers = nil
        publishService := publishservice.NewPublishService(kongDomain, kongProtocol, kongIPv4, kongDataPlanePort, kongControlPlanePort, capifProtocol, capifIPv4, capifPort)
@@ -110,7 +112,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        invokerManagerSwagger, err := invokermanagementapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading InvokerManagement swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
        invokerManagerSwagger.Servers = nil
        invokerManager := invokermanagement.NewInvokerManager(capifProtocol, capifIPv4, capifPort)
@@ -122,7 +124,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        discoverServiceSwagger, err := discoverserviceapi.GetSwagger()
        if err != nil {
                log.Fatalf("error loading DiscoverService swagger spec\n: %s", err)
-               return nil, err
+               return err
        }
 
        discoverServiceSwagger.Servers = nil
@@ -135,7 +137,7 @@ func getEcho(myEnv map[string]string, myPorts map[string]int) (*echo.Echo, error
        e.GET("/", hello)
        e.GET("/swagger/:apiName", getSwagger)
 
-       return e, err
+       return err
 }
 
 func startWebServer(e *echo.Echo, port int) {
index 0f6e4cd..20353bd 100644 (file)
@@ -22,6 +22,8 @@ package main
 
 import (
        "net/http"
+       "net/http/httptest"
+       "net/url"
        "os"
        "testing"
 
@@ -33,32 +35,123 @@ import (
 
        "oransc.org/nonrtric/servicemanager/internal/common29122"
        "oransc.org/nonrtric/servicemanager/internal/envreader"
+       "oransc.org/nonrtric/servicemanager/internal/kongclear"
+
+       "oransc.org/nonrtric/capifcore"
+       "oransc.org/nonrtric/servicemanager/mockkong"
 )
 
-var e *echo.Echo
-var myPorts map [string]int
+var (
+       eServiceManager          *echo.Echo
+       eCapifWeb                *echo.Echo
+       eKong                    *echo.Echo
+       mockConfigReader         *envreader.MockConfigReader
+       serviceManagerServer *httptest.Server
+       capifServer              *httptest.Server
+       mockKongServer           *httptest.Server
+)
 
+// Init code to run before tests
 func TestMain(m *testing.M) {
-    // Init code to run before tests
-       myEnv, myPorts, err := envreader.ReadDotEnv()
+       err := setupTest()
        if err != nil {
-               log.Fatal("error loading environment file")
                return
        }
 
-       e, err = getEcho(myEnv, myPorts)
+       ret := m.Run()
+       if ret == 0 {
+               teardown()
+       }
+       os.Exit(ret)
+}
+
+func setupTest() error {
+       // Start the mock Kong server
+       eKong = echo.New()
+       mockKong.RegisterHandlers(eKong)
+       mockKongServer = httptest.NewServer(eKong)
+
+       // Parse the server URL
+       parsedMockKongURL, err := url.Parse(mockKongServer.URL)
        if err != nil {
-               log.Fatal("getEcho fatal error")
-               return
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       mockKongHost := parsedMockKongURL.Hostname()
+       mockKongControlPlanePort := parsedMockKongURL.Port()
+
+       eCapifWeb = echo.New()
+       capifcore.RegisterHandlers(eCapifWeb, nil, nil)
+       capifServer = httptest.NewServer(eCapifWeb)
+
+       // Parse the server URL
+       parsedCapifURL, err := url.Parse(capifServer.URL)
+       if err != nil {
+               log.Fatalf("error parsing mock Kong URL: %v", err)
+               return err
+       }
+
+       // Extract the host and port
+       capifHost := parsedCapifURL.Hostname()
+       capifPort := parsedCapifURL.Port()
+
+       // Set up the mock config reader with the desired configuration for testing
+       mockConfigReader = &envreader.MockConfigReader{
+               MockedConfig: map[string]string{
+                       "KONG_DOMAIN":             "kong",
+                       "KONG_PROTOCOL":           "http",
+                       "KONG_IPV4":               mockKongHost,
+                       "KONG_DATA_PLANE_PORT":    "32080",
+                       "KONG_CONTROL_PLANE_PORT": mockKongControlPlanePort,
+                       "CAPIF_PROTOCOL":          "http",
+                       "CAPIF_IPV4":              capifHost,
+                       "CAPIF_PORT":              capifPort,
+                       "LOG_LEVEL":               "Info",
+                       "SERVICE_MANAGER_PORT":    "8095",
+                       "TEST_SERVICE_IPV4":       "10.101.1.101",
+                       "TEST_SERVICE_PORT":       "30951",
+               },
+       }
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
+       if err != nil {
+               log.Fatal("error loading environment file on setupTest")
+               return err
+       }
+
+       eServiceManager = echo.New()
+       err = registerHandlers(eServiceManager, myEnv, myPorts)
+       if err != nil {
+               log.Fatal("registerHandlers fatal error on setupTest")
+               return err
+       }
+       serviceManagerServer = httptest.NewServer(eServiceManager)
+
+       return err
+}
+
+
+func teardown() error {
+       log.Trace("entering teardown")
+
+       myEnv, myPorts, err := mockConfigReader.ReadDotEnv()
+       if err != nil {
+               log.Fatal("error loading environment file")
+               return err
        }
-    
-    // Run tests
-    exitVal := m.Run()
-    
-    // Finalization code to run after tests
-
-    // Exit with exit value from tests
-    os.Exit(exitVal)
+
+       err = kongclear.KongClear(myEnv, myPorts)
+       if err != nil {
+               log.Fatal("error clearing Kong on teardown")
+       }
+
+       mockKongServer.Close()
+       capifServer.Close()
+       serviceManagerServer.Close()
+
+       return err
 }
 
 
@@ -117,9 +210,9 @@ func Test_routing(t *testing.T) {
                t.Run(tt.name, func(t *testing.T) {
                        var result *testutil.CompletedRequest
                        if tt.args.method == "GET" {
-                               result = testutil.NewRequest().Get(tt.args.url).Go(t, e)
+                               result = testutil.NewRequest().Get(tt.args.url).Go(t, eServiceManager)
                        } else if tt.args.method == "DELETE" {
-                               result = testutil.NewRequest().Delete(tt.args.url).Go(t, e)
+                               result = testutil.NewRequest().Delete(tt.args.url).Go(t, eServiceManager)
                        }
 
                        assert.Equal(t, tt.args.returnStatus, result.Code(), tt.name)
@@ -167,7 +260,7 @@ func TestGetSwagger(t *testing.T) {
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       result := testutil.NewRequest().Get("/swagger/"+tt.args.apiPath).Go(t, e)
+                       result := testutil.NewRequest().Get("/swagger/"+tt.args.apiPath).Go(t, eServiceManager)
                        assert.Equal(t, http.StatusOK, result.Code())
                        var swaggerResponse openapi3.T
                        err := result.UnmarshalJsonToObject(&swaggerResponse)
@@ -176,7 +269,7 @@ func TestGetSwagger(t *testing.T) {
                })
        }
        invalidApi := "foobar"
-       result := testutil.NewRequest().Get("/swagger/"+invalidApi).Go(t, e)
+       result := testutil.NewRequest().Get("/swagger/"+invalidApi).Go(t, eServiceManager)
        assert.Equal(t, http.StatusBadRequest, result.Code())
        var errorResponse common29122.ProblemDetails
        err := result.UnmarshalJsonToObject(&errorResponse)
diff --git a/servicemanager/mockkong/kong_mock.go b/servicemanager/mockkong/kong_mock.go
new file mode 100644 (file)
index 0000000..14acd57
--- /dev/null
@@ -0,0 +1,135 @@
+// -
+//   ========================LICENSE_START=================================
+//   O-RAN-SC
+//   %%
+//   Copyright (C) 2024: OpenInfra Foundation Europe
+//   %%
+//   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.
+//   ========================LICENSE_END===================================
+//
+
+package mockKong
+
+import (
+       "io"
+       "net/http"
+
+       echo "github.com/labstack/echo/v4"
+)
+
+func RegisterHandlers(e *echo.Echo) {
+       // Handle Kong service and route endpoint mock responses here
+       e.POST("/services", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/services/api_id_apiName_helloworld/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/services/api_id_apiName1_helloworld/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/services/api_id_apiName2_helloworld/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/services/api_id_apiName1_app/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/services/api_id_apiName2_app/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.POST("/routes", func(c echo.Context) error {
+               body, err := io.ReadAll(c.Request().Body)
+               if err != nil {
+                       return c.String(http.StatusInternalServerError, "Error reading request body")
+               }
+               return c.String(http.StatusCreated, string(body))
+       })
+
+       e.GET("/services", func(c echo.Context) error {
+               return c.String(http.StatusOK, "{}")
+       })
+
+       e.GET("/routes", func(c echo.Context) error {
+               return c.String(http.StatusOK, "{}")
+       })
+
+       e.DELETE("/routes/api_id_apiName_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/services/api_id_apiName_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/routes/api_id_apiName1_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/routes/api_id_apiName2_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/routes/api_id_apiName1_app", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/routes/api_id_apiName2_app", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/services/api_id_apiName1_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/services/api_id_apiName2_helloworld", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/services/api_id_apiName1_app", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+
+       e.DELETE("/services/api_id_apiName2_app", func(c echo.Context) error {
+               return c.NoContent(http.StatusNoContent)
+       })
+}
\ No newline at end of file