mirror of https://github.com/docker/compose.git
Add default context to the context ls output
This commit is contained in:
parent
eae864ac33
commit
95e07a2134
|
@ -54,11 +54,11 @@ jobs:
|
|||
- name: Install Protoc
|
||||
uses: arduino/setup-protoc@master
|
||||
with:
|
||||
version: '3.9.1'
|
||||
version: "3.9.1"
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
node-version: "10.x"
|
||||
|
||||
- name: E2E Test
|
||||
run: make e2e-local
|
||||
run: make e2e-local
|
||||
|
|
|
@ -31,6 +31,8 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -60,17 +62,43 @@ func runList(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
fmt.Fprintln(w, "NAME\tDESCRIPTION\tTYPE")
|
||||
format := "%s\t%s\t%s\n"
|
||||
sort.Slice(contexts, func(i, j int) bool {
|
||||
return strings.Compare(contexts[i].Name, contexts[j].Name) == -1
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "NAME\tTYPE\tDESCRIPTION\tDOCKER ENPOINT\tKUBERNETES ENDPOINT\tORCHESTRATOR")
|
||||
format := "%s\t%s\t%s\t%s\t%s\t%s\n"
|
||||
|
||||
for _, c := range contexts {
|
||||
contextName := c.Name
|
||||
if c.Name == currentContext {
|
||||
contextName += " *"
|
||||
}
|
||||
fmt.Fprintf(w, format, contextName, c.Metadata.Description, c.Metadata.Type)
|
||||
|
||||
fmt.Fprintf(w,
|
||||
format,
|
||||
contextName,
|
||||
c.Metadata.Type,
|
||||
c.Metadata.Description,
|
||||
getEndpoint("docker", c.Endpoints),
|
||||
getEndpoint("kubernetes", c.Endpoints),
|
||||
c.Metadata.StackOrchestrator)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func getEndpoint(name string, meta map[string]store.Endpoint) string {
|
||||
d, ok := meta[name]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
result := d.Host
|
||||
if d.DefaultNamespace != "" {
|
||||
result += fmt.Sprintf(" (%s)", d.DefaultNamespace)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
apicontext "github.com/docker/api/context"
|
||||
"github.com/docker/api/context/store"
|
||||
)
|
||||
|
||||
type ContextSuite struct {
|
||||
suite.Suite
|
||||
ctx context.Context
|
||||
writer *os.File
|
||||
reader *os.File
|
||||
originalStdout *os.File
|
||||
storeRoot string
|
||||
}
|
||||
|
||||
func (sut *ContextSuite) BeforeTest(suiteName, testName string) {
|
||||
ctx := context.Background()
|
||||
ctx = apicontext.WithCurrentContext(ctx, "example")
|
||||
dir, err := ioutil.TempDir("", "store")
|
||||
require.Nil(sut.T(), err)
|
||||
s, err := store.New(
|
||||
store.WithRoot(dir),
|
||||
)
|
||||
require.Nil(sut.T(), err)
|
||||
|
||||
err = s.Create("example", store.TypedContext{
|
||||
Type: "example",
|
||||
})
|
||||
require.Nil(sut.T(), err)
|
||||
|
||||
sut.storeRoot = dir
|
||||
|
||||
ctx = store.WithContextStore(ctx, s)
|
||||
sut.ctx = ctx
|
||||
|
||||
sut.originalStdout = os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
require.Nil(sut.T(), err)
|
||||
|
||||
os.Stdout = w
|
||||
sut.writer = w
|
||||
sut.reader = r
|
||||
}
|
||||
|
||||
func (sut *ContextSuite) getStdOut() string {
|
||||
err := sut.writer.Close()
|
||||
require.Nil(sut.T(), err)
|
||||
|
||||
out, _ := ioutil.ReadAll(sut.reader)
|
||||
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func (sut *ContextSuite) AfterTest(suiteName, testName string) {
|
||||
os.Stdout = sut.originalStdout
|
||||
err := os.RemoveAll(sut.storeRoot)
|
||||
require.Nil(sut.T(), err)
|
||||
}
|
||||
|
||||
func (sut *ContextSuite) TestLs() {
|
||||
err := runList(sut.ctx)
|
||||
require.Nil(sut.T(), err)
|
||||
golden.Assert(sut.T(), sut.getStdOut(), "ls-out.golden")
|
||||
}
|
||||
|
||||
func TestPs(t *testing.T) {
|
||||
suite.Run(t, new(ContextSuite))
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
NAME TYPE DESCRIPTION DOCKER ENPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||
default docker Current DOCKER_HOST based configuration unix:///var/run/docker.sock https://35.205.93.167 (default) swarm
|
||||
example * example
|
|
@ -54,7 +54,7 @@ func runPs(ctx context.Context, opts psOpts) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', 0)
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintf(w, "CONTAINER ID\tIMAGE\tCOMMAND\tSTATUS\tPORTS\n")
|
||||
format := "%s\t%s\t%s\t%s\t%s\n"
|
||||
for _, c := range containers {
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
apicontext "github.com/docker/api/context"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
CONTAINER ID IMAGE COMMAND STATUS PORTS
|
||||
id nginx
|
||||
1234 alpine
|
||||
CONTAINER ID IMAGE COMMAND STATUS PORTS
|
||||
id nginx
|
||||
1234 alpine
|
||||
|
|
|
@ -104,35 +104,31 @@ func New(opts ...Opt) (Store, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := filepath.Join(home, configDir)
|
||||
if err := createDirIfNotExist(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &store{
|
||||
root: filepath.Join(home, configDir),
|
||||
}
|
||||
if _, err := os.Stat(s.root); os.IsNotExist(err) {
|
||||
if err = os.Mkdir(s.root, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root: root,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
cd := filepath.Join(s.root, contextsDir)
|
||||
if _, err := os.Stat(cd); os.IsNotExist(err) {
|
||||
if err = os.Mkdir(cd, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
m := filepath.Join(cd, metadataDir)
|
||||
if _, err := os.Stat(m); os.IsNotExist(err) {
|
||||
if err = os.Mkdir(m, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := filepath.Join(s.root, contextsDir, metadataDir)
|
||||
if err := createDirIfNotExist(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Get returns the context with the given name
|
||||
func (s *store) Get(name string, getter func() interface{}) (*Metadata, error) {
|
||||
meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
|
||||
meta := filepath.Join(s.root, contextsDir, metadataDir, contextDirOf(name), metaFile)
|
||||
m, err := read(meta, getter)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||
|
@ -150,31 +146,42 @@ func read(meta string, getter func() interface{}) (*Metadata, error) {
|
|||
}
|
||||
|
||||
var um untypedMetadata
|
||||
if err := json.Unmarshal(bytes, &um); err != nil {
|
||||
if err := marshalTyped(bytes, &um); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var uc untypedContext
|
||||
if err := json.Unmarshal(um.Metadata, &uc); err != nil {
|
||||
if err := marshalTyped(um.Metadata, &uc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uc.Type == "" {
|
||||
uc.Type = "docker"
|
||||
}
|
||||
|
||||
data, err := parse(uc.Data, getter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var data interface{}
|
||||
if uc.Data != nil {
|
||||
data, err = parse(uc.Data, getter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Metadata{
|
||||
Name: um.Name,
|
||||
Endpoints: um.Endpoints,
|
||||
Metadata: TypedContext{
|
||||
Description: uc.Description,
|
||||
Type: uc.Type,
|
||||
Data: data,
|
||||
StackOrchestrator: uc.StackOrchestrator,
|
||||
Description: uc.Description,
|
||||
Type: uc.Type,
|
||||
Data: data,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func marshalTyped(in []byte, val interface{}) error {
|
||||
return json.Unmarshal(in, val)
|
||||
}
|
||||
|
||||
func parse(payload []byte, getter func() interface{}) (interface{}, error) {
|
||||
if getter == nil {
|
||||
var res map[string]interface{}
|
||||
|
@ -183,10 +190,12 @@ func parse(payload []byte, getter func() interface{}) (interface{}, error) {
|
|||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
typed := getter()
|
||||
if err := json.Unmarshal(payload, &typed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reflect.ValueOf(typed).Elem().Interface(), nil
|
||||
}
|
||||
|
||||
|
@ -204,7 +213,7 @@ func (s *store) Create(name string, data TypedContext) error {
|
|||
if name == DefaultContextName {
|
||||
return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
|
||||
}
|
||||
dir := contextdirOf(name)
|
||||
dir := contextDirOf(name)
|
||||
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
||||
if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
|
||||
return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
|
||||
|
@ -222,9 +231,9 @@ func (s *store) Create(name string, data TypedContext) error {
|
|||
meta := Metadata{
|
||||
Name: name,
|
||||
Metadata: data,
|
||||
Endpoints: map[string]interface{}{
|
||||
(dockerEndpointKey): dummyContext{},
|
||||
(data.Type): dummyContext{},
|
||||
Endpoints: map[string]Endpoint{
|
||||
(dockerEndpointKey): {},
|
||||
(data.Type): {},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -255,6 +264,12 @@ func (s *store) List() ([]*Metadata, error) {
|
|||
}
|
||||
}
|
||||
|
||||
dockerDefault, err := dockerGefaultContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, dockerDefault)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
@ -262,7 +277,7 @@ func (s *store) Remove(name string) error {
|
|||
if name == DefaultContextName {
|
||||
return errors.Wrap(errdefs.ErrForbidden, objectName(name))
|
||||
}
|
||||
dir := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name))
|
||||
dir := filepath.Join(s.root, contextsDir, metadataDir, contextDirOf(name))
|
||||
// Check if directory exists because os.RemoveAll returns nil if it doesn't
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||
|
@ -273,7 +288,7 @@ func (s *store) Remove(name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func contextdirOf(name string) string {
|
||||
func contextDirOf(name string) string {
|
||||
return digest.FromString(name).Encoded()
|
||||
}
|
||||
|
||||
|
@ -281,32 +296,49 @@ func objectName(name string) string {
|
|||
return fmt.Sprintf("context %q", name)
|
||||
}
|
||||
|
||||
func createDirIfNotExist(dir string) error {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type dummyContext struct{}
|
||||
|
||||
// Endpoint holds the Docker or the Kubernetes endpoint
|
||||
type Endpoint struct {
|
||||
Host string `json:",omitempty"`
|
||||
DefaultNamespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Metadata represents the docker context metadata
|
||||
type Metadata struct {
|
||||
Name string `json:",omitempty"`
|
||||
Metadata TypedContext `json:",omitempty"`
|
||||
Endpoints map[string]interface{} `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Metadata TypedContext `json:",omitempty"`
|
||||
Endpoints map[string]Endpoint `json:",omitempty"`
|
||||
}
|
||||
|
||||
type untypedMetadata struct {
|
||||
Name string `json:",omitempty"`
|
||||
Metadata json.RawMessage `json:",omitempty"`
|
||||
Endpoints map[string]interface{} `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Metadata json.RawMessage `json:",omitempty"`
|
||||
Endpoints map[string]Endpoint `json:",omitempty"`
|
||||
}
|
||||
|
||||
type untypedContext struct {
|
||||
Data json.RawMessage `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Type string `json:",omitempty"`
|
||||
StackOrchestrator string `json:",omitempty"`
|
||||
Type string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Data json.RawMessage `json:",omitempty"`
|
||||
}
|
||||
|
||||
// TypedContext is a context with a type (moby, aci, etc...)
|
||||
type TypedContext struct {
|
||||
Type string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Data interface{} `json:",omitempty"`
|
||||
StackOrchestrator string `json:",omitempty"`
|
||||
Type string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Data interface{} `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AciContext is the context for ACI
|
||||
|
|
|
@ -103,9 +103,10 @@ func (suite *StoreTestSuite) TestList() {
|
|||
contexts, err := suite.store.List()
|
||||
require.Nil(suite.T(), err)
|
||||
|
||||
require.Equal(suite.T(), len(contexts), 2)
|
||||
require.Equal(suite.T(), contexts[0].Name, "test1")
|
||||
require.Equal(suite.T(), contexts[1].Name, "test2")
|
||||
require.Equal(suite.T(), len(contexts), 3)
|
||||
require.Equal(suite.T(), "test1", contexts[0].Name)
|
||||
require.Equal(suite.T(), "test2", contexts[1].Name)
|
||||
require.Equal(suite.T(), "default", contexts[2].Name)
|
||||
}
|
||||
|
||||
func (suite *StoreTestSuite) TestRemoveNotFound() {
|
||||
|
@ -119,13 +120,15 @@ func (suite *StoreTestSuite) TestRemove() {
|
|||
require.Nil(suite.T(), err)
|
||||
contexts, err := suite.store.List()
|
||||
require.Nil(suite.T(), err)
|
||||
require.Equal(suite.T(), len(contexts), 1)
|
||||
require.Equal(suite.T(), len(contexts), 2)
|
||||
|
||||
err = suite.store.Remove("testremove")
|
||||
require.Nil(suite.T(), err)
|
||||
contexts, err = suite.store.List()
|
||||
require.Nil(suite.T(), err)
|
||||
require.Equal(suite.T(), len(contexts), 0)
|
||||
// The default context is always here, that's why we
|
||||
// have len(contexts) == 1
|
||||
require.Equal(suite.T(), len(contexts), 1)
|
||||
}
|
||||
|
||||
func TestExampleTestSuite(t *testing.T) {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Represents a context as created by the docker cli
|
||||
type defaultContext struct {
|
||||
Metadata TypedContext
|
||||
Endpoints endpoints
|
||||
}
|
||||
|
||||
// Normally (in docker/cli code), the endpoints are mapped as map[string]interface{}
|
||||
// but docker cli contexts always have a "docker" and "kubernetes" key so we
|
||||
// create real types for those to no have to juggle around with interfaces.
|
||||
type endpoints struct {
|
||||
Docker endpoint `json:"docker,omitempty"`
|
||||
Kubernetes endpoint `json:"kubernetes,omitempty"`
|
||||
}
|
||||
|
||||
// Both "docker" and "kubernetes" endpoints in the docker cli created contexts
|
||||
// have a "Host", only kubernetes has the "DefaultNamespace", we put both of
|
||||
// those here for easier manipulation and to not have to create two distinct
|
||||
// structs
|
||||
type endpoint struct {
|
||||
Host string
|
||||
DefaultNamespace string
|
||||
}
|
||||
|
||||
func dockerGefaultContext() (*Metadata, error) {
|
||||
cmd := exec.Command("docker", "context", "inspect", "default")
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ctx []defaultContext
|
||||
err = json.Unmarshal(stdout.Bytes(), &ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ctx) != 1 {
|
||||
return nil, errors.New("found more than one default context")
|
||||
}
|
||||
|
||||
defaultCtx := ctx[0]
|
||||
|
||||
meta := Metadata{
|
||||
Name: "default",
|
||||
Endpoints: map[string]Endpoint{
|
||||
"docker": {
|
||||
Host: defaultCtx.Endpoints.Docker.Host,
|
||||
},
|
||||
"kubernetes": {
|
||||
Host: defaultCtx.Endpoints.Kubernetes.Host,
|
||||
DefaultNamespace: defaultCtx.Endpoints.Kubernetes.DefaultNamespace,
|
||||
},
|
||||
},
|
||||
Metadata: TypedContext{
|
||||
Description: "Current DOCKER_HOST based configuration",
|
||||
Type: "docker",
|
||||
StackOrchestrator: defaultCtx.Metadata.StackOrchestrator,
|
||||
Data: defaultCtx.Metadata,
|
||||
},
|
||||
}
|
||||
|
||||
return &meta, nil
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultContext(t *testing.T) {
|
||||
s, err := dockerGefaultContext()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "default", s.Name)
|
||||
}
|
1
go.mod
1
go.mod
|
@ -40,6 +40,7 @@ require (
|
|||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.21.0
|
||||
|
|
5
go.sum
5
go.sum
|
@ -66,6 +66,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
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=
|
||||
|
@ -217,7 +218,9 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||
github.com/robpike/filter v0.0.0-20150108201509-2984852a2183 h1:qDhD/wJDGyWrXKLIKmEKpKK/ejaZlguyeEaLZzmrtzo=
|
||||
github.com/robpike/filter v0.0.0-20150108201509-2984852a2183/go.mod h1:3dvYi47BCPInRb2ILlNnrXfl++XpwTWLbIxPyJsUvCw=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
|
@ -301,6 +304,8 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
Loading…
Reference in New Issue