Add first version of protocol utilities
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
67a02df187
commit
576c78e6dd
5 changed files with 450 additions and 0 deletions
239
bootserver/bootprotocol/bootprotocol.go
Normal file
239
bootserver/bootprotocol/bootprotocol.go
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
// package bootprotocol contains the elements necessary to use the custom network boot protocol
|
||||||
|
package bootprotocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Action int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ActionRequest Action = iota
|
||||||
|
ActionAccept
|
||||||
|
ActionDeny
|
||||||
|
ActionUnknown
|
||||||
|
)
|
||||||
|
|
||||||
|
var spaceByte = []byte(" ")
|
||||||
|
var commaByte = []byte(",")
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyID = "id"
|
||||||
|
keyEfiApp = "efi_app"
|
||||||
|
keyReason = "reason"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidFormat = errors.New("invalid format for message")
|
||||||
|
var ErrUnknownAction = errors.New("unknown action for message")
|
||||||
|
var ErrInvalidParam = errors.New("invalid parameter for message")
|
||||||
|
var ErrMissingParam = errors.New("missing parameter for message")
|
||||||
|
|
||||||
|
func (a Action) String() string {
|
||||||
|
switch a {
|
||||||
|
case ActionAccept:
|
||||||
|
return "BOOT_ACCEPT"
|
||||||
|
case ActionDeny:
|
||||||
|
return "BOOT_DENY"
|
||||||
|
case ActionRequest:
|
||||||
|
return "BOOT_REQUEST"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newActionFromBytes(raw []byte) Action {
|
||||||
|
switch string(raw) {
|
||||||
|
case "BOOT_ACCEPT":
|
||||||
|
return ActionAccept
|
||||||
|
case "BOOT_DENY":
|
||||||
|
return ActionDeny
|
||||||
|
case "BOOT_REQUEST":
|
||||||
|
return ActionRequest
|
||||||
|
default:
|
||||||
|
return ActionUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message interface {
|
||||||
|
encoding.BinaryUnmarshaler
|
||||||
|
encoding.BinaryMarshaler
|
||||||
|
Action() Action
|
||||||
|
ID() uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestMessage struct {
|
||||||
|
id uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *requestMessage) UnmarshalBinary(data []byte) error {
|
||||||
|
params := bytes.Split(data, commaByte)
|
||||||
|
for _, p := range params {
|
||||||
|
k, v, err := splitKeyValue(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse parameter %q: %w", string(p), err)
|
||||||
|
}
|
||||||
|
if bytes.Equal(k, []byte(keyID)) {
|
||||||
|
parsedId, err := uuid.ParseBytes(v)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidParam
|
||||||
|
}
|
||||||
|
rm.id = parsedId
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrMissingParam
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *requestMessage) MarshalBinary() (data []byte, err error) {
|
||||||
|
action := []byte(rm.Action().String())
|
||||||
|
params := []byte(fmt.Sprintf("%s=%s", keyID, rm.id.String()))
|
||||||
|
return bytes.Join([][]byte{action, params}, spaceByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *requestMessage) Action() Action {
|
||||||
|
return ActionRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *requestMessage) ID() uuid.UUID {
|
||||||
|
return rm.id
|
||||||
|
}
|
||||||
|
|
||||||
|
type acceptMessage struct {
|
||||||
|
id uuid.UUID
|
||||||
|
efiApp string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *acceptMessage) UnmarshalBinary(data []byte) error {
|
||||||
|
params := bytes.Split(data, commaByte)
|
||||||
|
for _, p := range params {
|
||||||
|
k, v, err := splitKeyValue(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse parameter %q: %w", string(p), err)
|
||||||
|
}
|
||||||
|
switch string(k) {
|
||||||
|
case keyID:
|
||||||
|
parsedId, err := uuid.ParseBytes(v)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidParam
|
||||||
|
}
|
||||||
|
am.id = parsedId
|
||||||
|
case keyEfiApp:
|
||||||
|
am.efiApp = string(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if am.id == uuid.Nil || am.efiApp == "" {
|
||||||
|
return ErrMissingParam
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *acceptMessage) MarshalBinary() (data []byte, err error) {
|
||||||
|
action := []byte(am.Action().String())
|
||||||
|
params := [][]byte{
|
||||||
|
[]byte(fmt.Sprintf("%s=%s", keyID, am.id.String())),
|
||||||
|
[]byte(fmt.Sprintf("%s=%s", keyEfiApp, am.efiApp)),
|
||||||
|
}
|
||||||
|
param_bytes := bytes.Join(params, commaByte)
|
||||||
|
return bytes.Join([][]byte{action, param_bytes}, spaceByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *acceptMessage) Action() Action {
|
||||||
|
return ActionAccept
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *acceptMessage) ID() uuid.UUID {
|
||||||
|
return am.id
|
||||||
|
}
|
||||||
|
|
||||||
|
type denyMessage struct {
|
||||||
|
id uuid.UUID
|
||||||
|
reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *denyMessage) UnmarshalBinary(data []byte) error {
|
||||||
|
params := bytes.Split(data, commaByte)
|
||||||
|
for _, p := range params {
|
||||||
|
k, v, err := splitKeyValue(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse parameter %q: %w", string(p), err)
|
||||||
|
}
|
||||||
|
switch string(k) {
|
||||||
|
case keyID:
|
||||||
|
parsedId, err := uuid.ParseBytes(v)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidParam
|
||||||
|
}
|
||||||
|
dm.id = parsedId
|
||||||
|
case keyReason:
|
||||||
|
dm.reason = string(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dm.id == uuid.Nil || dm.reason == "" {
|
||||||
|
return ErrMissingParam
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *denyMessage) MarshalBinary() (data []byte, err error) {
|
||||||
|
action := []byte(dm.Action().String())
|
||||||
|
params := [][]byte{
|
||||||
|
[]byte(fmt.Sprintf("%s=%s", keyID, dm.id.String())),
|
||||||
|
[]byte(fmt.Sprintf("%s=%s", keyReason, dm.reason)),
|
||||||
|
}
|
||||||
|
param_bytes := bytes.Join(params, commaByte)
|
||||||
|
return bytes.Join([][]byte{action, param_bytes}, spaceByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *denyMessage) Action() Action {
|
||||||
|
return ActionDeny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *denyMessage) ID() uuid.UUID {
|
||||||
|
return dm.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func MessageFromBytes(dat []byte) (Message, error) {
|
||||||
|
rawAction, content, found := bytes.Cut(dat, spaceByte)
|
||||||
|
if !found {
|
||||||
|
return nil, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
var message Message
|
||||||
|
action := newActionFromBytes(rawAction)
|
||||||
|
switch action {
|
||||||
|
case ActionRequest:
|
||||||
|
message = &requestMessage{}
|
||||||
|
case ActionAccept:
|
||||||
|
message = &acceptMessage{}
|
||||||
|
case ActionDeny:
|
||||||
|
message = &denyMessage{}
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownAction
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := message.UnmarshalBinary(content); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse message: %w", err)
|
||||||
|
}
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Accept(id uuid.UUID, efiApp string) Message {
|
||||||
|
return &acceptMessage{
|
||||||
|
id: id,
|
||||||
|
efiApp: efiApp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Deny(id uuid.UUID, reason string) Message {
|
||||||
|
return &denyMessage{
|
||||||
|
id: id,
|
||||||
|
reason: reason,
|
||||||
|
}
|
||||||
|
}
|
195
bootserver/bootprotocol/bootprotocol_test.go
Normal file
195
bootserver/bootprotocol/bootprotocol_test.go
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
package bootprotocol_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.faercol.me/faercol/http-boot-server/bootserver/bootprotocol"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRequestMessage(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Message OK", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id=%s", bootprotocol.ActionRequest.String(), id.String())
|
||||||
|
parsedMsg, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.NoError(t, err, "failed to generate message from bytes")
|
||||||
|
|
||||||
|
require.Equal(t, id, parsedMsg.ID())
|
||||||
|
require.Equal(t, bootprotocol.ActionRequest, parsedMsg.Action())
|
||||||
|
|
||||||
|
parsedMsgBytes, err := parsedMsg.MarshalBinary()
|
||||||
|
require.NoError(t, err, "failed to generate bytes from parsed message")
|
||||||
|
require.Equal(t, msgStr, string(parsedMsgBytes))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err no parameters", func(t *testing.T) {
|
||||||
|
msgStr := bootprotocol.ActionRequest.String()
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidFormat)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid parameter format", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id:%s", bootprotocol.ActionRequest.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong key", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s uuid=%s", bootprotocol.ActionRequest.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrMissingParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong format", func(t *testing.T) {
|
||||||
|
msgStr := fmt.Sprintf("%s id=toto", bootprotocol.ActionRequest.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcceptMessage(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Message OK", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id=%s,efi_app=toto.efi", bootprotocol.ActionAccept.String(), id.String())
|
||||||
|
parsedMsg, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.NoError(t, err, "failed to generate message from bytes")
|
||||||
|
|
||||||
|
require.Equal(t, id, parsedMsg.ID())
|
||||||
|
require.Equal(t, bootprotocol.ActionAccept, parsedMsg.Action())
|
||||||
|
|
||||||
|
parsedMsgBytes, err := parsedMsg.MarshalBinary()
|
||||||
|
require.NoError(t, err, "failed to generate bytes from parsed message")
|
||||||
|
require.Equal(t, msgStr, string(parsedMsgBytes))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err no parameters", func(t *testing.T) {
|
||||||
|
msgStr := bootprotocol.ActionAccept.String()
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidFormat)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid parameter format", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id:%s", bootprotocol.ActionAccept.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong key", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s uuid=%s,efi_app=toto.efi", bootprotocol.ActionAccept.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrMissingParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong format", func(t *testing.T) {
|
||||||
|
msgStr := fmt.Sprintf("%s id=toto,efi_app=toto.efi", bootprotocol.ActionAccept.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid efi_app wrong key", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id=%s,uefi_app=toto.efi", bootprotocol.ActionAccept.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrMissingParam)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDenyMessage(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Message OK", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id=%s,reason=lol", bootprotocol.ActionDeny.String(), id.String())
|
||||||
|
parsedMsg, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.NoError(t, err, "failed to generate message from bytes")
|
||||||
|
|
||||||
|
require.Equal(t, id, parsedMsg.ID())
|
||||||
|
require.Equal(t, bootprotocol.ActionDeny, parsedMsg.Action())
|
||||||
|
|
||||||
|
parsedMsgBytes, err := parsedMsg.MarshalBinary()
|
||||||
|
require.NoError(t, err, "failed to generate bytes from parsed message")
|
||||||
|
require.Equal(t, msgStr, string(parsedMsgBytes))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err no parameters", func(t *testing.T) {
|
||||||
|
msgStr := bootprotocol.ActionDeny.String()
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidFormat)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid parameter format", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id:%s", bootprotocol.ActionDeny.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong key", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s uuid=%s,reason=lol", bootprotocol.ActionDeny.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrMissingParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid id wrong format", func(t *testing.T) {
|
||||||
|
msgStr := fmt.Sprintf("%s id=toto,reason=lol", bootprotocol.ActionDeny.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrInvalidParam)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Err invalid reason wrong key", func(t *testing.T) {
|
||||||
|
id := uuid.New()
|
||||||
|
msgStr := fmt.Sprintf("%s id=%s,no_reason=lol", bootprotocol.ActionDeny.String(), id.String())
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrMissingParam)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownAction(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
msgStr := "PLOP param1=toto"
|
||||||
|
_, err := bootprotocol.MessageFromBytes([]byte(msgStr))
|
||||||
|
require.ErrorIs(t, err, bootprotocol.ErrUnknownAction)
|
||||||
|
require.Equal(t, bootprotocol.ActionUnknown.String(), "unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccept(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
id := uuid.New()
|
||||||
|
efiApp := "toto.efi"
|
||||||
|
msg := bootprotocol.Accept(id, efiApp)
|
||||||
|
|
||||||
|
require.Equal(t, id, msg.ID())
|
||||||
|
require.Equal(t, bootprotocol.ActionAccept, msg.Action())
|
||||||
|
|
||||||
|
msgBytes, err := msg.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, string(msgBytes), efiApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeny(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
id := uuid.New()
|
||||||
|
reason := "lolnope"
|
||||||
|
msg := bootprotocol.Deny(id, reason)
|
||||||
|
|
||||||
|
require.Equal(t, id, msg.ID())
|
||||||
|
require.Equal(t, bootprotocol.ActionDeny, msg.Action())
|
||||||
|
|
||||||
|
msgBytes, err := msg.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, string(msgBytes), reason)
|
||||||
|
}
|
13
bootserver/bootprotocol/helpers.go
Normal file
13
bootserver/bootprotocol/helpers.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package bootprotocol
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
var equalByte = []byte("=")
|
||||||
|
|
||||||
|
func splitKeyValue(param []byte) (key []byte, value []byte, err error) {
|
||||||
|
splitted := bytes.Split(param, equalByte)
|
||||||
|
if len(splitted) != 2 {
|
||||||
|
return nil, nil, ErrInvalidParam
|
||||||
|
}
|
||||||
|
return splitted[0], splitted[1], nil
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ module git.faercol.me/faercol/http-boot-server/bootserver
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
|
Loading…
Reference in a new issue