Commit 2aeac62e authored by tavit ohanian's avatar tavit ohanian

Merge remote-tracking branch 'upstream/master' into reference

parents 52bfd828 22c5a172
Pipeline #108 failed with stages
in 0 seconds
version: 2.1
orbs:
ci-go: ipfs/ci-go@0.1
workflows:
version: 2
test:
jobs:
- ci-go/build
- ci-go/lint
- ci-go/test
blank_issues_enabled: false
contact_links:
- name: Getting Help on IPFS
url: https://ipfs.io/help
about: All information about how and where to get help on IPFS.
- name: IPFS Official Forum
url: https://discuss.ipfs.io
about: Please post general questions, support requests, and discussions here.
---
name: Open an issue
about: Only for actionable issues relevant to this repository.
title: ''
labels: need/triage
assignees: ''
---
<!--
Hello! To ensure this issue is correctly addressed as soon as possible by the IPFS team, please try to make sure:
- This issue is relevant to this repository's topic or codebase.
- A clear description is provided. It should includes as much relevant information as possible and clear scope for the issue to be actionable.
FOR GENERAL DISCUSSION, HELP OR QUESTIONS, please see the options at https://ipfs.io/help or head directly to https://discuss.ipfs.io.
(you can delete this section after reading)
-->
# Configuration for welcome - https://github.com/behaviorbot/welcome
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
# Comment to be posted to on first time issues
newIssueWelcomeComment: >
Thank you for submitting your first issue to this repository! A maintainer
will be here shortly to triage and review.
In the meantime, please double-check that you have provided all the
necessary information to make this process easy! Any information that can
help save additional round trips is useful! We currently aim to give
initial feedback within **two business days**. If this does not happen, feel
free to leave a comment.
Please keep an eye on how this issue will be labeled, as labels give an
overview of priorities, assignments and additional actions requested by the
maintainers:
- "Priority" labels will show how urgent this is for the team.
- "Status" labels will show if this is ready to be worked on, blocked, or in progress.
- "Need" labels will indicate if additional input or analysis is required.
Finally, remember to use https://discuss.ipfs.io if you just need general
support.
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: >
Thank you for submitting this PR!
A maintainer will be here shortly to review it.
We are super grateful, but we are also overloaded! Help us by making sure
that:
* The context for this PR is clear, with relevant discussion, decisions
and stakeholders linked/mentioned.
* Your contribution itself is clear (code comments, self-review for the
rest) and in its best form. Follow the [code contribution
guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines)
if they apply.
Getting other community members to do a review would be great help too on
complex PRs (you can ask in the chats/forums). If you are unsure about
something, just leave us a comment.
Next steps:
* A maintainer will triage and assign priority to this PR, commenting on
any missing things and potentially assigning a reviewer for high
priority items.
* The PR gets reviews, discussed and approvals as needed.
* The PR is merged by maintainers when it has been approved and comments addressed.
We currently aim to provide initial feedback/triaging within **two business
days**. Please keep an eye on any labelling actions, as these will indicate
priorities and status of your contribution.
We are very grateful for your contribution!
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
# Currently disabled
#firstPRMergeComment: ""
1.12.4: QmeSwaXGLDbzGXTaaNoCP9drpFp4YDUDRwE2Qw7wzvDCKm
The MIT License
Copyright (c) 2016 Łukasz Magiera
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
# go-ds-badger # go-ds-badger
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![GoDoc](https://godoc.org/github.com/ipfs/go-ds-badger?status.svg)](https://godoc.org/github.com/ipfs/go-ds-badger)
[![Build Status](https://travis-ci.org/ipfs/go-ds-badger.svg?branch=master)](https://travis-ci.org/ipfs/go-ds-badger)
> Datastore implementation using [badger](https://github.com/dgraph-io/badger) as backend.
## Lead Maintainer
[Łukasz Magiera](https://github.com/magik6k)
## Table of Contents
- [Documentation](#documentation)
- [Badger2](#badger2)
- [Contribute](#contribute)
- [License](#license)
## Documentation
https://godoc.org/github.com/ipfs/go-ds-badger
## Badger2
This repo contains a datastore implementation using Badger v1. If you are looking for a Badger v2 datastore check out https://github.com/ipfs/go-ds-badger2.
## Contribute
Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-ds-badger/issues)!
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
### Want to hack on IPFS?
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
## License
MIT
coverage:
range: "50...100"
comment: off
This diff is collapsed.
package badger
import (
"bytes"
"fmt"
"io/ioutil"
"math/rand"
"os"
"sort"
"testing"
"time"
ds "github.com/ipfs/go-datastore"
dsq "github.com/ipfs/go-datastore/query"
dstest "github.com/ipfs/go-datastore/test"
)
var testcases = map[string]string{
"/a": "a",
"/a/b": "ab",
"/a/b/c": "abc",
"/a/b/d": "a/b/d",
"/a/c": "ac",
"/a/d": "ad",
"/e": "e",
"/f": "f",
"/g": "",
}
// returns datastore, and a function to call on exit.
// (this garbage collects). So:
//
// d, close := newDS(t)
// defer close()
func newDS(t *testing.T) (*Datastore, func()) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
d, err := NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
return d, func() {
d.Close()
os.RemoveAll(path)
}
}
func addTestCases(t *testing.T, d *Datastore, testcases map[string]string) {
for k, v := range testcases {
dsk := ds.NewKey(k)
if err := d.Put(dsk, []byte(v)); err != nil {
t.Fatal(err)
}
}
for k, v := range testcases {
dsk := ds.NewKey(k)
v2, err := d.Get(dsk)
if err != nil {
t.Fatal(err)
}
if string(v2) != v {
t.Errorf("%s values differ: %s != %s", k, v, v2)
}
}
}
func TestQuery(t *testing.T) {
d, done := newDS(t)
defer done()
addTestCases(t, d, testcases)
rs, err := d.Query(dsq.Query{Prefix: "/a/"})
if err != nil {
t.Fatal(err)
}
expectMatches(t, []string{
"/a/b",
"/a/b/c",
"/a/b/d",
"/a/c",
"/a/d",
}, rs)
// test offset and limit
rs, err = d.Query(dsq.Query{Prefix: "/a/", Offset: 2, Limit: 2})
if err != nil {
t.Fatal(err)
}
expectMatches(t, []string{
"/a/b/d",
"/a/c",
}, rs)
}
func TestHas(t *testing.T) {
d, done := newDS(t)
defer done()
addTestCases(t, d, testcases)
has, err := d.Has(ds.NewKey("/a/b/c"))
if err != nil {
t.Error(err)
}
if !has {
t.Error("Key should be found")
}
has, err = d.Has(ds.NewKey("/a/b/c/d"))
if err != nil {
t.Error(err)
}
if has {
t.Error("Key should not be found")
}
}
func TestGetSize(t *testing.T) {
d, done := newDS(t)
defer done()
addTestCases(t, d, testcases)
size, err := d.GetSize(ds.NewKey("/a/b/c"))
if err != nil {
t.Error(err)
}
if size != len(testcases["/a/b/c"]) {
t.Error("")
}
_, err = d.GetSize(ds.NewKey("/a/b/c/d"))
if err != ds.ErrNotFound {
t.Error(err)
}
}
func TestNotExistGet(t *testing.T) {
d, done := newDS(t)
defer done()
addTestCases(t, d, testcases)
has, err := d.Has(ds.NewKey("/a/b/c/d"))
if err != nil {
t.Error(err)
}
if has {
t.Error("Key should not be found")
}
val, err := d.Get(ds.NewKey("/a/b/c/d"))
if val != nil {
t.Error("Key should not be found")
}
if err != ds.ErrNotFound {
t.Error("Error was not set to ds.ErrNotFound")
if err != nil {
t.Error(err)
}
}
}
func TestDelete(t *testing.T) {
d, done := newDS(t)
defer done()
addTestCases(t, d, testcases)
has, err := d.Has(ds.NewKey("/a/b/c"))
if err != nil {
t.Error(err)
}
if !has {
t.Error("Key should be found")
}
err = d.Delete(ds.NewKey("/a/b/c"))
if err != nil {
t.Error(err)
}
has, err = d.Has(ds.NewKey("/a/b/c"))
if err != nil {
t.Error(err)
}
if has {
t.Error("Key should not be found")
}
}
func TestGetEmpty(t *testing.T) {
d, done := newDS(t)
defer done()
err := d.Put(ds.NewKey("/a"), []byte{})
if err != nil {
t.Error(err)
}
v, err := d.Get(ds.NewKey("/a"))
if err != nil {
t.Error(err)
}
if len(v) != 0 {
t.Error("expected 0 len []byte form get")
}
}
func expectMatches(t *testing.T, expect []string, actualR dsq.Results) {
actual, err := actualR.Rest()
if err != nil {
t.Error(err)
}
if len(actual) != len(expect) {
t.Error("not enough", expect, actual)
}
for _, k := range expect {
found := false
for _, e := range actual {
if e.Key == k {
found = true
}
}
if !found {
t.Error(k, "not found")
}
}
}
func TestBatching(t *testing.T) {
d, done := newDS(t)
defer done()
b, err := d.Batch()
if err != nil {
t.Fatal(err)
}
for k, v := range testcases {
err := b.Put(ds.NewKey(k), []byte(v))
if err != nil {
t.Fatal(err)
}
}
err = b.Commit()
if err != nil {
t.Fatal(err)
}
for k, v := range testcases {
val, err := d.Get(ds.NewKey(k))
if err != nil {
t.Fatal(err)
}
if v != string(val) {
t.Fatal("got wrong data!")
}
}
//Test delete
b, err = d.Batch()
if err != nil {
t.Fatal(err)
}
err = b.Delete(ds.NewKey("/a/b"))
if err != nil {
t.Fatal(err)
}
err = b.Delete(ds.NewKey("/a/b/c"))
if err != nil {
t.Fatal(err)
}
err = b.Commit()
if err != nil {
t.Fatal(err)
}
rs, err := d.Query(dsq.Query{Prefix: "/"})
if err != nil {
t.Fatal(err)
}
expectMatches(t, []string{
"/a",
"/a/b/d",
"/a/c",
"/a/d",
"/e",
"/f",
"/g",
}, rs)
//Test cancel
b, err = d.Batch()
if err != nil {
t.Fatal(err)
}
const key = "/xyz"
err = b.Put(ds.NewKey(key), []byte("/x/y/z"))
if err != nil {
t.Fatal(err)
}
// TODO: remove type assertion once datastore.Batch interface has Cancel
err = b.(*batch).Cancel()
if err != nil {
t.Fatal(err)
}
_, err = d.Get(ds.NewKey(key))
if err == nil {
t.Fatal("expected error trying to get uncommited data")
}
}
func TestBatchingRequired(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
dsOpts := DefaultOptions
d, err := NewDatastore(path, &dsOpts)
if err != nil {
t.Fatal(err)
}
defer func() {
d.Close()
os.RemoveAll(path)
}()
const valSize = 1000
// Check that transaction fails when there are too many writes. This is
// not testing batching logic, but is here to prove that batching works
// where a transaction fails.
t.Logf("putting %d byte values until transaction overflows", valSize)
tx, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
var puts int
for ; puts < 10000000; puts++ {
buf := make([]byte, valSize)
rand.Read(buf)
err = tx.Put(ds.NewKey(fmt.Sprintf("/key%d", puts)), buf)
if err != nil {
break
}
puts++
}
if err == nil {
t.Error("expected transaction to fail")
} else {
t.Logf("OK - transaction cannot handle %d puts: %s", puts, err)
}
tx.Discard()
// Check that batch succeeds with the same number of writes that caused a
// transaction to fail.
t.Logf("putting %d %d byte values using batch", puts, valSize)
b, err := d.Batch()
if err != nil {
t.Fatal(err)
}
for i := 0; i < puts; i++ {
buf := make([]byte, valSize)
rand.Read(buf)
err = b.Put(ds.NewKey(fmt.Sprintf("/key%d", i)), buf)
if err != nil {
t.Fatal(err)
}
}
err = b.Commit()
if err != nil {
t.Fatal(err)
}
}
// Tests from basic_tests from go-datastore
func TestBasicPutGet(t *testing.T) {
d, done := newDS(t)
defer done()
k := ds.NewKey("foo")
val := []byte("Hello Datastore!")
err := d.Put(k, val)
if err != nil {
t.Fatal("error putting to datastore: ", err)
}
have, err := d.Has(k)
if err != nil {
t.Fatal("error calling has on key we just put: ", err)
}
if !have {
t.Fatal("should have key foo, has returned false")
}
out, err := d.Get(k)
if err != nil {
t.Fatal("error getting value after put: ", err)
}
if !bytes.Equal(out, val) {
t.Fatal("value received on get wasnt what we expected:", out)
}
have, err = d.Has(k)
if err != nil {
t.Fatal("error calling has after get: ", err)
}
if !have {
t.Fatal("should have key foo, has returned false")
}
err = d.Delete(k)
if err != nil {
t.Fatal("error calling delete: ", err)
}
have, err = d.Has(k)
if err != nil {
t.Fatal("error calling has after delete: ", err)
}
if have {
t.Fatal("should not have key foo, has returned true")
}
}
func TestNotFounds(t *testing.T) {
d, done := newDS(t)
defer done()
badk := ds.NewKey("notreal")
val, err := d.Get(badk)
if err != ds.ErrNotFound {
t.Fatal("expected ErrNotFound for key that doesnt exist, got: ", err)
}
if val != nil {
t.Fatal("get should always return nil for not found values")
}
have, err := d.Has(badk)
if err != nil {
t.Fatal("error calling has on not found key: ", err)
}
if have {
t.Fatal("has returned true for key we don't have")
}
}
func TestManyKeysAndQuery(t *testing.T) {
d, done := newDS(t)
defer done()
var keys []ds.Key
var keystrs []string
var values [][]byte
count := 100
for i := 0; i < count; i++ {
s := fmt.Sprintf("%dkey%d", i, i)
dsk := ds.NewKey(s)
keystrs = append(keystrs, dsk.String())
keys = append(keys, dsk)
buf := make([]byte, 64)
rand.Read(buf)
values = append(values, buf)
}
t.Logf("putting %d values", count)
for i, k := range keys {
err := d.Put(k, values[i])
if err != nil {
t.Fatalf("error on put[%d]: %s", i, err)
}
}
t.Log("getting values back")
for i, k := range keys {
val, err := d.Get(k)
if err != nil {
t.Fatalf("error on get[%d]: %s", i, err)
}
if !bytes.Equal(val, values[i]) {
t.Fatal("input value didnt match the one returned from Get")
}
}
t.Log("querying values")
q := dsq.Query{KeysOnly: true}
resp, err := d.Query(q)
if err != nil {
t.Fatal("calling query: ", err)
}
t.Log("aggregating query results")
var outkeys []string
for {
res, ok := resp.NextSync()
if res.Error != nil {
t.Fatal("query result error: ", res.Error)
}
if !ok {
break
}
outkeys = append(outkeys, res.Key)
}
t.Log("verifying query output")
sort.Strings(keystrs)
sort.Strings(outkeys)
if len(keystrs) != len(outkeys) {
t.Fatalf("got wrong number of keys back, %d != %d", len(keystrs), len(outkeys))
}
for i, s := range keystrs {
if outkeys[i] != s {
t.Fatalf("in key output, got %s but expected %s", outkeys[i], s)
}
}
t.Log("deleting all keys")
for _, k := range keys {
if err := d.Delete(k); err != nil {
t.Fatal(err)
}
}
}
func TestGC(t *testing.T) {
d, done := newDS(t)
defer done()
count := 10000
b, err := d.Batch()
if err != nil {
t.Fatal(err)
}
t.Logf("putting %d values", count)
for i := 0; i < count; i++ {
buf := make([]byte, 6400)
rand.Read(buf)
err = b.Put(ds.NewKey(fmt.Sprintf("/key%d", i)), buf)
if err != nil {
t.Fatal(err)
}
}
err = b.Commit()
if err != nil {
t.Fatal(err)
}
b, err = d.Batch()
if err != nil {
t.Fatal(err)
}
t.Logf("deleting %d values", count)
for i := 0; i < count; i++ {
err := b.Delete(ds.NewKey(fmt.Sprintf("/key%d", i)))
if err != nil {
t.Fatal(err)
}
}
err = b.Commit()
if err != nil {
t.Fatal(err)
}
if err := d.CollectGarbage(); err != nil {
t.Fatal(err)
}
}
// TestDiskUsage verifies we fetch some badger size correctly.
// Because the Size metric is only updated every minute in badger and
// this interval is not configurable, we re-open the database
// (the size is always calculated on Open) to make things quick.
func TestDiskUsage(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
d, err := NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
addTestCases(t, d, testcases)
d.Close()
d, err = NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
s, _ := d.DiskUsage()
if s <= 0 {
t.Error("expected some size")
}
d.Close()
}
func TestTxnDiscard(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
d, err := NewDatastore(path, nil)
defer os.RemoveAll(path)
if err != nil {
t.Fatal(err)
}
txn, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
key := ds.NewKey("/test/thing")
if err := txn.Put(key, []byte{1, 2, 3}); err != nil {
t.Fatal(err)
}
txn.Discard()
has, err := d.Has(key)
if err != nil {
t.Fatal(err)
}
if has {
t.Fatal("key written in aborted transaction still exists")
}
d.Close()
}
func TestTxnCommit(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
d, err := NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
txn, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
key := ds.NewKey("/test/thing")
if err := txn.Put(key, []byte{1, 2, 3}); err != nil {
t.Fatal(err)
}
err = txn.Commit()
if err != nil {
t.Fatal(err)
}
has, err := d.Has(key)
if err != nil {
t.Fatal(err)
}
if !has {
t.Fatal("key written in committed transaction does not exist")
}
d.Close()
}
func TestTxnBatch(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
d, err := NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
txn, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
data := make(map[ds.Key][]byte)
for i := 0; i < 10; i++ {
key := ds.NewKey(fmt.Sprintf("/test/%d", i))
bytes := make([]byte, 16)
_, err := rand.Read(bytes)
if err != nil {
t.Fatal(err)
}
data[key] = bytes
err = txn.Put(key, bytes)
if err != nil {
t.Fatal(err)
}
}
err = txn.Commit()
if err != nil {
t.Fatal(err)
}
for key, bytes := range data {
retrieved, err := d.Get(key)
if err != nil {
t.Fatal(err)
}
if len(retrieved) != len(bytes) {
t.Fatal("bytes stored different length from bytes generated")
}
for i, b := range retrieved {
if bytes[i] != b {
t.Fatal("bytes stored different content from bytes generated")
}
}
}
d.Close()
}
func TestTTL(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "testing_badger_")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
d, err := NewDatastore(path, nil)
if err != nil {
t.Fatal(err)
}
txn, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
data := make(map[ds.Key][]byte)
for i := 0; i < 10; i++ {
key := ds.NewKey(fmt.Sprintf("/test/%d", i))
bytes := make([]byte, 16)
_, err := rand.Read(bytes)
if err != nil {
t.Fatal(err)
}
data[key] = bytes
}
// write data
for key, bytes := range data {
err = txn.(ds.TTL).PutWithTTL(key, bytes, time.Second)
if err != nil {
t.Fatal(err)
}
}
err = txn.Commit()
if err != nil {
t.Fatal(err)
}
txn, err = d.NewTransaction(true)
if err != nil {
t.Fatal(err)
}
for key := range data {
_, err := txn.Get(key)
if err != nil {
t.Fatal(err)
}
}
txn.Discard()
time.Sleep(time.Second)
for key := range data {
has, err := d.Has(key)
if err != nil {
t.Fatal(err)
}
if has {
t.Fatal("record with ttl did not expire")
}
}
d.Close()
}
func TestExpirations(t *testing.T) {
var err error
d, done := newDS(t)
defer done()
txn, err := d.NewTransaction(false)
if err != nil {
t.Fatal(err)
}
ttltxn := txn.(ds.TTL)
defer txn.Discard()
key := ds.NewKey("/abc/def")
val := make([]byte, 32)
if n, err := rand.Read(val); n != 32 || err != nil {
t.Fatal("source of randomness failed")
}
ttl := time.Hour
now := time.Now()
tgt := now.Add(ttl)
if err = ttltxn.PutWithTTL(key, val, ttl); err != nil {
t.Fatalf("adding with ttl failed: %v", err)
}
if err = txn.Commit(); err != nil {
t.Fatalf("commiting transaction failed: %v", err)
}
// Second transaction to retrieve expirations.
txn, err = d.NewTransaction(true)
if err != nil {
t.Fatal(err)
}
ttltxn = txn.(ds.TTL)
defer txn.Discard()
// GetExpiration returns expected value.
var dsExp time.Time
if dsExp, err = ttltxn.GetExpiration(key); err != nil {
t.Fatalf("getting expiration failed: %v", err)
} else if tgt.Sub(dsExp) >= 5*time.Second {
t.Fatal("expiration returned by datastore not within the expected range (tolerance: 5 seconds)")
} else if tgt.Sub(dsExp) < 0 {
t.Fatal("expiration returned by datastore was earlier than expected")
}
// Iterator returns expected value.
q := dsq.Query{
ReturnExpirations: true,
KeysOnly: true,
}
var ress dsq.Results
if ress, err = txn.Query(q); err != nil {
t.Fatalf("querying datastore failed: %v", err)
}
defer ress.Close()
if res, ok := ress.NextSync(); !ok {
t.Fatal("expected 1 result in iterator")
} else if res.Expiration != dsExp {
t.Fatalf("expiration returned from iterator differs from GetExpiration, expected: %v, actual: %v", dsExp, res.Expiration)
}
if _, ok := ress.NextSync(); ok {
t.Fatal("expected no more results in iterator")
}
// Datastore->GetExpiration()
if exp, err := d.GetExpiration(key); err != nil {
t.Fatalf("querying datastore failed: %v", err)
} else if exp != dsExp {
t.Fatalf("expiration returned from DB differs from that returned by txn, expected: %v, actual: %v", dsExp, exp)
}
if _, err := d.GetExpiration(ds.NewKey("/foo/bar")); err != ds.ErrNotFound {
t.Fatalf("wrong error type: %v", err)
}
}
func TestSuite(t *testing.T) {
d, done := newDS(t)
defer done()
dstest.SubtestAll(t, d)
}
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg=
github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU=
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ipfs/go-datastore v0.4.0 h1:Kiep/Ll245udr3DEnWBG0c5oofT6Dsin7fubWtFdmsc=
github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8=
github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
github.com/ipfs/go-log v0.0.1 h1:9XTUN/rW64BCG1YhPK9Hoy3q8nr4gOmHHBpgFdfw6Lc=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY=
github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=
github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU=
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8 h1:bspPhN+oKYFk5fcGNuQzp6IGzYQSenLEgH3s6jkXrWw=
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10=
github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7 h1:C2F/nMkR/9sfUTpvR3QrjBuTdvMUC/cFajkphs1YLQo=
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/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-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment