Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
graphqlbackend: update throttled to use Ctx stores (#54748)
Browse files Browse the repository at this point in the history
It seems we were using a weird version of throttled which I don't think
was actually based on the v2 series correctly. This updates us to the
latest version and moves us away from the removed struct GCRARateLimiter
and the deprecated interface GCRAStore.

I came across this while attempting to update deps and getting failing
compiles.

Test Plan: we have unit tests for this, so CI.
  • Loading branch information
keegancsmith authored Jul 11, 2023
1 parent 304dfae commit c390e4b
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 61 deletions.
48 changes: 9 additions & 39 deletions cmd/frontend/graphqlbackend/rate_limit.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphqlbackend

import (
"context"
"strconv"
"sync/atomic"

Expand Down Expand Up @@ -369,14 +370,14 @@ type LimiterArgs struct {
}

type Limiter interface {
RateLimit(key string, quantity int, args LimiterArgs) (bool, throttled.RateLimitResult, error)
RateLimit(ctx context.Context, key string, quantity int, args LimiterArgs) (bool, throttled.RateLimitResult, error)
}

type LimitWatcher interface {
Get() (Limiter, bool)
}

func NewBasicLimitWatcher(logger log.Logger, store throttled.GCRAStore) *BasicLimitWatcher {
func NewBasicLimitWatcher(logger log.Logger, store throttled.GCRAStoreCtx) *BasicLimitWatcher {
basic := &BasicLimitWatcher{
store: store,
}
Expand All @@ -392,7 +393,7 @@ func NewBasicLimitWatcher(logger log.Logger, store throttled.GCRAStore) *BasicLi
}

type BasicLimitWatcher struct {
store throttled.GCRAStore
store throttled.GCRAStoreCtx
rl atomic.Value // *RateLimiter
}

Expand All @@ -403,7 +404,7 @@ func (bl *BasicLimitWatcher) updateFromConfig(logger log.Logger, limit int) {
return
}
maxBurstPercentage := 0.2
l, err := throttled.NewGCRARateLimiter(
l, err := throttled.NewGCRARateLimiterCtx(
bl.store,
throttled.RateQuota{
MaxRate: throttled.PerHour(limit),
Expand All @@ -428,46 +429,15 @@ func (bl *BasicLimitWatcher) Get() (Limiter, bool) {
}

type BasicLimiter struct {
*throttled.GCRARateLimiter
*throttled.GCRARateLimiterCtx
enabled bool
}

// RateLimit limits unauthenticated requests to the GraphQL API with an equal
// quantity of 1.
func (bl *BasicLimiter) RateLimit(_ string, _ int, args LimiterArgs) (bool, throttled.RateLimitResult, error) {
if args.Anonymous && args.RequestName == "unknown" && args.RequestSource == trace.SourceOther && bl.GCRARateLimiter != nil {
return bl.GCRARateLimiter.RateLimit("basic", 1)
func (bl *BasicLimiter) RateLimit(ctx context.Context, _ string, _ int, args LimiterArgs) (bool, throttled.RateLimitResult, error) {
if args.Anonymous && args.RequestName == "unknown" && args.RequestSource == trace.SourceOther && bl.GCRARateLimiterCtx != nil {
return bl.GCRARateLimiterCtx.RateLimitCtx(ctx, "basic", 1)
}
return false, throttled.RateLimitResult{}, nil
}

type RateLimiter struct {
enabled bool
ipLimiter *throttled.GCRARateLimiter
userLimiter *throttled.GCRARateLimiter
overrides map[string]limiter
}

func (rl *RateLimiter) RateLimit(uid string, cost int, args LimiterArgs) (bool, throttled.RateLimitResult, error) {
if r, ok := rl.overrides[uid]; ok {
return r.RateLimit(uid, cost)
}
if args.IsIP {
return rl.ipLimiter.RateLimit(uid, cost)
}
return rl.userLimiter.RateLimit(uid, cost)
}

type limiter interface {
RateLimit(string, int) (bool, throttled.RateLimitResult, error)
}

// fixedLimiter is a rate limiter that always returns the same result
type fixedLimiter struct {
limited bool
result throttled.RateLimitResult
}

func (f *fixedLimiter) RateLimit(string, int) (bool, throttled.RateLimitResult, error) {
return f.limited, f.result, nil
}
9 changes: 5 additions & 4 deletions cmd/frontend/graphqlbackend/rate_limit_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphqlbackend

import (
"context"
"fmt"
"testing"

Expand Down Expand Up @@ -415,7 +416,7 @@ func TestBasicLimiterEnabled(t *testing.T) {

for _, tt := range tests {
t.Run(fmt.Sprintf("limit:%d", tt.limit), func(t *testing.T) {
store, err := memstore.New(1)
store, err := memstore.NewCtx(1)
if err != nil {
t.Fatal(err)
}
Expand All @@ -435,7 +436,7 @@ func TestBasicLimiterEnabled(t *testing.T) {
}

func TestBasicLimiter(t *testing.T) {
store, err := memstore.New(1)
store, err := memstore.NewCtx(1)
if err != nil {
t.Fatal(err)
}
Expand All @@ -458,7 +459,7 @@ func TestBasicLimiter(t *testing.T) {
}

// 1st call should not be limited.
limited, _, err := limiter.RateLimit("", 1, limiterArgs)
limited, _, err := limiter.RateLimit(context.Background(), "", 1, limiterArgs)
if err != nil {
t.Fatal(err)
}
Expand All @@ -467,7 +468,7 @@ func TestBasicLimiter(t *testing.T) {
}

// 2nd call should be limited.
limited, _, err = limiter.RateLimit("", 1, limiterArgs)
limited, _, err = limiter.RateLimit(context.Background(), "", 1, limiterArgs)
if err != nil {
t.Fatal(err)
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/frontend/internal/cli/serve_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,14 @@ func isAllowedOrigin(origin string, allowedOrigins []string) bool {
}

func makeRateLimitWatcher() (*graphqlbackend.BasicLimitWatcher, error) {
var store throttled.GCRAStore
var store throttled.GCRAStoreCtx
var err error
if pool, ok := redispool.Cache.Pool(); ok {
store, err = redigostore.New(pool, "gql:rl:", 0)
store, err = redigostore.NewCtx(pool, "gql:rl:", 0)
} else {
// If redis is disabled we are in Sourcegraph App and can rely on an
// in-memory store.
store, err = memstore.New(0)
store, err = memstore.NewCtx(0)
}
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion cmd/frontend/internal/httpapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func init() {
func newTest(t *testing.T) *httptestutil.Client {
logger := logtest.Scoped(t)
enterpriseServices := enterprise.DefaultServices()
rateLimitStore, _ := memstore.New(1024)
rateLimitStore, _ := memstore.NewCtx(1024)
rateLimiter := graphqlbackend.NewBasicLimitWatcher(logger, rateLimitStore)

db := database.NewMockDB()
Expand Down
2 changes: 1 addition & 1 deletion cmd/frontend/internal/httpapi/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func serveGraphQL(logger sglog.Logger, schema *graphql.Schema, rlw graphqlbacken
traceData.cost = cost

if rl, enabled := rlw.Get(); enabled && cost != nil {
limited, result, err := rl.RateLimit(uid, cost.FieldCount, graphqlbackend.LimiterArgs{
limited, result, err := rl.RateLimit(r.Context(), uid, cost.FieldCount, graphqlbackend.LimiterArgs{
IsIP: isIP,
Anonymous: anonymous,
RequestName: requestName,
Expand Down
16 changes: 8 additions & 8 deletions deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -899,15 +899,15 @@ def go_dependencies():
name = "com_github_bsm_ginkgo_v2",
build_file_proto_mode = "disable_global",
importpath = "github.com/bsm/ginkgo/v2",
sum = "h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ=",
version = "v2.5.0",
sum = "h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=",
version = "v2.7.0",
)
go_repository(
name = "com_github_bsm_gomega",
build_file_proto_mode = "disable_global",
importpath = "github.com/bsm/gomega",
sum = "h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8=",
version = "v1.20.0",
sum = "h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=",
version = "v1.26.0",
)

go_repository(
Expand Down Expand Up @@ -5806,8 +5806,8 @@ def go_dependencies():
name = "com_github_redis_go_redis_v9",
build_file_proto_mode = "disable_global",
importpath = "github.com/redis/go-redis/v9",
sum = "h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=",
version = "v9.0.2",
sum = "h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=",
version = "v9.0.5",
)

go_repository(
Expand Down Expand Up @@ -6510,8 +6510,8 @@ def go_dependencies():
name = "com_github_throttled_throttled_v2",
build_file_proto_mode = "disable_global",
importpath = "github.com/throttled/throttled/v2",
sum = "h1:DOkCb1el7NYzRoPb1pyeHVghsUoonVWEjmo34vrcp/8=",
version = "v2.9.0",
sum = "h1:IezKE1uHlYC/0Al05oZV6Ar+uN/znw3cy9J8banxhEY=",
version = "v2.12.0",
)
go_repository(
name = "com_github_tidwall_gjson",
Expand Down
10 changes: 10 additions & 0 deletions dev/backcompat/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ fi
find . -type f -name "*.bazel" -exec $_sed_binary -i 's|@com_github_sourcegraph_conc|@back_compat_com_github_sourcegraph_conc|g' {} +
find . -type f -name "*.bazel" -exec $_sed_binary -i 's|@com_github_sourcegraph_scip|@back_compat_com_github_sourcegraph_scip|g' {} +
find . -type f -name "*.bazel" -exec $_sed_binary -i 's|@com_github_sourcegraph_zoekt|@back_compat_com_github_sourcegraph_zoekt|g' {} +
find . -type f -name "*.bazel" -exec $_sed_binary -i 's|@com_github_throttled_throttled_v2|@back_compat_com_github_throttled_throttled_v2|g' {} +
"""

def back_compat_defs():
Expand Down Expand Up @@ -114,6 +115,15 @@ def back_compat_defs():
version = "v0.0.0-20230620185637-63241cb1b17a",
)

go_repository(
name = "back_compat_com_github_throttled_throttled_v2",
build_file_proto_mode = "disable_global",
importpath = "github.com/throttled/throttled/v2",
sum = "h1:DOkCb1el7NYzRoPb1pyeHVghsUoonVWEjmo34vrcp/8=",
version = "v2.9.0",
)


# Now that we have declared a replacement for the two problematic go packages that
# @sourcegraph_back_compat depends on, we can define the repository itself. Because it
# comes with its Bazel rules (logical, that's just the current repository but with a different
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ require (
github.com/sourcegraph/sourcegraph/lib v0.0.0-20230613175844-f031949c72f5
github.com/stretchr/testify v1.8.2
github.com/temoto/robotstxt v1.1.2
github.com/throttled/throttled/v2 v2.9.0
github.com/throttled/throttled/v2 v2.12.0
github.com/tidwall/gjson v1.14.0
github.com/tj/go-naturaldate v1.3.0
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
Expand Down
14 changes: 10 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1l
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bufbuild/buf v1.4.0 h1:GqE3a8CMmcFvWPzuY3Mahf9Kf3S9XgZ/ORpfYFzO+90=
github.com/bufbuild/buf v1.4.0/go.mod h1:mwHG7klTHnX+rM/ym8LXGl7vYpVmnwT96xWoRB4H5QI=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
Expand Down Expand Up @@ -963,6 +965,7 @@ github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGK
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/go-redis/redis/v8 v8.4.2/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM=
Expand Down Expand Up @@ -1765,6 +1768,7 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
Expand Down Expand Up @@ -1927,8 +1931,9 @@ github.com/qustavo/sqlhooks/v2 v2.1.0/go.mod h1:aMREyKo7fOKTwiLuWPsaHRXEmtqG4yRE
github.com/rafaeljusto/redigomock/v3 v3.1.2 h1:B4Y0XJQiPjpwYmkH55aratKX1VfR+JRqzmDKyZbC99o=
github.com/rafaeljusto/redigomock/v3 v3.1.2/go.mod h1:F9zPqz8rMriScZkPtUiLJoLruYcpGo/XXREpeyasREM=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285 h1:d54EL9l+XteliUfUCGsEwwuk65dmmxX85VXF+9T6+50=
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285/go.mod h1:fxIDly1xtudczrZeOOlfaUvd2OPb2qZAPuWdU2BsBTk=
Expand Down Expand Up @@ -2139,8 +2144,8 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ
github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg=
github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
github.com/testcontainers/testcontainers-go v0.19.0/go.mod h1:3YsSoxK0rGEUzbGD4gUVt1Nm3GJpCIq94GX+2LSf3d4=
github.com/throttled/throttled/v2 v2.9.0 h1:DOkCb1el7NYzRoPb1pyeHVghsUoonVWEjmo34vrcp/8=
github.com/throttled/throttled/v2 v2.9.0/go.mod h1:0JHxhGAidPyqbgD4HF8Y1sNFfG0ffVXK6C8EpkNdLEM=
github.com/throttled/throttled/v2 v2.12.0 h1:IezKE1uHlYC/0Al05oZV6Ar+uN/znw3cy9J8banxhEY=
github.com/throttled/throttled/v2 v2.12.0/go.mod h1:+EAvrG2hZAQTx8oMpBu8fq6Xmm+d1P2luKK7fIY1Esc=
github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
Expand Down Expand Up @@ -2354,6 +2359,7 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.14.0 h1:j6Xah53xRDrR+K1c4Y1uVH
go.opentelemetry.io/contrib/propagators/jaeger v1.14.0/go.mod h1:viOfwr1OqHmCF6G3KvKnnmpSJUX/rLzXztU18FC9ymU=
go.opentelemetry.io/contrib/propagators/ot v1.14.0 h1:jqxznjMkz/3l4NUsYq4OMbP+zs5twBbCZwSlSt82KXo=
go.opentelemetry.io/contrib/propagators/ot v1.14.0/go.mod h1:0qeUHgw3lmmZupgaft3m2/K6ip+pzqwePuW/lvU7ia4=
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
Expand Down Expand Up @@ -2837,6 +2843,7 @@ 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
Expand Down Expand Up @@ -3225,7 +3232,6 @@ 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.7/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=
Expand Down

0 comments on commit c390e4b

Please sign in to comment.