console: implement an HTTP cache for Clickhouse-backed requests

Fix #328
This commit is contained in:
Vincent Bernat
2022-12-22 15:40:22 +01:00
parent fe4295614e
commit 775dc08db2
18 changed files with 550 additions and 86 deletions

View File

@@ -51,6 +51,12 @@ jobs:
ALLOW_PLAINTEXT_LISTENER: "yes"
ports:
- 9092:9093
redis:
image: bitnami/redis:7.0
env:
ALLOW_EMPTY_PASSWORD: "yes"
ports:
- 6379:6379
clickhouse:
image: clickhouse/clickhouse-server:22.8
ports:

View File

@@ -22,17 +22,23 @@ run tests:
variables:
FF_NETWORK_PER_BUILD: "true"
CI_AKVORADO_FUNCTIONAL_TESTS: "true"
# zookeeper
ALLOW_ANONYMOUS_LOGIN: "yes"
# kafka
ALLOW_PLAINTEXT_LISTENER: "yes"
KAFKA_BROKER_ID: "1"
KAFKA_CFG_LISTENERS: "PLAINTEXT://:9092"
KAFKA_CFG_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:9092"
KAFKA_CFG_ZOOKEEPER_CONNECT: "zookeeper:2181"
# redis
ALLOW_EMPTY_PASSWORD: "yes"
services:
- name: bitnami/zookeeper:3.7
alias: zookeeper
- name: bitnami/kafka:3.3
alias: kafka
- name: bitnami/redis:7.0
alias: redis
- name: clickhouse/clickhouse-server:22.8
alias: clickhouse
script:

66
common/http/cache.go Normal file
View File

@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
package http
import (
"bytes"
"hash/fnv"
"io/ioutil"
"time"
cache "github.com/chenyahui/gin-cache"
"github.com/gin-gonic/gin"
"akvorado/common/reporter"
)
// CacheByRequestPath is a middleware to cache the request using path as key
func (c *Component) CacheByRequestPath(expire time.Duration) gin.HandlerFunc {
opts := c.commonCacheOptions()
opts = append(opts, cache.WithCacheStrategyByRequest(func(gc *gin.Context) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: gc.Request.URL.Path,
}
}))
return cache.Cache(c.cacheStore, expire, opts...)
}
// CacheByRequestBody is a middleware to cache the request using body as key
func (c *Component) CacheByRequestBody(expire time.Duration) gin.HandlerFunc {
opts := c.commonCacheOptions()
opts = append(opts, cache.WithCacheStrategyByRequest(func(gc *gin.Context) (bool, cache.Strategy) {
requestBody, err := gc.GetRawData()
gc.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
if err != nil {
return false, cache.Strategy{}
}
h := fnv.New128()
bodyHash := string(h.Sum(requestBody))
return true, cache.Strategy{
CacheKey: bodyHash,
}
}))
return cache.Cache(c.cacheStore, expire, opts...)
}
func (c *Component) commonCacheOptions() []cache.Option {
return []cache.Option{
cache.WithLogger(cacheLogger{c.r}),
cache.WithOnHitCache(func(gc *gin.Context) {
c.metrics.cacheHit.WithLabelValues(gc.Request.URL.Path, gc.Request.Method).Inc()
}),
cache.WithOnMissCache(func(gc *gin.Context) {
c.metrics.cacheMiss.WithLabelValues(gc.Request.URL.Path, gc.Request.Method).Inc()
}),
cache.WithPrefixKey("cache-"),
}
}
type cacheLogger struct {
r *reporter.Reporter
}
func (cl cacheLogger) Errorf(msg string, args ...interface{}) {
cl.r.Error().Msgf(msg, args...)
}

174
common/http/cache_test.go Normal file
View File

@@ -0,0 +1,174 @@
// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
package http_test
import (
"context"
netHTTP "net/http"
"testing"
"time"
"akvorado/common/daemon"
"akvorado/common/helpers"
"akvorado/common/http"
"akvorado/common/reporter"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
)
func TestCacheByRequestPath(t *testing.T) {
r := reporter.NewMock(t)
h := http.NewMock(t, r)
count := 0
h.GinRouter.GET("/api/v0/test",
h.CacheByRequestPath(time.Minute),
func(c *gin.Context) {
count++
c.JSON(netHTTP.StatusOK, gin.H{
"message": "ping",
"count": count,
})
})
// Check the HTTP server is running and answering metrics
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
{
Description: "not cached",
URL: "/api/v0/test",
JSONOutput: gin.H{"message": "ping", "count": 1},
}, {
Description: "cached",
URL: "/api/v0/test",
JSONOutput: gin.H{"message": "ping", "count": 1},
}, {
Description: "cached twice",
URL: "/api/v0/test",
JSONOutput: gin.H{"message": "ping", "count": 1},
},
})
gotMetrics := r.GetMetrics("akvorado_common_http_", "requests_", "cache_")
expectedMetrics := map[string]string{
`cache_hit_total{method="GET",path="/api/v0/test"}`: "2",
`cache_miss_total{method="GET",path="/api/v0/test"}`: "1",
`requests_total{code="200",handler="/api/",method="get"}`: "3",
}
if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" {
t.Fatalf("Metrics (-got, +want):\n%s", diff)
}
}
func TestCacheByRequestBody(t *testing.T) {
r := reporter.NewMock(t)
h := http.NewMock(t, r)
count := 0
h.GinRouter.POST("/api/v0/test",
h.CacheByRequestBody(time.Minute),
func(c *gin.Context) {
count++
data, err := c.GetRawData()
if err != nil {
t.Fatalf("GetRawData() error:\n%+v", err)
}
c.JSON(netHTTP.StatusOK, gin.H{
"message": "ping",
"count": count,
"body": string(data),
})
})
// Check the HTTP server is running and answering metrics
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
{
Description: "not cached",
URL: "/api/v0/test",
JSONInput: gin.H{"hop": 1},
JSONOutput: gin.H{"message": "ping", "count": 1, "body": `{"hop":1}` + "\n"},
}, {
Description: "cached",
URL: "/api/v0/test",
JSONInput: gin.H{"hop": 1},
JSONOutput: gin.H{"message": "ping", "count": 1, "body": `{"hop":1}` + "\n"},
}, {
Description: "different body",
URL: "/api/v0/test",
JSONInput: gin.H{"hop": 2},
JSONOutput: gin.H{"message": "ping", "count": 2, "body": `{"hop":2}` + "\n"},
}, {
Description: "different body cached",
URL: "/api/v0/test",
JSONInput: gin.H{"hop": 2},
JSONOutput: gin.H{"message": "ping", "count": 2, "body": `{"hop":2}` + "\n"},
},
})
gotMetrics := r.GetMetrics("akvorado_common_http_", "requests_", "cache_")
expectedMetrics := map[string]string{
`cache_hit_total{method="POST",path="/api/v0/test"}`: "2",
`cache_miss_total{method="POST",path="/api/v0/test"}`: "2",
`requests_total{code="200",handler="/api/",method="post"}`: "4",
}
if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" {
t.Fatalf("Metrics (-got, +want):\n%s", diff)
}
}
func TestRedis(t *testing.T) {
server := helpers.CheckExternalService(t, "Redis", []string{"redis", "localhost"}, "6379")
client := redis.NewClient(&redis.Options{
Addr: server,
DB: 10,
})
defer client.Close()
if err := client.FlushAll(context.Background()).Err(); err != nil {
t.Fatalf("FlushAll() error:\n%+v", err)
}
r := reporter.NewMock(t)
// HTTP with Redis
config := http.DefaultConfiguration()
config.Listen = "127.0.0.1:0"
config.Cache.Config = http.RedisCacheConfiguration{
Protocol: "tcp",
Server: server,
DB: 10,
}
h, err := http.New(r, config, http.Dependencies{Daemon: daemon.NewMock(t)})
if err != nil {
t.Fatalf("New() error:\n%+v", err)
}
helpers.StartStop(t, h)
count := 0
h.GinRouter.GET("/api/v0/test",
h.CacheByRequestPath(time.Minute),
func(c *gin.Context) {
count++
c.JSON(netHTTP.StatusOK, gin.H{
"message": "ping",
"count": count,
})
})
// Check the HTTP server is running and answering metrics
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
{
Description: "not cached",
URL: "/api/v0/test",
JSONOutput: gin.H{"message": "ping", "count": 1},
}, {
Description: "cached",
URL: "/api/v0/test",
JSONOutput: gin.H{"message": "ping", "count": 1},
},
})
if err := client.Get(context.Background(), "cache-/api/v0/test").Err(); err != nil {
t.Fatalf("GET(\"cache-/api/v0/test\") error:\n%+v", err)
}
}

View File

@@ -3,17 +3,109 @@
package http
import (
"context"
"fmt"
"time"
"akvorado/common/helpers"
"github.com/chenyahui/gin-cache/persist"
"github.com/go-redis/redis/v8"
)
// Configuration describes the configuration for the HTTP server.
type Configuration struct {
// Listen defines the listening string to listen to.
Listen string `validate:"required,listen"`
// Profiler enables Go profiler as /debug
Profiler bool
// Cache configuration
Cache CacheConfiguration
}
// CacheConfiguration describes the configuration of the internal HTTP cache.
// Currently, it delegates everything to the implemented backends.
type CacheConfiguration struct {
// Config is the backend-specific configuration for the cache
Config CacheBackendConfiguration
}
// CacheBackendConfiguration represents the configuration of a cache backend.
type CacheBackendConfiguration interface {
New() (persist.CacheStore, error)
}
// MemoryCacheConfiguration is the configuration for an in-memory cache. There
// is no configuration.
type MemoryCacheConfiguration struct{}
// New creates a new memory cache store from a memory cache configuration.
func (MemoryCacheConfiguration) New() (persist.CacheStore, error) {
return persist.NewMemoryStore(5 * time.Minute), nil
}
// DefaultMemoryCacheConfiguration returns the default configuration for an
// in-memory cache.
func DefaultMemoryCacheConfiguration() CacheBackendConfiguration {
return MemoryCacheConfiguration{}
}
// RedisCacheConfiguration is the configuration for a Redis cache.
type RedisCacheConfiguration struct {
// Protocol to connect with
Protocol string `validate:"oneof=tcp unix"`
// Server to connect to (with port)
Server string `validate:"required,listen"`
// Optional username
Username string
// Optional password
Password string
// Database to connect to
DB int
}
// New creates a new Redis cache store from a Redis cache configuration.
func (c RedisCacheConfiguration) New() (persist.CacheStore, error) {
client := redis.NewClient(&redis.Options{
Network: c.Protocol,
Addr: c.Server,
Username: c.Username,
Password: c.Password,
DB: c.DB,
})
// TODO: defer client.Close()
if _, err := client.Ping(context.Background()).Result(); err != nil {
return nil, fmt.Errorf("cannot ping Redis server: %w", err)
}
return persist.NewRedisStore(client), nil
}
// DefaultRedisCacheConfiguration returns the default configuration for a
// Redis-backed cache.
func DefaultRedisCacheConfiguration() CacheBackendConfiguration {
return RedisCacheConfiguration{
Protocol: "tcp",
Server: "127.0.0.1:6379",
}
}
// DefaultConfiguration is the default configuration of the HTTP server.
func DefaultConfiguration() Configuration {
return Configuration{
Listen: "0.0.0.0:8080",
Cache: CacheConfiguration{
Config: DefaultMemoryCacheConfiguration(),
},
}
}
var cacheConfigurationMap = map[string](func() CacheBackendConfiguration){
"memory": DefaultMemoryCacheConfiguration,
"redis": DefaultRedisCacheConfiguration,
}
func init() {
helpers.RegisterMapstructureUnmarshallerHook(
helpers.ParametrizedConfigurationUnmarshallerHook(CacheConfiguration{}, cacheConfigurationMap))
}

56
common/http/metrics.go Normal file
View File

@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
package http
import "akvorado/common/reporter"
type metrics struct {
inflights reporter.Gauge
requests *reporter.CounterVec
durations *reporter.HistogramVec
sizes *reporter.HistogramVec
cacheHit *reporter.CounterVec
cacheMiss *reporter.CounterVec
}
func (c *Component) initMetrics() {
c.metrics.inflights = c.r.Gauge(
reporter.GaugeOpts{
Name: "inflight_requests",
Help: "Number of requests currently being served by the HTTP server.",
},
)
c.metrics.requests = c.r.CounterVec(
reporter.CounterOpts{
Name: "requests_total",
Help: "Number of requests handled by an handler.",
}, []string{"handler", "code", "method"},
)
c.metrics.durations = c.r.HistogramVec(
reporter.HistogramOpts{
Name: "request_duration_seconds",
Help: "Latencies for served requests.",
Buckets: []float64{.25, .5, 1, 2.5, 5, 10},
}, []string{"handler", "method"},
)
c.metrics.sizes = c.r.HistogramVec(
reporter.HistogramOpts{
Name: "response_size_bytes",
Help: "Response sizes for requests.",
Buckets: []float64{200, 500, 1000, 1500, 5000},
}, []string{"handler", "method"},
)
c.metrics.cacheHit = c.r.CounterVec(
reporter.CounterOpts{
Name: "cache_hit_total",
Help: "Number of requests served from cache",
}, []string{"path", "method"},
)
c.metrics.cacheMiss = c.r.CounterVec(
reporter.CounterOpts{
Name: "cache_miss_total",
Help: "Number of requests not served from cache",
}, []string{"path", "method"},
)
}

View File

@@ -12,6 +12,7 @@ import (
"net/http/pprof"
"time"
"github.com/chenyahui/gin-cache/persist"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -30,16 +31,12 @@ type Component struct {
config Configuration
mux *http.ServeMux
metrics struct {
inflights reporter.Gauge
requests *reporter.CounterVec
durations *reporter.HistogramVec
sizes *reporter.HistogramVec
}
metrics metrics
address net.Addr
// GinRouter is the router exposed for /api
GinRouter *gin.Engine
cacheStore persist.CacheStore
}
// Dependencies define the dependencies of the HTTP component.
@@ -49,6 +46,7 @@ type Dependencies struct {
// New creates a new HTTP component.
func New(r *reporter.Reporter, configuration Configuration, dependencies Dependencies) (*Component, error) {
var err error
c := Component{
r: r,
d: &dependencies,
@@ -57,35 +55,12 @@ func New(r *reporter.Reporter, configuration Configuration, dependencies Depende
mux: http.NewServeMux(),
GinRouter: gin.New(),
}
c.initMetrics()
c.d.Daemon.Track(&c.t, "common/http")
c.metrics.inflights = c.r.Gauge(
reporter.GaugeOpts{
Name: "inflight_requests",
Help: "Number of requests currently being served by the HTTP server.",
},
)
c.metrics.requests = c.r.CounterVec(
reporter.CounterOpts{
Name: "requests_total",
Help: "Number of requests handled by an handler.",
}, []string{"handler", "code", "method"},
)
c.metrics.durations = c.r.HistogramVec(
reporter.HistogramOpts{
Name: "request_duration_seconds",
Help: "Latencies for served requests.",
Buckets: []float64{.25, .5, 1, 2.5, 5, 10},
}, []string{"handler", "method"},
)
c.metrics.sizes = c.r.HistogramVec(
reporter.HistogramOpts{
Name: "response_size_bytes",
Help: "Response sizes for requests.",
Buckets: []float64{200, 500, 1000, 1500, 5000},
}, []string{"handler", "method"},
)
c.cacheStore, err = configuration.Cache.Config.New()
if err != nil {
return nil, err
}
c.GinRouter.Use(gin.Recovery())
c.AddHandler("/api/", c.GinRouter)
if configuration.Profiler {

View File

@@ -16,9 +16,8 @@ import (
// NewMock create a new HTTP component listening on a random free port.
func NewMock(t *testing.T, r *reporter.Reporter) *Component {
t.Helper()
config := Configuration{
Listen: "127.0.0.1:0",
}
config := DefaultConfiguration()
config.Listen = "127.0.0.1:0"
c, err := New(r, config, Dependencies{Daemon: daemon.NewMock(t)})
if err != nil {
t.Fatalf("New() error:\n%+v", err)

View File

@@ -347,18 +347,30 @@ SNMPv2.
### HTTP
The builtin HTTP server serves various pages. Its configuration
supports the `listen` key to specify the address and port to listen.
For example:
supports the following keys:
- `listen` defines the address and port to listen to.
- `profiler` enables [Go profiler HTTP
interface](https://pkg.go.dev/net/http/pprof). Check the [troubleshooting
section]( 05-troubleshooting.html#profiling) for details.
- `cache` defines the cache backend to use for some HTTP requests. It accepts a
`type` key which can be either `memory` (the default value) or `redis`. When
using the Redis backend, the following additional keys are also accepted:
`protocol` (`tcp` or `unix`), `server` (host and port), `username`,
`password`, and `db` (an integer to specify which database to use).
```yaml
http:
listen: 0.0.0.0:8000
cache:
type: redis
username: akvorado
password: akvorado
```
It also supports the `profiler` key. When set to `true`, various
[profiling data](https://pkg.go.dev/net/http/pprof) are made available
on the `/debug/pprof/` endpoint. This is useful if you wish to
optimize CPU or memory usage of one of the components.
Note that the cache backend is currently only useful with the console. You need
to define the cache in the `http` key of the `console` section for it to be
useful (not in the `inlet` section).
### Reporting

View File

@@ -16,6 +16,14 @@ identified with a specific icon:
There is a schema update in this version: you also have to restart ClickHouse
after upgrading for it to pick the new schema.
This version also introduces a cache for some HTTP requests, notably those to
plot the graphs in the “Visualize” tab. The default backend is in-memory,
however the shipped `akvorado.yaml` configuration file is using Redis instead.
The `docker-compose` setup has also been updated to start a Redis container for
this usage. Use of Redis is preferred but on upgrade, you need to enable it
explicitely by adding `console.http.cache` in your configuration.
-*console*: cache some costly requests to the backend
-*console*: add `SrcNetPrefix` and `DstNetPrefix` (as a dimension and a filter attribute)
-*inlet*: add `inlet.flow.inputs.use-src-addr-for-exporter-addr` to override exporter address
- 🌱 *console*: add `limit` and `graph-type` to `console.default-visualize-options`

View File

@@ -121,6 +121,14 @@ const encodedState = computed(() => encodeState(state.value));
// Fetch data
const fetchedData = ref<GraphHandlerResult | SankeyHandlerResult | null>(null);
const orderedJSONPayload = <T extends Record<string, any>>(input: T): T => {
return Object.keys(input)
.sort()
.reduce(
(o, k) => ((o[k] = input[k]), o),
{} as { [key: string]: any }
) as T;
};
const jsonPayload = computed(
(): SankeyHandlerInput | GraphHandlerInput | null => {
if (state.value === null) return null;
@@ -134,8 +142,8 @@ const jsonPayload = computed(
"humanEnd",
]),
};
return input;
}
return orderedJSONPayload(input);
} else {
const input: GraphHandlerInput = {
...omit(state.value, [
"graphType",
@@ -146,7 +154,8 @@ const jsonPayload = computed(
points: state.value.graphType === "grid" ? 50 : 200,
"previous-period": state.value.previousPeriod,
};
return input;
return orderedJSONPayload(input);
}
}
);
const request = ref<ModelType>(null); // Same as state, but once request is successful

View File

@@ -81,15 +81,15 @@ func (c *Component) Start() error {
endpoint := c.d.HTTP.GinRouter.Group("/api/v0/console", c.d.Auth.UserAuthentication())
endpoint.GET("/configuration", c.configHandlerFunc)
endpoint.GET("/docs/:name", c.docsHandlerFunc)
endpoint.GET("/widget/flow-last", c.widgetFlowLastHandlerFunc)
endpoint.GET("/widget/flow-rate", c.widgetFlowRateHandlerFunc)
endpoint.GET("/widget/exporters", c.widgetExportersHandlerFunc)
endpoint.GET("/widget/top/:name", c.widgetTopHandlerFunc)
endpoint.GET("/widget/graph", c.widgetGraphHandlerFunc)
endpoint.POST("/graph", c.graphHandlerFunc)
endpoint.POST("/sankey", c.sankeyHandlerFunc)
endpoint.GET("/widget/flow-last", c.d.HTTP.CacheByRequestPath(5*time.Second), c.widgetFlowLastHandlerFunc)
endpoint.GET("/widget/flow-rate", c.d.HTTP.CacheByRequestPath(5*time.Second), c.widgetFlowRateHandlerFunc)
endpoint.GET("/widget/exporters", c.d.HTTP.CacheByRequestPath(30*time.Second), c.widgetExportersHandlerFunc)
endpoint.GET("/widget/top/:name", c.d.HTTP.CacheByRequestPath(30*time.Second), c.widgetTopHandlerFunc)
endpoint.GET("/widget/graph", c.d.HTTP.CacheByRequestPath(5*time.Minute), c.widgetGraphHandlerFunc)
endpoint.POST("/graph", c.d.HTTP.CacheByRequestBody(30*time.Minute), c.graphHandlerFunc)
endpoint.POST("/sankey", c.d.HTTP.CacheByRequestBody(30*time.Minute), c.sankeyHandlerFunc)
endpoint.POST("/filter/validate", c.filterValidateHandlerFunc)
endpoint.POST("/filter/complete", c.filterCompleteHandlerFunc)
endpoint.POST("/filter/complete", c.d.HTTP.CacheByRequestBody(time.Minute), c.filterCompleteHandlerFunc)
endpoint.GET("/filter/saved", c.filterSavedListHandlerFunc)
endpoint.DELETE("/filter/saved/:id", c.filterSavedDeleteHandlerFunc)
endpoint.POST("/filter/saved", c.filterSavedAddHandlerFunc)

View File

@@ -10,8 +10,6 @@ import (
"strings"
"time"
"akvorado/common/helpers"
"github.com/gin-gonic/gin"
)
@@ -195,21 +193,8 @@ LIMIT 5
gc.JSON(http.StatusOK, gin.H{"top": results})
}
type widgetParameters struct {
Points uint `form:"points" binding:"isdefault|min=5,max=1000"`
}
func (c *Component) widgetGraphHandlerFunc(gc *gin.Context) {
ctx := c.t.Context(gc.Request.Context())
var params widgetParameters
if err := gc.ShouldBindQuery(&params); err != nil {
gc.JSON(http.StatusBadRequest, gin.H{"message": helpers.Capitalize(err.Error())})
return
}
if params.Points == 0 {
params.Points = 200
}
now := c.d.Clock.Now()
query := c.finalizeQuery(fmt.Sprintf(`
{{ with %s }}
@@ -229,7 +214,7 @@ ORDER BY Time WITH FILL
Start: now.Add(-24 * time.Hour),
End: now,
MainTableRequired: false,
Points: params.Points,
Points: 200,
})))
gc.Header("X-SQL-Query", query)

View File

@@ -232,8 +232,8 @@ func TestWidgetGraph(t *testing.T) {
mockConn.EXPECT().
Select(gomock.Any(), gomock.Any(), strings.TrimSpace(`
SELECT
toStartOfInterval(TimeReceived + INTERVAL 144 second, INTERVAL 864 second) - INTERVAL 144 second AS Time,
SUM(Bytes*SamplingRate*8/864)/1000/1000/1000 AS Gbps
toStartOfInterval(TimeReceived + INTERVAL 144 second, INTERVAL 432 second) - INTERVAL 144 second AS Time,
SUM(Bytes*SamplingRate*8/432)/1000/1000/1000 AS Gbps
FROM flows
WHERE TimeReceived BETWEEN toDateTime('2009-11-10 23:00:00', 'UTC') AND toDateTime('2009-11-11 23:00:00', 'UTC')
AND InIfBoundary = 'external'
@@ -241,13 +241,13 @@ GROUP BY Time
ORDER BY Time WITH FILL
FROM toDateTime('2009-11-10 23:00:00', 'UTC')
TO toDateTime('2009-11-11 23:00:00', 'UTC') + INTERVAL 1 second
STEP 864`)).
STEP 432`)).
SetArg(1, expected).
Return(nil)
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
{
URL: "/api/v0/console/widget/graph?points=100",
URL: "/api/v0/console/widget/graph",
JSONOutput: gin.H{
"data": []gin.H{
{"t": "2009-11-10T23:00:00Z", "gbps": 25.3},

View File

@@ -22,6 +22,13 @@ services:
ports:
- 127.0.0.1:9092:9093/tcp
redis:
image: bitnami/redis:7.0
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- 127.0.0.1:6379:6379/tcp
clickhouse:
image: clickhouse/clickhouse-server:22.8
ports:

View File

@@ -56,6 +56,12 @@ services:
- traefik.http.routers.kafka-ui.entrypoints=private
- traefik.http.routers.kafka-ui.rule=PathPrefix(`/kafka-ui`)
redis:
image: bitnami/redis:7.0
environment:
- ALLOW_EMPTY_PASSWORD=yes
restart: unless-stopped
# If you need to build from the source, uncomment the build option
# here and use `docker-compose build akvorado-service`.
akvorado-service: &akvorado-image
@@ -77,6 +83,8 @@ services:
akvorado-console:
<<: *akvorado-image
restart: unless-stopped
depends_on:
- redis
command: console http://akvorado-orchestrator:8080
volumes:
- akvorado-console-db:/run/akvorado

10
go.mod
View File

@@ -9,6 +9,7 @@ require (
github.com/antonmedv/expr v1.9.0
github.com/benbjohnson/clock v1.3.0
github.com/cenkalti/backoff/v4 v4.2.0
github.com/chenyahui/gin-cache v1.7.1
github.com/docker/docker v20.10.22+incompatible
github.com/docker/go-connections v0.4.0
github.com/eapache/go-resiliency v1.3.0
@@ -16,6 +17,7 @@ require (
github.com/gin-gonic/gin v1.8.1
github.com/glebarez/sqlite v1.6.0
github.com/go-playground/validator/v10 v10.11.1
github.com/go-redis/redis/v8 v8.11.4
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
github.com/google/gopacket v1.1.19
@@ -26,6 +28,7 @@ require (
github.com/mattn/go-isatty v0.0.16
github.com/mitchellh/mapstructure v1.5.0
github.com/netsampler/goflow2 v1.1.1-0.20221008154147-57fad2e0c837
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/oschwald/maxminddb-golang v1.10.0
github.com/osrg/gobgp/v3 v3.9.0
github.com/prometheus/client_golang v1.14.0
@@ -40,6 +43,7 @@ require (
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
golang.org/x/sys v0.3.0
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
google.golang.org/protobuf v1.28.1
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
gopkg.in/yaml.v2 v2.4.0
gorm.io/gorm v1.24.2
@@ -53,6 +57,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -79,6 +84,7 @@ require (
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jellydator/ttlcache/v2 v2.11.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -90,7 +96,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/paulmach/orb v0.7.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
@@ -117,8 +122,8 @@ require (
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 // indirect
golang.org/x/text v0.3.8 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.3.0 // indirect
modernc.org/libc v1.21.5 // indirect
@@ -129,5 +134,6 @@ require (
replace (
github.com/benbjohnson/clock => github.com/vincentbernat/go-clock v0.0.0-20220922224448-739bd11b5833
github.com/chenyahui/gin-cache => github.com/vincentbernat/gin-cache v0.0.0-20221221202310-6f54afb45ed6
github.com/kentik/patricia => github.com/vincentbernat/patricia v0.0.0-20220923091046-b376a1167a94
)

55
go.sum
View File

@@ -82,6 +82,8 @@ github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
@@ -108,12 +110,15 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
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/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/glebarez/go-sqlite v1.20.0 h1:6D9uRXq3Kd+W7At+hOU2eIAeahv6qcYfO8jzmvb4Dr8=
@@ -139,13 +144,19 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
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 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@@ -198,6 +209,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -233,6 +245,7 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
@@ -253,6 +266,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJz
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jellydator/ttlcache/v2 v2.11.1 h1:AZGME43Eh2Vv3giG6GeqeLeFXxwxn1/qHItqWZl6U64=
github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2Hy3c5Z4n14XmSvTI=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@@ -264,6 +279,7 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1 h1:Q6uM1SfwyYPCBtezf829EqAqolrIGhAm6KfVx3QBRWg=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
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=
@@ -291,12 +307,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -326,6 +344,17 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/netsampler/goflow2 v1.1.1-0.20221008154147-57fad2e0c837 h1:oO41e5uathu41BOCco3GIkyoYiKz9skbW805rMomwBc=
github.com/netsampler/goflow2 v1.1.1-0.20221008154147-57fad2e0c837/go.mod h1:C9f54WtFVVbGpPWnpLMz+/hS3c7wc4L0g9ZzdIFAcuM=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
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=
@@ -439,11 +468,15 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
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=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vincentbernat/gin-cache v0.0.0-20221221202310-6f54afb45ed6 h1:qP2BJklMG2KpyRjeY1lbJYA5ezSNkjgKUIYlrDCyWLg=
github.com/vincentbernat/gin-cache v0.0.0-20221221202310-6f54afb45ed6/go.mod h1:eEAwR4874QJI3dY7rdkoartzwVD0e1iq8wEJaEbzA64=
github.com/vincentbernat/go-clock v0.0.0-20220922224448-739bd11b5833 h1:eeHgOFlrGNESR9TF+AJovNWOxH8AdmXWK2nGXKa6RUU=
github.com/vincentbernat/go-clock v0.0.0-20220922224448-739bd11b5833/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/vincentbernat/patricia v0.0.0-20220923091046-b376a1167a94 h1:T7+yyM6300NYIv1kqlXX53d2cjEHpgDt6cFbBYO+upk=
@@ -480,6 +513,8 @@ go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -514,6 +549,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
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 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/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=
@@ -526,6 +563,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -550,6 +588,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
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-20200520004742-59133d7f0dd7/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=
@@ -559,6 +598,7 @@ golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -584,8 +624,11 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
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.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc=
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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=
@@ -599,14 +642,18 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -626,6 +673,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -677,6 +725,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
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-20191108193012-7d206e10da11/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=
@@ -704,9 +753,12 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
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-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
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=
@@ -797,6 +849,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
@@ -805,6 +859,7 @@ 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=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=