Replace Portainer struct copies with original ones wherever possible

This commit is contained in:
Juan Carlos Mejías Rodríguez 2019-08-10 12:14:55 -04:00
parent bffadc245c
commit 0b887b414b
10 changed files with 80 additions and 106 deletions

View File

@ -9,11 +9,13 @@ import (
"io/ioutil"
"net/http"
"net/url"
portainer "github.com/portainer/portainer/api"
)
type StackListFilter struct {
SwarmId string `json:",omitempty"`
EndpointId uint32 `json:",omitempty"`
EndpointId portainer.EndpointID `json:",omitempty"`
}
type Config struct {
@ -30,34 +32,34 @@ type PortainerClient interface {
Authenticate() (token string, err error)
// Get endpoints
GetEndpoints() ([]EndpointSubset, error)
GetEndpoints() ([]portainer.Endpoint, error)
// Get endpoint groups
GetEndpointGroups() ([]EndpointGroup, error)
GetEndpointGroups() ([]portainer.EndpointGroup, error)
// Get stacks, optionally filtered by swarmId and endpointId
GetStacks(swarmId string, endpointId uint32) ([]Stack, error)
GetStacks(swarmId string, endpointId portainer.EndpointID) ([]portainer.Stack, error)
// Create swarm stack
CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId uint32) (stack Stack, err error)
CreateSwarmStack(stackName string, environmentVariables []portainer.Pair, stackFileContent string, swarmClusterId string, endpointId portainer.EndpointID) (stack portainer.Stack, err error)
// Create compose stack
CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId uint32) (stack Stack, err error)
CreateComposeStack(stackName string, environmentVariables []portainer.Pair, stackFileContent string, endpointId portainer.EndpointID) (stack portainer.Stack, err error)
// Update stack
UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId uint32) error
UpdateStack(stack portainer.Stack, environmentVariables []portainer.Pair, stackFileContent string, prune bool, endpointId portainer.EndpointID) error
// Delete stack
DeleteStack(stackId uint32) error
DeleteStack(stackId portainer.StackID) error
// Get stack file content
GetStackFileContent(stackId uint32) (content string, err error)
GetStackFileContent(stackId portainer.StackID) (content string, err error)
// Get endpoint Docker info
GetEndpointDockerInfo(endpointId uint32) (info map[string]interface{}, err error)
GetEndpointDockerInfo(endpointId portainer.EndpointID) (info map[string]interface{}, err error)
// Get Portainer status info
GetStatus() (Status, error)
GetStatus() (portainer.Status, error)
// Run a function before sending a request to Portainer
BeforeRequest(hook func(req *http.Request) (err error))
@ -218,17 +220,17 @@ func (n *portainerClientImp) Authenticate() (token string, err error) {
return
}
func (n *portainerClientImp) GetEndpoints() (endpoints []EndpointSubset, err error) {
func (n *portainerClientImp) GetEndpoints() (endpoints []portainer.Endpoint, err error) {
err = n.doJSON("endpoints", http.MethodGet, nil, &endpoints)
return
}
func (n *portainerClientImp) GetEndpointGroups() (endpointGroups []EndpointGroup, err error) {
func (n *portainerClientImp) GetEndpointGroups() (endpointGroups []portainer.EndpointGroup, err error) {
err = n.doJSON("endpoint_groups", http.MethodGet, nil, &endpointGroups)
return
}
func (n *portainerClientImp) GetStacks(swarmId string, endpointId uint32) (stacks []Stack, err error) {
func (n *portainerClientImp) GetStacks(swarmId string, endpointId portainer.EndpointID) (stacks []portainer.Stack, err error) {
filter := StackListFilter{
SwarmId: swarmId,
EndpointId: endpointId,
@ -241,7 +243,7 @@ func (n *portainerClientImp) GetStacks(swarmId string, endpointId uint32) (stack
return
}
func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId uint32) (stack Stack, err error) {
func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVariables []portainer.Pair, stackFileContent string, swarmClusterId string, endpointId portainer.EndpointID) (stack portainer.Stack, err error) {
reqBody := StackCreateRequest{
Name: stackName,
Env: environmentVariables,
@ -253,7 +255,7 @@ func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVaria
return
}
func (n *portainerClientImp) CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId uint32) (stack Stack, err error) {
func (n *portainerClientImp) CreateComposeStack(stackName string, environmentVariables []portainer.Pair, stackFileContent string, endpointId portainer.EndpointID) (stack portainer.Stack, err error) {
reqBody := StackCreateRequest{
Name: stackName,
Env: environmentVariables,
@ -264,23 +266,23 @@ func (n *portainerClientImp) CreateComposeStack(stackName string, environmentVar
return
}
func (n *portainerClientImp) UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId uint32) (err error) {
func (n *portainerClientImp) UpdateStack(stack portainer.Stack, environmentVariables []portainer.Pair, stackFileContent string, prune bool, endpointId portainer.EndpointID) (err error) {
reqBody := StackUpdateRequest{
Env: environmentVariables,
StackFileContent: stackFileContent,
Prune: prune,
}
err = n.doJSON(fmt.Sprintf("stacks/%v?endpointId=%v", stack.Id, endpointId), http.MethodPut, &reqBody, nil)
err = n.doJSON(fmt.Sprintf("stacks/%v?endpointId=%v", stack.ID, endpointId), http.MethodPut, &reqBody, nil)
return
}
func (n *portainerClientImp) DeleteStack(stackId uint32) (err error) {
func (n *portainerClientImp) DeleteStack(stackId portainer.StackID) (err error) {
err = n.doJSON(fmt.Sprintf("stacks/%d", stackId), http.MethodDelete, nil, nil)
return
}
func (n *portainerClientImp) GetStackFileContent(stackId uint32) (content string, err error) {
func (n *portainerClientImp) GetStackFileContent(stackId portainer.StackID) (content string, err error) {
var respBody StackFileInspectResponse
err = n.doJSON(fmt.Sprintf("stacks/%v/file", stackId), http.MethodGet, nil, &respBody)
@ -293,12 +295,12 @@ func (n *portainerClientImp) GetStackFileContent(stackId uint32) (content string
return
}
func (n *portainerClientImp) GetEndpointDockerInfo(endpointId uint32) (info map[string]interface{}, err error) {
func (n *portainerClientImp) GetEndpointDockerInfo(endpointId portainer.EndpointID) (info map[string]interface{}, err error) {
err = n.doJSON(fmt.Sprintf("endpoints/%v/docker/info", endpointId), http.MethodGet, nil, &info)
return
}
func (n *portainerClientImp) GetStatus() (status Status, err error) {
func (n *portainerClientImp) GetStatus() (status portainer.Status, err error) {
err = n.doJSON("status", http.MethodGet, nil, &status)
return
}

View File

@ -1,18 +1,12 @@
package client
import "fmt"
import (
"fmt"
type Stack struct {
// In the API documentation this field is a String,
// but it's returned as a number
Id uint32
Name string
Type uint8 // 1 for a Swarm stack, 2 for a Compose stack
EndpointID uint
Env []StackEnv
}
portainer "github.com/portainer/portainer/api"
)
func (s *Stack) GetTranslatedStackType() string {
func GetTranslatedStackType(s portainer.Stack) string {
switch s.Type {
case 1:
return "swarm"
@ -23,36 +17,16 @@ func (s *Stack) GetTranslatedStackType() string {
}
}
type StackEnv struct {
Name string `json:"name"`
Value string `json:"value"`
}
type EndpointSubset struct {
Id uint32
Name string
Type uint8
URL string
PublicURL string
GroupID uint32
}
type EndpointGroup struct {
Id uint32
Name string
Description string
}
type StackCreateRequest struct {
Name string
SwarmID string
StackFileContent string
Env []StackEnv `json:",omitempty"`
Env []portainer.Pair `json:",omitempty"`
}
type StackUpdateRequest struct {
StackFileContent string
Env []StackEnv `json:",omitempty"`
Env []portainer.Pair `json:",omitempty"`
Prune bool
}
@ -60,13 +34,6 @@ type StackFileInspectResponse struct {
StackFileContent string
}
type Status struct {
Authentication bool
EndpointManagement bool
Analytics bool
Version string
}
type GenericError struct {
Err string
Details string

View File

@ -45,7 +45,7 @@ var endpointGroupListCmd = &cobra.Command{
for _, g := range endpointGroups {
_, err := fmt.Fprintln(writer, fmt.Sprintf(
"%v\t%s\t%s",
g.Id,
g.ID,
g.Name,
g.Description,
))

View File

@ -70,7 +70,7 @@ var endpointListCmd = &cobra.Command{
}
_, err := fmt.Fprintln(writer, fmt.Sprintf(
"%v\t%s\t%v\t%s\t%s\t%v",
e.Id,
e.ID,
e.Name,
endpointType,
e.URL,

View File

@ -3,7 +3,8 @@ package cmd
import (
"io/ioutil"
"github.com/greenled/portainer-stack-utils/client"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/greenled/portainer-stack-utils/common"
@ -20,7 +21,7 @@ var stackDeployCmd = &cobra.Command{
Example: "psu stack deploy mystack --stack-file mystack.yml",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var loadedEnvironmentVariables []client.StackEnv
var loadedEnvironmentVariables []portainer.Pair
if viper.GetString("stack.deploy.env-file") != "" {
var loadingErr error
loadedEnvironmentVariables, loadingErr = loadEnvironmentVariablesFile(viper.GetString("stack.deploy.env-file"))
@ -31,7 +32,7 @@ var stackDeployCmd = &cobra.Command{
common.CheckError(clientRetrievalErr)
stackName := args[0]
endpointId := viper.GetInt32("stack.deploy.endpoint")
endpointId := portainer.EndpointID(viper.GetInt("stack.deploy.endpoint"))
// Guess EndpointID if not set
if endpointId == 0 {
@ -40,13 +41,13 @@ var stackDeployCmd = &cobra.Command{
}).Warning("Endpoint ID not set")
endpoint, err := common.GetDefaultEndpoint()
common.CheckError(err)
endpointId = int32(endpoint.Id)
endpointId = endpoint.ID
logrus.WithFields(logrus.Fields{
"endpoint": endpointId,
}).Debug("Using the only available endpoint")
}
endpointSwarmClusterId, selectionErr := common.GetEndpointSwarmClusterId(uint32(endpointId))
endpointSwarmClusterId, selectionErr := common.GetEndpointSwarmClusterId(endpointId)
switch selectionErr.(type) {
case nil:
// It's a swarm cluster
@ -61,7 +62,7 @@ var stackDeployCmd = &cobra.Command{
"stack": stackName,
"endpoint": endpointId,
}).Debug("Getting stack")
retrievedStack, stackRetrievalErr := common.GetStackByName(stackName, endpointSwarmClusterId, uint32(endpointId))
retrievedStack, stackRetrievalErr := common.GetStackByName(stackName, endpointSwarmClusterId, endpointId)
switch stackRetrievalErr.(type) {
case nil:
// We are updating an existing stack
@ -79,11 +80,11 @@ var stackDeployCmd = &cobra.Command{
logrus.WithFields(logrus.Fields{
"stack": retrievedStack.Name,
}).Debug("Getting stack file content")
stackFileContent, stackFileContentRetrievalErr = portainerClient.GetStackFileContent(retrievedStack.Id)
stackFileContent, stackFileContentRetrievalErr = portainerClient.GetStackFileContent(retrievedStack.ID)
common.CheckError(stackFileContentRetrievalErr)
}
var newEnvironmentVariables []client.StackEnv
var newEnvironmentVariables []portainer.Pair
if viper.GetBool("stack.deploy.replace-env") {
newEnvironmentVariables = loadedEnvironmentVariables
} else {
@ -97,7 +98,7 @@ var stackDeployCmd = &cobra.Command{
continue LoadedVariablesLoop
}
}
newEnvironmentVariables = append(newEnvironmentVariables, client.StackEnv{
newEnvironmentVariables = append(newEnvironmentVariables, portainer.Pair{
Name: loadedEnvironmentVariable.Name,
Value: loadedEnvironmentVariable.Value,
})
@ -107,7 +108,7 @@ var stackDeployCmd = &cobra.Command{
logrus.WithFields(logrus.Fields{
"stack": retrievedStack.Name,
}).Info("Updating stack")
err := portainerClient.UpdateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune"), uint32(endpointId))
err := portainerClient.UpdateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune"), endpointId)
common.CheckError(err)
case *common.StackNotFoundError:
// We are deploying a new stack
@ -127,12 +128,12 @@ var stackDeployCmd = &cobra.Command{
"stack": stackName,
"endpoint": endpointId,
}).Info("Creating stack")
stack, deploymentErr := portainerClient.CreateSwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, endpointSwarmClusterId, uint32(endpointId))
stack, deploymentErr := portainerClient.CreateSwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, endpointSwarmClusterId, endpointId)
common.CheckError(deploymentErr)
logrus.WithFields(logrus.Fields{
"stack": stack.Name,
"endpoint": stack.EndpointID,
"id": stack.Id,
"id": stack.ID,
}).Info("Stack created")
} else {
// It's not a swarm cluster
@ -140,12 +141,12 @@ var stackDeployCmd = &cobra.Command{
"stack": stackName,
"endpoint": endpointId,
}).Info("Creating stack")
stack, deploymentErr := portainerClient.CreateComposeStack(stackName, loadedEnvironmentVariables, stackFileContent, uint32(endpointId))
stack, deploymentErr := portainerClient.CreateComposeStack(stackName, loadedEnvironmentVariables, stackFileContent, endpointId)
common.CheckError(deploymentErr)
logrus.WithFields(logrus.Fields{
"stack": stack.Name,
"endpoint": stack.EndpointID,
"id": stack.Id,
"id": stack.ID,
}).Info("Stack created")
}
default:
@ -159,7 +160,7 @@ func init() {
stackCmd.AddCommand(stackDeployCmd)
stackDeployCmd.Flags().StringP("stack-file", "c", "", "Path to a file with the content of the stack.")
stackDeployCmd.Flags().Uint32("endpoint", 0, "Endpoint ID.")
stackDeployCmd.Flags().Int("endpoint", 0, "Endpoint ID.")
stackDeployCmd.Flags().StringP("env-file", "e", "", "Path to a file with environment variables used during stack deployment.")
stackDeployCmd.Flags().Bool("replace-env", false, "Replace environment variables instead of merging them.")
stackDeployCmd.Flags().BoolP("prune", "r", false, "Prune services that are no longer referenced (only available for Swarm stacks).")
@ -179,15 +180,15 @@ func loadStackFile(path string) (string, error) {
}
// Load environment variables
func loadEnvironmentVariablesFile(path string) ([]client.StackEnv, error) {
var variables []client.StackEnv
func loadEnvironmentVariablesFile(path string) ([]portainer.Pair, error) {
var variables []portainer.Pair
variablesMap, readingErr := godotenv.Read(path)
if readingErr != nil {
return []client.StackEnv{}, readingErr
return []portainer.Pair{}, readingErr
}
for key, value := range variablesMap {
variables = append(variables, client.StackEnv{
variables = append(variables, portainer.Pair{
Name: key,
Value: value,
})

View File

@ -7,6 +7,8 @@ import (
"github.com/greenled/portainer-stack-utils/client"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/greenled/portainer-stack-utils/common"
@ -24,26 +26,26 @@ var stackListCmd = &cobra.Command{
portainerClient, err := common.GetClient()
common.CheckError(err)
endpointId := viper.GetInt32("stack.list.endpoint")
endpointId := portainer.EndpointID(viper.GetInt("stack.list.endpoint"))
var endpointSwarmClusterId string
var stacks []client.Stack
var stacks []portainer.Stack
if endpointId != 0 {
var selectionErr error
endpointSwarmClusterId, selectionErr = common.GetEndpointSwarmClusterId(uint32(endpointId))
endpointSwarmClusterId, selectionErr = common.GetEndpointSwarmClusterId(endpointId)
switch selectionErr.(type) {
case nil:
// It's a swarm cluster
logrus.WithFields(logrus.Fields{
"endpoint": endpointId,
}).Debug("Getting stacks")
stacks, err = portainerClient.GetStacks(endpointSwarmClusterId, uint32(endpointId))
stacks, err = portainerClient.GetStacks(endpointSwarmClusterId, endpointId)
common.CheckError(err)
case *common.StackClusterNotFoundError:
// It's not a swarm cluster
logrus.WithFields(logrus.Fields{
"endpoint": endpointId,
}).Debug("Getting stacks")
stacks, err = portainerClient.GetStacks("", uint32(endpointId))
stacks, err = portainerClient.GetStacks("", endpointId)
common.CheckError(err)
default:
// Something else happened
@ -76,9 +78,9 @@ var stackListCmd = &cobra.Command{
for _, s := range stacks {
_, err := fmt.Fprintln(writer, fmt.Sprintf(
"%v\t%s\t%v\t%v",
s.Id,
s.ID,
s.Name,
s.GetTranslatedStackType(),
client.GetTranslatedStackType(s),
s.EndpointID,
))
common.CheckError(err)
@ -92,7 +94,7 @@ var stackListCmd = &cobra.Command{
func init() {
stackCmd.AddCommand(stackListCmd)
stackListCmd.Flags().Uint32("endpoint", 0, "Filter by endpoint ID.")
stackListCmd.Flags().Int("endpoint", 0, "Filter by endpoint ID.")
stackListCmd.Flags().String("format", "", "Format output using a Go template.")
viper.BindPFlag("stack.list.endpoint", stackListCmd.Flags().Lookup("endpoint"))
viper.BindPFlag("stack.list.format", stackListCmd.Flags().Lookup("format"))

View File

@ -3,8 +3,8 @@ package cmd
import (
"fmt"
"github.com/greenled/portainer-stack-utils/client"
"github.com/greenled/portainer-stack-utils/common"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -22,9 +22,9 @@ var stackRemoveCmd = &cobra.Command{
common.CheckError(clientRetrievalErr)
stackName := args[0]
endpointId := viper.GetInt32("stack.remove.endpoint")
endpointId := portainer.EndpointID(viper.GetInt("stack.remove.endpoint"))
var endpointSwarmClusterId string
var stack client.Stack
var stack portainer.Stack
// Guess EndpointID if not set
if endpointId == 0 {
@ -33,14 +33,14 @@ var stackRemoveCmd = &cobra.Command{
}).Warning("Endpoint ID not set")
endpoint, err := common.GetDefaultEndpoint()
common.CheckError(err)
endpointId = int32(endpoint.Id)
endpointId = endpoint.ID
logrus.WithFields(logrus.Fields{
"endpoint": endpointId,
}).Debug("Using the only available endpoint")
}
var selectionErr, stackRetrievalErr error
endpointSwarmClusterId, selectionErr = common.GetEndpointSwarmClusterId(uint32(endpointId))
endpointSwarmClusterId, selectionErr = common.GetEndpointSwarmClusterId(endpointId)
switch selectionErr.(type) {
case nil:
// It's a swarm cluster
@ -48,14 +48,14 @@ var stackRemoveCmd = &cobra.Command{
"stack": stackName,
"endpoint": endpointId,
}).Debug("Getting stack")
stack, stackRetrievalErr = common.GetStackByName(stackName, endpointSwarmClusterId, uint32(endpointId))
stack, stackRetrievalErr = common.GetStackByName(stackName, endpointSwarmClusterId, endpointId)
case *common.StackClusterNotFoundError:
// It's not a swarm cluster
logrus.WithFields(logrus.Fields{
"stack": stackName,
"endpoint": endpointId,
}).Debug("Getting stack")
stack, stackRetrievalErr = common.GetStackByName(stackName, "", uint32(endpointId))
stack, stackRetrievalErr = common.GetStackByName(stackName, "", endpointId)
default:
// Something else happened
common.CheckError(selectionErr)
@ -64,7 +64,7 @@ var stackRemoveCmd = &cobra.Command{
switch stackRetrievalErr.(type) {
case nil:
// The stack exists
stackId := stack.Id
stackId := stack.ID
logrus.WithFields(logrus.Fields{
"stack": stackName,
@ -100,7 +100,7 @@ func init() {
stackCmd.AddCommand(stackRemoveCmd)
stackRemoveCmd.Flags().Bool("strict", false, "Fail if stack does not exist.")
stackRemoveCmd.Flags().Uint32("endpoint", 0, "Endpoint ID.")
stackRemoveCmd.Flags().Int("endpoint", 0, "Endpoint ID.")
viper.BindPFlag("stack.remove.strict", stackRemoveCmd.Flags().Lookup("strict"))
viper.BindPFlag("stack.remove.endpoint", stackRemoveCmd.Flags().Lookup("endpoint"))
}

View File

@ -4,12 +4,11 @@ import (
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/greenled/portainer-stack-utils/client"
)
func GetDefaultEndpoint() (endpoint client.EndpointSubset, err error) {
func GetDefaultEndpoint() (endpoint portainer.Endpoint, err error) {
portainerClient, err := GetClient()
if err != nil {
return
@ -33,7 +32,7 @@ func GetDefaultEndpoint() (endpoint client.EndpointSubset, err error) {
return
}
func GetStackByName(name string, swarmId string, endpointId uint32) (stack client.Stack, err error) {
func GetStackByName(name string, swarmId string, endpointId portainer.EndpointID) (stack portainer.Stack, err error) {
portainerClient, err := GetClient()
if err != nil {
return
@ -55,7 +54,7 @@ func GetStackByName(name string, swarmId string, endpointId uint32) (stack clien
return
}
func GetEndpointSwarmClusterId(endpointId uint32) (endpointSwarmClusterId string, err error) {
func GetEndpointSwarmClusterId(endpointId portainer.EndpointID) (endpointSwarmClusterId string, err error) {
// Get docker information for endpoint
portainerClient, err := GetClient()
if err != nil {

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.12
require (
github.com/joho/godotenv v1.3.0
github.com/mitchellh/go-homedir v1.1.0
github.com/portainer/portainer v0.0.0-20190604035120-c1433eff0dde
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.3.2

2
go.sum
View File

@ -26,6 +26,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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/portainer/portainer v0.0.0-20190604035120-c1433eff0dde h1:UyBfo+OfcYHC43XcLJcAs0BI2ZD4Lfynehk3yJTuVh4=
github.com/portainer/portainer v0.0.0-20190604035120-c1433eff0dde/go.mod h1:XXujMKBnBMNC9Et0mL41InhBxITYiKNqxpj39e9fz/w=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=