modifies the command line interface handling / timer work
This commit is contained in:
parent
d5a7d1d9a4
commit
63a16a3253
81
cmd/root.go
81
cmd/root.go
@ -5,11 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.xsfx.dev/schnutibox/internal/config"
|
"go.xsfx.dev/schnutibox/internal/config"
|
||||||
"go.xsfx.dev/schnutibox/pkg/prepare"
|
"go.xsfx.dev/schnutibox/pkg/prepare"
|
||||||
@ -67,16 +69,23 @@ func init() {
|
|||||||
|
|
||||||
// Timer.
|
// Timer.
|
||||||
rootCmd.AddCommand(timerCmd)
|
rootCmd.AddCommand(timerCmd)
|
||||||
|
timerCmd.Flags().String("hostname", "localhost", "Hostname of schnutibox")
|
||||||
|
timerCmd.Flags().Int("port", 6600, "Port of schnutibox")
|
||||||
|
timerCmd.Flags().DurationP("duration", "d", time.Minute, "Duration until the timer stops the playback")
|
||||||
|
|
||||||
|
if err := timerCmd.MarkFlagRequired("duration"); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("missing flag")
|
||||||
|
}
|
||||||
|
|
||||||
// Defaults.
|
// Defaults.
|
||||||
viper.SetDefault("box.hostname", "localhost")
|
viper.SetDefault("box.hostname", "localhost")
|
||||||
viper.SetDefault("box.port", 9999)
|
viper.SetDefault("box.port", 9999)
|
||||||
viper.SetDefault("box.grpc", 9998)
|
|
||||||
viper.SetDefault("mpd.hostname", "localhost")
|
viper.SetDefault("mpd.hostname", "localhost")
|
||||||
viper.SetDefault("mpd.port", 6600)
|
viper.SetDefault("mpd.port", 6600)
|
||||||
viper.SetDefault("reader.dev", "/dev/hidraw0")
|
viper.SetDefault("reader.dev", "/dev/hidraw0")
|
||||||
viper.SetDefault("reader.ignore", false)
|
viper.SetDefault("reader.ignore", false)
|
||||||
viper.SetDefault("pprof", false)
|
viper.SetDefault("debug.pprof", false)
|
||||||
|
viper.SetDefault("timer.duration", time.Minute)
|
||||||
|
|
||||||
// Environment handling.
|
// Environment handling.
|
||||||
viper.SetEnvPrefix("SCHNUTIBOX")
|
viper.SetEnvPrefix("SCHNUTIBOX")
|
||||||
@ -84,16 +93,17 @@ func init() {
|
|||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
// Flags.
|
// Flags.
|
||||||
if err := viper.BindPFlag("pprof", rootCmd.PersistentFlags().Lookup("pprof")); err != nil {
|
for k, v := range map[string]*pflag.Flag{
|
||||||
log.Fatal().Err(err).Msg("could not bind flag")
|
"debug.pprof": rootCmd.PersistentFlags().Lookup("pprof"),
|
||||||
}
|
"reader.dev": prepareCmd.Flags().Lookup("rfid-reader"),
|
||||||
|
"reader.ignore": runCmd.Flags().Lookup("ignore-reader"),
|
||||||
if err := viper.BindPFlag("reader.dev", prepareCmd.Flags().Lookup("rfid-reader")); err != nil {
|
"box.hostname": timerCmd.Flags().Lookup("hostname"),
|
||||||
log.Fatal().Err(err).Msg("could not bind flag")
|
"box.port": timerCmd.Flags().Lookup("port"),
|
||||||
}
|
"timer.duration": timerCmd.Flags().Lookup("duration"),
|
||||||
|
} {
|
||||||
if err := viper.BindPFlag("reader.ignore", runCmd.Flags().Lookup("ignore-reader")); err != nil {
|
if err := viper.BindPFlag(k, v); err != nil {
|
||||||
log.Fatal().Err(err).Msg("could not bind flag")
|
log.Fatal().Err(err).Msg("could not bind flag")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,18 +113,25 @@ func initConfig(fatal bool) {
|
|||||||
logger := log.With().Str("config", cfgFile).Logger()
|
logger := log.With().Str("config", cfgFile).Logger()
|
||||||
|
|
||||||
// Parse config file.
|
// Parse config file.
|
||||||
if cfgFile != "" {
|
if cfgFile == "" && fatal {
|
||||||
viper.SetConfigFile(cfgFile)
|
|
||||||
parseConfig(logger, fatal)
|
|
||||||
} else {
|
|
||||||
logger.Fatal().Msg("missing config file")
|
logger.Fatal().Msg("missing config file")
|
||||||
|
} else {
|
||||||
|
logger.Warn().Msg("missing config file")
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.WatchConfig()
|
// Dont mind if there is no config file... viper also should populate
|
||||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
// flags and environment variables.
|
||||||
logger.Info().Msg("config file changed")
|
viper.SetConfigFile(cfgFile)
|
||||||
parseConfig(logger, false)
|
parseConfig(logger, fatal)
|
||||||
})
|
|
||||||
|
// Configfile changes watch only enabled if there is a config file.
|
||||||
|
if cfgFile != "" {
|
||||||
|
viper.WatchConfig()
|
||||||
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||||
|
logger.Info().Msg("config file changed")
|
||||||
|
parseConfig(logger, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseConfig parses the config and does some tests if required fields are there.
|
// parseConfig parses the config and does some tests if required fields are there.
|
||||||
@ -126,8 +143,6 @@ func parseConfig(logger zerolog.Logger, fatal bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Error().Err(err).Msg("error loading config file")
|
logger.Error().Err(err).Msg("error loading config file")
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := viper.Unmarshal(&config.Cfg); err != nil {
|
if err := viper.Unmarshal(&config.Cfg); err != nil {
|
||||||
@ -136,16 +151,22 @@ func parseConfig(logger zerolog.Logger, fatal bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Error().Err(err).Msg("could not unmarshal config")
|
logger.Error().Err(err).Msg("could not unmarshal config")
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Cfg.Require(); err != nil {
|
// Disabling require check if no config is set.
|
||||||
if fatal {
|
// Not sure about this!
|
||||||
logger.Fatal().Err(err).Msg("missing config parts")
|
if cfgFile != "" {
|
||||||
}
|
if err := config.Cfg.Require(); err != nil {
|
||||||
|
if fatal {
|
||||||
|
logger.Fatal().Err(err).Msg("missing config parts")
|
||||||
|
}
|
||||||
|
|
||||||
logger.Error().Err(err).Msg("missing config parts")
|
logger.Error().Err(err).Msg("missing config parts")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Warn().Msg("doesnt do a config requirement check")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.xsfx.dev/schnutibox/pkg/timer"
|
"go.xsfx.dev/schnutibox/pkg/timer"
|
||||||
)
|
)
|
||||||
@ -10,4 +14,8 @@ var timerCmd = &cobra.Command{
|
|||||||
Use: "timer",
|
Use: "timer",
|
||||||
Short: "Handling timer",
|
Short: "Handling timer",
|
||||||
Run: timer.Run,
|
Run: timer.Run,
|
||||||
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
initConfig(false)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -8,14 +8,17 @@ require (
|
|||||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e // indirect
|
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e // indirect
|
||||||
github.com/fhs/gompd/v2 v2.2.0
|
github.com/fhs/gompd/v2 v2.2.0
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0
|
||||||
github.com/helloyi/go-sshclient v1.0.0
|
github.com/helloyi/go-sshclient v1.0.0
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
||||||
github.com/ory/dockertest/v3 v3.6.3
|
github.com/ory/dockertest/v3 v3.6.3
|
||||||
|
github.com/philip-bui/grpc-zerolog v1.0.1
|
||||||
github.com/prometheus/client_golang v0.9.3
|
github.com/prometheus/client_golang v0.9.3
|
||||||
github.com/rs/zerolog v1.22.0
|
github.com/rs/zerolog v1.22.0
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.7.0
|
github.com/spf13/viper v1.7.0
|
||||||
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef
|
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef
|
||||||
go.xsfx.dev/don v1.0.0
|
go.xsfx.dev/don v1.0.0
|
||||||
|
3
go.sum
3
go.sum
@ -274,6 +274,8 @@ github.com/ory/dockertest/v3 v3.6.3/go.mod h1:EFLcVUOl8qCwp9NyDAcCDtq/QviLtYswW/
|
|||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA=
|
||||||
|
github.com/philip-bui/grpc-zerolog v1.0.1/go.mod h1:qXbiq/2X4ZUMMshsqlWyTHOcw7ns+GZmlqZZN05ZHcQ=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@ -336,6 +338,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
@ -11,6 +11,10 @@ import (
|
|||||||
var Cfg Config
|
var Cfg Config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Debug struct {
|
||||||
|
PPROF bool `mapstructure:"PPROF"`
|
||||||
|
}
|
||||||
|
|
||||||
// Reader is used to configure the RFID Reader.
|
// Reader is used to configure the RFID Reader.
|
||||||
Reader struct {
|
Reader struct {
|
||||||
Dev string `mapstructure:"Dev"`
|
Dev string `mapstructure:"Dev"`
|
||||||
|
18
internal/grpcclient/grpcclient.go
Normal file
18
internal/grpcclient/grpcclient.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package grpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Conn(hostname string, port int) (*grpc.ClientConn, error) {
|
||||||
|
var conn *grpc.ClientConn
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(fmt.Sprintf("%s:%d", hostname, port), grpc.WithInsecure())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not connect: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
@ -1,45 +1,77 @@
|
|||||||
package timer
|
package timer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/duration"
|
"github.com/golang/protobuf/ptypes/duration"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.xsfx.dev/schnutibox/internal/config"
|
||||||
|
"go.xsfx.dev/schnutibox/internal/grpcclient"
|
||||||
api "go.xsfx.dev/schnutibox/pkg/api/v1"
|
api "go.xsfx.dev/schnutibox/pkg/api/v1"
|
||||||
|
"go.xsfx.dev/schnutibox/pkg/mpc"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint:gochecknoglobals
|
// nolint:gochecknoglobals
|
||||||
var T = &api.Timer{}
|
var T = &Timer{}
|
||||||
|
|
||||||
func Timer() {
|
type Timer struct {
|
||||||
if T.Duration != nil {
|
Req *api.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Timer) Handle() {
|
||||||
|
if t.Req != nil {
|
||||||
// Initialize the current object.
|
// Initialize the current object.
|
||||||
if T.Current == nil {
|
if t.Req.Current == nil {
|
||||||
T.Current = &duration.Duration{}
|
t.Req.Current = &duration.Duration{}
|
||||||
T.Current.Seconds = T.Duration.Seconds
|
t.Req.Current.Seconds = t.Req.Duration.Seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// There is some timing going on.
|
// There is some timing going on.
|
||||||
case T.Duration.Seconds != 0 && T.Current.Seconds != 0:
|
case t.Req.Duration.Seconds != 0 && t.Req.Current.Seconds != 0:
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Int64("current", T.Current.Seconds).
|
Int64("current", t.Req.Current.Seconds).
|
||||||
Int64("duration", T.Duration.Seconds).
|
Int64("duration", t.Req.Duration.Seconds).
|
||||||
Msg("timer is running")
|
Msg("timer is running")
|
||||||
|
|
||||||
if T.Current.Seconds > 0 {
|
if t.Req.Current.Seconds > 0 {
|
||||||
T.Current.Seconds -= 1
|
t.Req.Current.Seconds -= 1
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// No timer is running... so setting the duration to 0.
|
// No timer is running... so setting the duration to 0.
|
||||||
// TODO: Needs to do something actually!
|
case t.Req.Current.Seconds == 0 && t.Req.Duration.Seconds != 0:
|
||||||
case T.Current.Seconds == 0 && T.Duration.Seconds != 0:
|
|
||||||
log.Debug().Msg("stoping timer")
|
log.Debug().Msg("stoping timer")
|
||||||
|
|
||||||
T.Duration.Seconds = 0
|
if err := mpc.Stop(log.Logger); err != nil {
|
||||||
|
log.Error().Err(err).Msg("could not stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Req.Duration.Seconds = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(cmd *cobra.Command, args []string) {}
|
// Run is the command line interface for triggering the timer.
|
||||||
|
func Run(cmd *cobra.Command, args []string) {
|
||||||
|
conn, err := grpcclient.Conn(config.Cfg.Box.Hostname, config.Cfg.Box.Port)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not connect")
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
c := api.NewTimerServiceClient(conn)
|
||||||
|
|
||||||
|
d := durationpb.New(viper.GetDuration("timer.duration"))
|
||||||
|
|
||||||
|
_, err = c.Create(context.Background(), &api.Timer{Duration: d})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not create timer")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msg("added timer")
|
||||||
|
}
|
||||||
|
@ -23,19 +23,20 @@ func Run() {
|
|||||||
<-ticker.C
|
<-ticker.C
|
||||||
|
|
||||||
// Timer.
|
// Timer.
|
||||||
go timer.Timer()
|
go timer.T.Handle()
|
||||||
|
|
||||||
// Metrics.
|
// Metrics.
|
||||||
go func() {
|
go func() {
|
||||||
m, err := mpc.Conn()
|
m, err := mpc.Conn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// log.Error().Err(err).Msg("could not connect")
|
log.Error().Err(err).Msg("could not connect")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uris, err := mpc.PlaylistURIS()
|
uris, err := mpc.PlaylistURIS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// log.Error().Err(err).Msg("could not get playlist uris")
|
log.Error().Err(err).Msg("could not get playlist uris")
|
||||||
metrics.BoxErrors.Inc()
|
metrics.BoxErrors.Inc()
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -44,7 +45,7 @@ func Run() {
|
|||||||
// Gettings MPD state.
|
// Gettings MPD state.
|
||||||
s, err := m.Status()
|
s, err := m.Status()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// log.Error().Err(err).Msg("could not get status")
|
log.Error().Err(err).Msg("could not get status")
|
||||||
metrics.BoxErrors.Inc()
|
metrics.BoxErrors.Inc()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
zerolog "github.com/philip-bui/grpc-zerolog"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.xsfx.dev/logginghandler"
|
"go.xsfx.dev/logginghandler"
|
||||||
assets "go.xsfx.dev/schnutibox/assets/web"
|
assets "go.xsfx.dev/schnutibox/assets/web"
|
||||||
"go.xsfx.dev/schnutibox/internal/config"
|
"go.xsfx.dev/schnutibox/internal/config"
|
||||||
@ -81,14 +81,14 @@ func (i identifyServer) Identify(ctx context.Context, in *api.IdentifyRequest) (
|
|||||||
type timerServer struct{}
|
type timerServer struct{}
|
||||||
|
|
||||||
func (t timerServer) Create(ctx context.Context, req *api.Timer) (*api.Timer, error) {
|
func (t timerServer) Create(ctx context.Context, req *api.Timer) (*api.Timer, error) {
|
||||||
timer.T = req
|
timer.T.Req = req
|
||||||
|
|
||||||
return timer.T, nil
|
return timer.T.Req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get just returns the status of the timer.
|
// Get just returns the status of the timer.
|
||||||
func (t timerServer) Get(ctx context.Context, req *api.TimerEmpty) (*api.Timer, error) {
|
func (t timerServer) Get(ctx context.Context, req *api.TimerEmpty) (*api.Timer, error) {
|
||||||
return timer.T, nil
|
return timer.T.Req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gw(s *grpc.Server, conn string) *runtime.ServeMux {
|
func gw(s *grpc.Server, conn string) *runtime.ServeMux {
|
||||||
@ -114,7 +114,9 @@ func Run(command *cobra.Command, args []string) {
|
|||||||
lh := fmt.Sprintf("%s:%d", config.Cfg.Box.Hostname, config.Cfg.Box.Port)
|
lh := fmt.Sprintf("%s:%d", config.Cfg.Box.Hostname, config.Cfg.Box.Port)
|
||||||
|
|
||||||
// Create grpc server.
|
// Create grpc server.
|
||||||
grpcServer := grpc.NewServer()
|
grpcServer := grpc.NewServer(
|
||||||
|
zerolog.UnaryInterceptor(),
|
||||||
|
)
|
||||||
|
|
||||||
// Define http handlers.
|
// Define http handlers.
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
@ -138,7 +140,7 @@ func Run(command *cobra.Command, args []string) {
|
|||||||
mux.Handle("/api/", http.StripPrefix("/api", gw(grpcServer, lh)))
|
mux.Handle("/api/", http.StripPrefix("/api", gw(grpcServer, lh)))
|
||||||
|
|
||||||
// PPROF.
|
// PPROF.
|
||||||
if viper.GetBool("pprof") {
|
if config.Cfg.Debug.PPROF {
|
||||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
vendor/github.com/philip-bui/grpc-zerolog/.gitignore
generated
vendored
Normal file
16
vendor/github.com/philip-bui/grpc-zerolog/.gitignore
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
/vendor
|
||||||
|
grpc-zerolog
|
||||||
|
coverage.txt
|
14
vendor/github.com/philip-bui/grpc-zerolog/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/philip-bui/grpc-zerolog/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||||
|
- dep ensure
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
259
vendor/github.com/philip-bui/grpc-zerolog/Gopkg.lock
generated
vendored
Normal file
259
vendor/github.com/philip-bui/grpc-zerolog/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||||
|
name = "github.com/davecgh/go-spew"
|
||||||
|
packages = ["spew"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:4ba8096f8ea6ea9f7aefb98e189e0128324883f0b4e839800ce4b37c90230c5c"
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
packages = [
|
||||||
|
"jsonpb",
|
||||||
|
"proto",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/struct",
|
||||||
|
"ptypes/timestamp",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "d04d7b157bb510b1e0c10132224b616ac0e26b17"
|
||||||
|
version = "v1.4.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||||
|
name = "github.com/pmezard/go-difflib"
|
||||||
|
packages = ["difflib"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:342baf1b227dbc8829a60dc06cd7a5e70d3de350b501e2184ec8f19fe4418d4a"
|
||||||
|
name = "github.com/rs/zerolog"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"internal/cbor",
|
||||||
|
"internal/json",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "7825d863376faee2723fc99c061c538bd80812c8"
|
||||||
|
version = "v1.19.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
|
||||||
|
name = "github.com/stretchr/objx"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
|
||||||
|
version = "v0.1.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:5201127841a78d84d0ca68a2e564c08e3882c0fb9321a75997ce87926e0d63ea"
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
packages = [
|
||||||
|
"assert",
|
||||||
|
"mock",
|
||||||
|
"require",
|
||||||
|
"suite",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f654a9112bbeac49ca2cd45bfbe11533c4666cf8"
|
||||||
|
version = "v1.6.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:dc1b5a4f3a84b5c8503036630dd0f245de44d9c22414ddff0e5e473849139fd3"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"http/httpguts",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"trace",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "ed29d75add3d7c4bf7ca65aac0c6df3d1420216f"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:601e63e7d4577f907118bec825902505291918859d223bce015539e79f1160e3"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = ["googleapis/rpc/status"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "ff3583edef7de132f219f0efc00e097cabcc0ec0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"balancer",
|
||||||
|
"balancer/base",
|
||||||
|
"balancer/roundrobin",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"encoding",
|
||||||
|
"encoding/proto",
|
||||||
|
"grpclog",
|
||||||
|
"internal",
|
||||||
|
"internal/backoff",
|
||||||
|
"internal/channelz",
|
||||||
|
"internal/grpcrand",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"resolver",
|
||||||
|
"resolver/dns",
|
||||||
|
"resolver/passthrough",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap",
|
||||||
|
"transport",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||||
|
version = "v1.13.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:6a351cb031761dcd668e5aca9a10bd6d1675aa5427ac1483a61c3a49b2d64962"
|
||||||
|
name = "google.golang.org/protobuf"
|
||||||
|
packages = [
|
||||||
|
"encoding/protojson",
|
||||||
|
"encoding/prototext",
|
||||||
|
"encoding/protowire",
|
||||||
|
"internal/descfmt",
|
||||||
|
"internal/descopts",
|
||||||
|
"internal/detrand",
|
||||||
|
"internal/encoding/defval",
|
||||||
|
"internal/encoding/json",
|
||||||
|
"internal/encoding/messageset",
|
||||||
|
"internal/encoding/tag",
|
||||||
|
"internal/encoding/text",
|
||||||
|
"internal/errors",
|
||||||
|
"internal/fieldsort",
|
||||||
|
"internal/filedesc",
|
||||||
|
"internal/filetype",
|
||||||
|
"internal/flags",
|
||||||
|
"internal/genid",
|
||||||
|
"internal/impl",
|
||||||
|
"internal/mapsort",
|
||||||
|
"internal/pragma",
|
||||||
|
"internal/set",
|
||||||
|
"internal/strs",
|
||||||
|
"internal/version",
|
||||||
|
"proto",
|
||||||
|
"reflect/protoreflect",
|
||||||
|
"reflect/protoregistry",
|
||||||
|
"runtime/protoiface",
|
||||||
|
"runtime/protoimpl",
|
||||||
|
"types/known/anypb",
|
||||||
|
"types/known/durationpb",
|
||||||
|
"types/known/structpb",
|
||||||
|
"types/known/timestamppb",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "3f7a61f89bb6813f89d981d1870ed68da0b3c3f1"
|
||||||
|
version = "v1.25.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v3"
|
||||||
|
digest = "1:229cb0f6192914f518cc1241ede6d6f1f458b31debfa18bf3a5c9e4f7b01e24b"
|
||||||
|
name = "gopkg.in/yaml.v3"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "eeeca48fe7764f320e4870d231902bf9c1be2c08"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
input-imports = [
|
||||||
|
"github.com/davecgh/go-spew/spew",
|
||||||
|
"github.com/golang/protobuf/jsonpb",
|
||||||
|
"github.com/golang/protobuf/proto",
|
||||||
|
"github.com/golang/protobuf/ptypes",
|
||||||
|
"github.com/golang/protobuf/ptypes/any",
|
||||||
|
"github.com/golang/protobuf/ptypes/duration",
|
||||||
|
"github.com/golang/protobuf/ptypes/struct",
|
||||||
|
"github.com/golang/protobuf/ptypes/timestamp",
|
||||||
|
"github.com/pmezard/go-difflib/difflib",
|
||||||
|
"github.com/rs/zerolog",
|
||||||
|
"github.com/rs/zerolog/log",
|
||||||
|
"github.com/stretchr/objx",
|
||||||
|
"github.com/stretchr/testify/assert",
|
||||||
|
"github.com/stretchr/testify/mock",
|
||||||
|
"github.com/stretchr/testify/require",
|
||||||
|
"github.com/stretchr/testify/suite",
|
||||||
|
"golang.org/x/net/context",
|
||||||
|
"golang.org/x/net/http/httpguts",
|
||||||
|
"golang.org/x/net/http2",
|
||||||
|
"golang.org/x/net/http2/hpack",
|
||||||
|
"golang.org/x/net/idna",
|
||||||
|
"golang.org/x/net/trace",
|
||||||
|
"golang.org/x/text/collate",
|
||||||
|
"golang.org/x/text/collate/build",
|
||||||
|
"golang.org/x/text/language",
|
||||||
|
"golang.org/x/text/secure/bidirule",
|
||||||
|
"golang.org/x/text/transform",
|
||||||
|
"golang.org/x/text/unicode/bidi",
|
||||||
|
"golang.org/x/text/unicode/cldr",
|
||||||
|
"golang.org/x/text/unicode/norm",
|
||||||
|
"golang.org/x/text/unicode/rangetable",
|
||||||
|
"google.golang.org/genproto/googleapis/rpc/status",
|
||||||
|
"google.golang.org/grpc",
|
||||||
|
"google.golang.org/grpc/balancer",
|
||||||
|
"google.golang.org/grpc/balancer/base",
|
||||||
|
"google.golang.org/grpc/balancer/roundrobin",
|
||||||
|
"google.golang.org/grpc/codes",
|
||||||
|
"google.golang.org/grpc/connectivity",
|
||||||
|
"google.golang.org/grpc/credentials",
|
||||||
|
"google.golang.org/grpc/encoding",
|
||||||
|
"google.golang.org/grpc/encoding/proto",
|
||||||
|
"google.golang.org/grpc/grpclog",
|
||||||
|
"google.golang.org/grpc/keepalive",
|
||||||
|
"google.golang.org/grpc/metadata",
|
||||||
|
"google.golang.org/grpc/naming",
|
||||||
|
"google.golang.org/grpc/peer",
|
||||||
|
"google.golang.org/grpc/resolver",
|
||||||
|
"google.golang.org/grpc/resolver/dns",
|
||||||
|
"google.golang.org/grpc/resolver/passthrough",
|
||||||
|
"google.golang.org/grpc/stats",
|
||||||
|
"google.golang.org/grpc/status",
|
||||||
|
"google.golang.org/grpc/tap",
|
||||||
|
"google.golang.org/grpc/transport",
|
||||||
|
]
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
46
vendor/github.com/philip-bui/grpc-zerolog/Gopkg.toml
generated
vendored
Normal file
46
vendor/github.com/philip-bui/grpc-zerolog/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
version = "1.1.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/rs/zerolog"
|
||||||
|
version = "1.8.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
version = "1.13.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
version = "1.2.2"
|
21
vendor/github.com/philip-bui/grpc-zerolog/LICENSE
generated
vendored
Normal file
21
vendor/github.com/philip-bui/grpc-zerolog/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Philip Bui
|
||||||
|
|
||||||
|
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.
|
17
vendor/github.com/philip-bui/grpc-zerolog/Makefile
generated
vendored
Normal file
17
vendor/github.com/philip-bui/grpc-zerolog/Makefile
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
PORT?=8000
|
||||||
|
PACKAGE:=github.com/philip-bui/grpc-zerolog
|
||||||
|
COVERAGE:=coverage.txt
|
||||||
|
proto:
|
||||||
|
protoc -I protos/ protos/*.proto --go_out=plugins=grpc:protos
|
||||||
|
|
||||||
|
godoc:
|
||||||
|
echo "localhost:${PORT}/pkg/${PACKAGE}"
|
||||||
|
godoc -http=:${PORT}
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -race -coverprofile=${COVERAGE} -covermode=atomic
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
go tool cover -html=${COVERAGE}
|
49
vendor/github.com/philip-bui/grpc-zerolog/README.md
generated
vendored
Normal file
49
vendor/github.com/philip-bui/grpc-zerolog/README.md
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# gRPC Zerolog
|
||||||
|
|
||||||
|
[![BuildStatus Widget]][BuildStatus Result]
|
||||||
|
[![CodeCov Widget]][CodeCov Result]
|
||||||
|
[![GoReport Widget]][GoReport Status]
|
||||||
|
[![GoDoc Widget]][GoDoc]
|
||||||
|
|
||||||
|
[BuildStatus Result]: https://travis-ci.org/philip-bui/grpc-zerolog
|
||||||
|
[BuildStatus Widget]: https://travis-ci.org/philip-bui/grpc-zerolog.svg?branch=master
|
||||||
|
|
||||||
|
[CodeCov Result]: https://codecov.io/gh/philip-bui/grpc-zerolog
|
||||||
|
[CodeCov Widget]: https://codecov.io/gh/philip-bui/grpc-zerolog/branch/master/graph/badge.svg
|
||||||
|
|
||||||
|
[GoReport Status]: https://goreportcard.com/report/github.com/philip-bui/grpc-zerolog
|
||||||
|
[GoReport Widget]: https://goreportcard.com/badge/github.com/philip-bui/grpc-zerolog
|
||||||
|
|
||||||
|
[GoDoc]: https://godoc.org/github.com/philip-bui/grpc-zerolog
|
||||||
|
[GoDoc Widget]: https://godoc.org/github.com/philip-bui/grpc-zerolog?status.svg
|
||||||
|
|
||||||
|
Implementation of gRPC Logging Middleware, integrating [Zerolog](https://github.com/rs/zerolog) as a gRPC [Interceptor](https://github.com/grpc-ecosystem/go-grpc-middleware) to log the following fields:
|
||||||
|
|
||||||
|
- Request Protobufs as JSON.
|
||||||
|
- Response Protobufs as JSON, or Errors.
|
||||||
|
- Status Code, Duration, Timestamp, Service Name, Service Method, IP, Metadata Fields and User Agent.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/philip-bui/grpc-zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// With global Zerolog logger.
|
||||||
|
grpc.NewServer(
|
||||||
|
zerolog.UnaryInterceptor(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// With custom Zerolog instance.
|
||||||
|
log := zerolog.New(os.Stdout)
|
||||||
|
grpc.NewServer(
|
||||||
|
zerolog.UnaryInterceptorWithLogger(&log),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
gRPC Zerolog is available under the MIT license. [See LICENSE](https://github.com/philip-bui/grpc-zerolog/blob/master/LICENSE) for details.
|
66
vendor/github.com/philip-bui/grpc-zerolog/unary_interceptor.go
generated
vendored
Normal file
66
vendor/github.com/philip-bui/grpc-zerolog/unary_interceptor.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package zerolog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnaryInterceptor is a gRPC Server Option that uses NewUnaryServerInterceptor() to log gRPC Requests.
|
||||||
|
func UnaryInterceptor() grpc.ServerOption {
|
||||||
|
return grpc.UnaryInterceptor(NewUnaryServerInterceptor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnaryInterceptorWithLogger(log *zerolog.Logger) grpc.ServerOption {
|
||||||
|
return grpc.UnaryInterceptor(NewUnaryServerInterceptorWithLogger(log))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnaryServerInterceptor that logs gRPC Requests using Zerolog.
|
||||||
|
// {
|
||||||
|
// ServiceField: "ExampleService",
|
||||||
|
// MethodField: "ExampleMethod",
|
||||||
|
// DurationField: 1.00
|
||||||
|
//
|
||||||
|
// IpField: "127.0.0.1",
|
||||||
|
//
|
||||||
|
// MetadataField: {},
|
||||||
|
//
|
||||||
|
// UserAgentField: "ExampleClientUserAgent",
|
||||||
|
// ReqField: {}, // JSON representation of Request Protobuf
|
||||||
|
//
|
||||||
|
// Err: "An unexpected error occurred",
|
||||||
|
// CodeField: "Unknown",
|
||||||
|
// MsgField: "Error message returned from the server",
|
||||||
|
// DetailsField: [Errors],
|
||||||
|
//
|
||||||
|
// RespField: {}, // JSON representation of Response Protobuf
|
||||||
|
//
|
||||||
|
// ZerologMessageField: "UnaryMessageDefault",
|
||||||
|
// }
|
||||||
|
func NewUnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return NewUnaryServerInterceptorWithLogger(&log.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnaryServerInterceptorWithLogger(log *zerolog.Logger) grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||||
|
now := time.Now()
|
||||||
|
resp, err := handler(ctx, req)
|
||||||
|
if log.Error().Enabled() {
|
||||||
|
if err != nil {
|
||||||
|
logger := log.Error()
|
||||||
|
LogIncomingCall(ctx, logger, info.FullMethod, now, req)
|
||||||
|
LogStatusError(logger, err)
|
||||||
|
logger.Msg(UnaryMessageDefault)
|
||||||
|
} else if log.Info().Enabled() {
|
||||||
|
logger := log.Info()
|
||||||
|
LogIncomingCall(ctx, logger, info.FullMethod, now, req)
|
||||||
|
LogResponse(logger, resp)
|
||||||
|
logger.Msg(UnaryMessageDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
}
|
224
vendor/github.com/philip-bui/grpc-zerolog/util.go
generated
vendored
Normal file
224
vendor/github.com/philip-bui/grpc-zerolog/util.go
generated
vendored
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package zerolog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/jsonpb"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Marshaller of Protobuf to JSON
|
||||||
|
Marshaller = &jsonpb.Marshaler{}
|
||||||
|
// TimestampLog call start.
|
||||||
|
TimestampLog = true
|
||||||
|
// ServiceField key.
|
||||||
|
ServiceField = "service"
|
||||||
|
// ServiceLog gRPC service name.
|
||||||
|
ServiceLog = true
|
||||||
|
// MethodField key.
|
||||||
|
MethodField = "method"
|
||||||
|
// MethodLog gRPC method name.
|
||||||
|
MethodLog = true
|
||||||
|
// DurationField key.
|
||||||
|
DurationField = "dur"
|
||||||
|
// DurationLog gRPC call duration.
|
||||||
|
DurationLog = true
|
||||||
|
// IPField key.
|
||||||
|
IPField = "ip"
|
||||||
|
// IPLog gRPC client IP.
|
||||||
|
IPLog = true
|
||||||
|
// MetadataField key.
|
||||||
|
MetadataField = "md"
|
||||||
|
// MetadataLog gRPC call metadata.
|
||||||
|
MetadataLog = true
|
||||||
|
// UserAgentField key.
|
||||||
|
UserAgentField = "ua"
|
||||||
|
// UserAgentLog gRPC client User Agent.
|
||||||
|
UserAgentLog = true
|
||||||
|
// ReqField key.
|
||||||
|
ReqField = "req"
|
||||||
|
// ReqLog gRPC request body.
|
||||||
|
ReqLog = true
|
||||||
|
// RespField key.
|
||||||
|
RespField = "resp"
|
||||||
|
// RespLog gRPC response body.
|
||||||
|
RespLog = true
|
||||||
|
// MaxSize to log gRPC bodies.
|
||||||
|
MaxSize = 2048000
|
||||||
|
// CodeField gRPC status code response.
|
||||||
|
CodeField = "code"
|
||||||
|
// MsgField gRPC response message.
|
||||||
|
MsgField = "msg"
|
||||||
|
// DetailsField gRPC response errors.
|
||||||
|
DetailsField = "details"
|
||||||
|
// UnaryMessageDefault of logging messages from unary.
|
||||||
|
UnaryMessageDefault = "unary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogIncomingCall of gRPC method.
|
||||||
|
// {
|
||||||
|
// ServiceField: ExampleService,
|
||||||
|
// MethodField: ExampleMethod,
|
||||||
|
// DurationField: 1.00,
|
||||||
|
// }
|
||||||
|
func LogIncomingCall(ctx context.Context, logger *zerolog.Event, method string, t time.Time, req interface{}) {
|
||||||
|
LogTimestamp(logger, t)
|
||||||
|
LogService(logger, method)
|
||||||
|
LogMethod(logger, method)
|
||||||
|
LogDuration(logger, t)
|
||||||
|
LogRequest(logger, req)
|
||||||
|
LogIncomingMetadata(ctx, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogTimestamp of call.
|
||||||
|
// {
|
||||||
|
// TimestampField: Timestamp,
|
||||||
|
// }
|
||||||
|
func LogTimestamp(logger *zerolog.Event, t time.Time) {
|
||||||
|
if TimestampLog {
|
||||||
|
*logger = *logger.Time(zerolog.TimestampFieldName, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogService of gRPC name.
|
||||||
|
// {
|
||||||
|
// ServiceField: gRPCServiceName,
|
||||||
|
// }
|
||||||
|
func LogService(logger *zerolog.Event, method string) {
|
||||||
|
if ServiceLog {
|
||||||
|
*logger = *logger.Str(ServiceField, path.Dir(method)[1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMethod of gRPC call.
|
||||||
|
// {
|
||||||
|
// MethodField: gRPCMethodName,
|
||||||
|
// }
|
||||||
|
func LogMethod(logger *zerolog.Event, method string) {
|
||||||
|
if MethodLog {
|
||||||
|
*logger = *logger.Str(MethodField, path.Base(method))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogDuration in seconds of gRPC call.
|
||||||
|
// {
|
||||||
|
// DurationField: Timestamp,
|
||||||
|
// }
|
||||||
|
func LogDuration(logger *zerolog.Event, t time.Time) {
|
||||||
|
if DurationLog {
|
||||||
|
*logger = *logger.Dur(DurationField, time.Since(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogIP address of gRPC client, if assigned.
|
||||||
|
// {
|
||||||
|
// IpField: 127.0.0.1
|
||||||
|
// }
|
||||||
|
func LogIP(ctx context.Context, logger *zerolog.Event) {
|
||||||
|
if IPLog {
|
||||||
|
if p, ok := peer.FromContext(ctx); ok {
|
||||||
|
*logger = *logger.Str(IPField, p.Addr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogRequest in JSON of gRPC Call, given Request is smaller than MaxSize (Default=2MB).
|
||||||
|
// {
|
||||||
|
// ReqField: {}
|
||||||
|
// }
|
||||||
|
func LogRequest(e *zerolog.Event, req interface{}) {
|
||||||
|
if ReqLog {
|
||||||
|
if b := GetRawJSON(req); b != nil {
|
||||||
|
*e = *e.RawJSON(ReqField, b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogResponse in JSON of gRPC Call, given Response is smaller than MaxSize (Default=2MB).
|
||||||
|
// {
|
||||||
|
// RespField: {}
|
||||||
|
// }
|
||||||
|
func LogResponse(e *zerolog.Event, resp interface{}) {
|
||||||
|
if RespLog {
|
||||||
|
if b := GetRawJSON(resp); b != nil {
|
||||||
|
*e = *e.RawJSON(RespField, b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawJSON converts a Protobuf message to JSON bytes if less than MaxSize.
|
||||||
|
func GetRawJSON(i interface{}) *bytes.Buffer {
|
||||||
|
if pb, ok := i.(proto.Message); ok {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if err := Marshaller.Marshal(b, pb); err == nil && b.Len() < MaxSize {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogIncomingMetadata or UserAgent field of incoming gRPC Request, if assigned.
|
||||||
|
// {
|
||||||
|
// MetadataField: {
|
||||||
|
// MetadataKey1: MetadataValue1,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// UserAgentField: "Client-assigned User-Agent",
|
||||||
|
// }
|
||||||
|
func LogIncomingMetadata(ctx context.Context, e *zerolog.Event) {
|
||||||
|
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||||
|
if MetadataLog {
|
||||||
|
*e = *e.Dict(MetadataField, LogMetadata(&md))
|
||||||
|
return
|
||||||
|
} else if UserAgentLog {
|
||||||
|
LogUserAgent(e, &md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMetadata of gRPC Request
|
||||||
|
// {
|
||||||
|
// MetadataField: {
|
||||||
|
// MetadataKey1: MetadataValue1,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
func LogMetadata(md *metadata.MD) *zerolog.Event {
|
||||||
|
dict := zerolog.Dict()
|
||||||
|
for i := range *md {
|
||||||
|
dict = dict.Str(i, strings.Join(md.Get(i), ","))
|
||||||
|
}
|
||||||
|
return dict
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogUserAgent of gRPC Client, if assigned.
|
||||||
|
// {
|
||||||
|
// UserAgentField: "Client-assigned User-Agent",
|
||||||
|
// }
|
||||||
|
func LogUserAgent(logger *zerolog.Event, md *metadata.MD) {
|
||||||
|
if ua := strings.Join(md.Get("user-agent"), ""); ua != "" {
|
||||||
|
*logger = *logger.Str(UserAgentField, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogStatusError of gRPC Error Response.
|
||||||
|
// {
|
||||||
|
// Err: "An unexpected error occurred",
|
||||||
|
// CodeField: "Unknown",
|
||||||
|
// MsgField: "Error message returned from the server",
|
||||||
|
// DetailsField: [Errors],
|
||||||
|
// }
|
||||||
|
func LogStatusError(logger *zerolog.Event, err error) {
|
||||||
|
statusErr := status.Convert(err)
|
||||||
|
*logger = *logger.Err(err).Str(CodeField, statusErr.Code().String()).Str(MsgField, statusErr.Message()).Interface(DetailsField, statusErr.Details())
|
||||||
|
}
|
119
vendor/github.com/philip-bui/grpc-zerolog/zerolog.go
generated
vendored
Normal file
119
vendor/github.com/philip-bui/grpc-zerolog/zerolog.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package zerolog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GrpcLogSetNewZeroLogger sets grpclog to a new GrpcZeroLogger.
|
||||||
|
func GrpcLogSetNewZeroLogger() {
|
||||||
|
grpclog.SetLoggerV2(NewGrpcZeroLogger(zerolog.Logger{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrpcLogSetZeroLogger sets grpclog to a GrpcZeroLogger.
|
||||||
|
func GrpcLogSetZeroLogger(logger GrpcZeroLogger) {
|
||||||
|
grpclog.SetLoggerV2(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrpcZeroLogger transforms grpc log calls to Zerolog logger.
|
||||||
|
type GrpcZeroLogger struct {
|
||||||
|
log zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGrpcZeroLogger creates a new GrpcZeroLogger
|
||||||
|
func NewGrpcZeroLogger(logger zerolog.Logger) GrpcZeroLogger {
|
||||||
|
return GrpcZeroLogger{log: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal fatals arguments.
|
||||||
|
func (l GrpcZeroLogger) Fatal(args ...interface{}) {
|
||||||
|
l.log.Fatal().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf fatals formatted string with arguments.
|
||||||
|
func (l GrpcZeroLogger) Fatalf(format string, args ...interface{}) {
|
||||||
|
l.log.Fatal().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln fatals and new line.
|
||||||
|
func (l GrpcZeroLogger) Fatalln(args ...interface{}) {
|
||||||
|
l.Fatal(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error errors arguments.
|
||||||
|
func (l GrpcZeroLogger) Error(args ...interface{}) {
|
||||||
|
l.log.Error().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf errors formatted string with arguments.
|
||||||
|
func (l GrpcZeroLogger) Errorf(format string, args ...interface{}) {
|
||||||
|
l.log.Error().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln errors and new line.
|
||||||
|
func (l GrpcZeroLogger) Errorln(args ...interface{}) {
|
||||||
|
l.Error(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info infos arguments.
|
||||||
|
func (l GrpcZeroLogger) Info(args ...interface{}) {
|
||||||
|
l.log.Info().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof infos formatted string with arguments.
|
||||||
|
func (l GrpcZeroLogger) Infof(format string, args ...interface{}) {
|
||||||
|
l.log.Info().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln infos and new line.
|
||||||
|
func (l GrpcZeroLogger) Infoln(args ...interface{}) {
|
||||||
|
l.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning warns arguments.
|
||||||
|
func (l GrpcZeroLogger) Warning(args ...interface{}) {
|
||||||
|
l.log.Warn().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf warns formatted string with arguments.
|
||||||
|
func (l GrpcZeroLogger) Warningf(format string, args ...interface{}) {
|
||||||
|
l.log.Warn().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningln warns and new line.
|
||||||
|
func (l GrpcZeroLogger) Warningln(args ...interface{}) {
|
||||||
|
l.Warning(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print logs arguments.
|
||||||
|
func (l GrpcZeroLogger) Print(args ...interface{}) {
|
||||||
|
l.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf logs formatted string with arguments.
|
||||||
|
func (l GrpcZeroLogger) Printf(format string, args ...interface{}) {
|
||||||
|
l.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs with new line.
|
||||||
|
func (l GrpcZeroLogger) Println(args ...interface{}) {
|
||||||
|
l.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V determines Verbosity Level.
|
||||||
|
func (l GrpcZeroLogger) V(level int) bool {
|
||||||
|
switch level {
|
||||||
|
case 0:
|
||||||
|
return zerolog.InfoLevel <= zerolog.GlobalLevel()
|
||||||
|
case 1:
|
||||||
|
return zerolog.WarnLevel <= zerolog.GlobalLevel()
|
||||||
|
case 2:
|
||||||
|
return zerolog.ErrorLevel <= zerolog.GlobalLevel()
|
||||||
|
case 3:
|
||||||
|
return zerolog.FatalLevel <= zerolog.GlobalLevel()
|
||||||
|
default:
|
||||||
|
panic("unhandled gRPC logger level")
|
||||||
|
}
|
||||||
|
}
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@ -156,6 +156,7 @@ github.com/golang/glog
|
|||||||
# github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
|
# github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
|
||||||
github.com/golang/groupcache/lru
|
github.com/golang/groupcache/lru
|
||||||
# github.com/golang/protobuf v1.5.2
|
# github.com/golang/protobuf v1.5.2
|
||||||
|
## explicit
|
||||||
github.com/golang/protobuf/descriptor
|
github.com/golang/protobuf/descriptor
|
||||||
github.com/golang/protobuf/jsonpb
|
github.com/golang/protobuf/jsonpb
|
||||||
github.com/golang/protobuf/proto
|
github.com/golang/protobuf/proto
|
||||||
@ -268,6 +269,9 @@ github.com/ory/dockertest/v3/docker/types/strslice
|
|||||||
github.com/ory/dockertest/v3/docker/types/versions
|
github.com/ory/dockertest/v3/docker/types/versions
|
||||||
# github.com/pelletier/go-toml v1.2.0
|
# github.com/pelletier/go-toml v1.2.0
|
||||||
github.com/pelletier/go-toml
|
github.com/pelletier/go-toml
|
||||||
|
# github.com/philip-bui/grpc-zerolog v1.0.1
|
||||||
|
## explicit
|
||||||
|
github.com/philip-bui/grpc-zerolog
|
||||||
# github.com/pkg/errors v0.9.1
|
# github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/errors
|
github.com/pkg/errors
|
||||||
# github.com/pkg/profile v1.5.0
|
# github.com/pkg/profile v1.5.0
|
||||||
@ -306,6 +310,7 @@ github.com/spf13/cobra
|
|||||||
# github.com/spf13/jwalterweatherman v1.0.0
|
# github.com/spf13/jwalterweatherman v1.0.0
|
||||||
github.com/spf13/jwalterweatherman
|
github.com/spf13/jwalterweatherman
|
||||||
# github.com/spf13/pflag v1.0.5
|
# github.com/spf13/pflag v1.0.5
|
||||||
|
## explicit
|
||||||
github.com/spf13/pflag
|
github.com/spf13/pflag
|
||||||
# github.com/spf13/viper v1.7.0
|
# github.com/spf13/viper v1.7.0
|
||||||
## explicit
|
## explicit
|
||||||
|
Loading…
Reference in New Issue
Block a user