From 038a93486e7e762fb93645fda16f2121edc831c9 Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Tue, 28 Nov 2023 19:00:10 +0530 Subject: [PATCH 1/5] feat: add interfaces for context , logger and spinner --- cli/common/constants.go | 2 +- cli/common/files.go | 1 + cli/common/interfaces.go | 91 ++++++++++++++++++++++++++++++++++++++++ cli/common/logs.go | 1 + cli/common/spinner.go | 1 + cli/common/types.go | 62 ++++++++++++++++++++++++--- 6 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 cli/common/files.go create mode 100644 cli/common/interfaces.go create mode 100644 cli/common/logs.go create mode 100644 cli/common/spinner.go diff --git a/cli/common/constants.go b/cli/common/constants.go index a66fa8fd..a25a7c11 100644 --- a/cli/common/constants.go +++ b/cli/common/constants.go @@ -25,7 +25,7 @@ const ( DiveIconNodeAlreadyRunning = "Icon Node Already Running" DiveLogDirectory = "/logs/" DiveDitLogFile = "divelog.log" - DiveErorLogFile = "error.log" + DiveErrorLogFile = "error.log" DiveOutFile = "dive.json" ServiceFilePath = "services.json" starlarkScript = ` diff --git a/cli/common/files.go b/cli/common/files.go new file mode 100644 index 00000000..805d0c79 --- /dev/null +++ b/cli/common/files.go @@ -0,0 +1 @@ +package common diff --git a/cli/common/interfaces.go b/cli/common/interfaces.go new file mode 100644 index 00000000..d31a4307 --- /dev/null +++ b/cli/common/interfaces.go @@ -0,0 +1,91 @@ +package common + +import ( + "os" + + "github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings" + "github.com/spf13/cobra" +) + +type Logger interface { + SetOutput(*os.File) + Debug(errorCode int8, errorMessage string) + Info(errorCode int8, errorMessage string) + Warn(errorCode int8, errorMessage string) + Error(errorCode int8, errorMessage string) + Fatal(errorCode int8, errorMessage string) + Infof(format string, errorCode int8, errorMessage string) + Warnf(format string, errorCode int8, errorMessage string) + Errorf(format string, errorCode int8, errorMessage string) + Fatalf(format string, errorCode int8, errorMessage string) +} + +type Spinner interface { + SetMessage(message string, color string) + SetColor(color string) + Start(message string) + Stop(message string) +} + +type Context interface { + CheckSkippedInstructions() + CleanAll() + Clean(enclaveName string) + CreateEnclave(enclaveName string) + GetEnclaves() []string + GetSerializedData(response chan *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) (string, map[string]string, map[string]bool, error) + InitialiseKurtosisContext() + StopServices() + StopService() +} + +type FileHandler interface { + ReadFromFile(filePath string) ([]byte, error) + ReadFromJson(filePath string, obj interface{}) (string, error) + WriteToFile(filePath string, data []byte) error + WriteToJson(filePath string, data interface{}) error +} + +// CommandBuilder is an interface for building a Cobra command. +type CommandBuilder interface { + // AddCommand adds a subcommand to the command. + AddCommand(cmd *cobra.Command) CommandBuilder + + // Add Persistant Bool Flag + AddBoolPersistantFlag(p *bool, name string, value bool, usage string) CommandBuilder + + // Add Persistant Bool Flag with Short hand + AddBoolPersistantFlagWithShortHand(p *bool, name string, value bool, usage string, shorthand string) CommandBuilder + + // Add Persistant String Flag + AddStringPersistantFlag(p *string, name string, value string, usage string) CommandBuilder + + // Add Persistant String Flag with Short hand + AddStringPersistantFlagWithShortHand(p *string, name string, shorthand string, value string, usage string) CommandBuilder + + // Add StringFlag adds a string flag to the command that persists + AddStringFlag(name string, value string, usage string) CommandBuilder + + // Add StringFlag adds a string flag to the command that persists with short hand + AddStringFlagWithShortHand(p *string, name string, shorthand string, value string, usage string) CommandBuilder + + // Add BooFlag adds a boolean flag to the command that persists + AddBoolFlag(name string, value bool, usage string) CommandBuilder + + AddBoolFlagWithShortHand(name string, shorthand string, value bool, usage string) CommandBuilder + + // Build constructs and returns the Cobra command. + Build() *cobra.Command + + // SetUse sets the Use field of the command. + SetUse(use string) CommandBuilder + + // SetShort sets the Short field of the command. + SetShort(short string) CommandBuilder + + // SetLong sets the Long field of the command. + SetLong(long string) CommandBuilder + + // SetRun sets the Run field of the command. + SetRun(run func(cmd *cobra.Command, args []string)) CommandBuilder +} diff --git a/cli/common/logs.go b/cli/common/logs.go new file mode 100644 index 00000000..805d0c79 --- /dev/null +++ b/cli/common/logs.go @@ -0,0 +1 @@ +package common diff --git a/cli/common/spinner.go b/cli/common/spinner.go new file mode 100644 index 00000000..805d0c79 --- /dev/null +++ b/cli/common/spinner.go @@ -0,0 +1 @@ +package common diff --git a/cli/common/types.go b/cli/common/types.go index 63fb17a3..ed4e395f 100644 --- a/cli/common/types.go +++ b/cli/common/types.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "runtime" + "time" "github.com/google/go-github/github" "github.com/kurtosis-tech/stacktrace" @@ -18,6 +19,9 @@ import ( "github.com/sirupsen/logrus" ) +var lastChecked time.Time +var latestVersion = "" + type DiveserviceResponse struct { ServiceName string `json:"service_name,omitempty"` PublicEndpoint string `json:"endpoint_public,omitempty"` @@ -74,17 +78,63 @@ func GetLatestVersion() string { // Repo Name repo := "DIVE" owner := "HugoByte" + userHomeDir, err := os.UserHomeDir() - // Create a new github client - client := github.NewClient(nil) - release, _, err := client.Repositories.GetLatestRelease(context.Background(), owner, repo) if err != nil { fmt.Println(err) return "" } + cachedFile := filepath.Join(userHomeDir, "/.dive/version_cache.txt") + + if time.Since(lastChecked).Hours() > 1 { + cachedVersion, err := ReadConfigFile(cachedFile) + fmt.Println("here ") + + if err == nil && string(cachedVersion) != "" { + latestVersion = string(cachedVersion) + fmt.Println("here 1") + } else { + fmt.Println("here 2") + client := github.NewClient(nil) + release, _, err := client.Repositories.GetLatestRelease(context.Background(), owner, repo) + if err != nil { + fmt.Println(err) + return "" + } + + latestVersion = release.GetName() + writeCache(cachedFile, latestVersion) + } + lastChecked = time.Now() + + } + + return latestVersion +} + +func writeCache(filePath string, latestVersion string) { + // Extract the directory path from the file path + dir := filepath.Dir(filePath) + + // Create the directory if it does not exist + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + fmt.Println("Error creating directory:", err) + return + } + + // Write the latest version to the cache file + file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Println("Error opening file:", err) + return + } - // Print the release version. - return release.GetName() + defer file.Close() + + _, err = file.WriteString(latestVersion) + if err != nil { + fmt.Println("Error writing to cache:", err) + } } func ReadConfigFile(filePath string) ([]byte, error) { @@ -146,7 +196,7 @@ func setupLogger() *logrus.Logger { }) ditFilePath := pwd + DiveLogDirectory + DiveDitLogFile - errorFilePath := pwd + DiveLogDirectory + DiveErorLogFile + errorFilePath := pwd + DiveLogDirectory + DiveErrorLogFile ditLogger := &lumberjack.Logger{ // Log file abbsolute path, os agnostic From 969106182dd9351c234ad5818c05b493b3ea144e Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Wed, 29 Nov 2023 13:46:21 +0530 Subject: [PATCH 2/5] feat: add error codes --- cli/common/errors.go | 299 ++++++++++++++++++++++++++++++++++++++ cli/common/errors_test.go | 61 ++++++++ cli/common/interfaces.go | 15 +- cli/go.mod | 1 + 4 files changed, 369 insertions(+), 7 deletions(-) create mode 100644 cli/common/errors.go create mode 100644 cli/common/errors_test.go diff --git a/cli/common/errors.go b/cli/common/errors.go new file mode 100644 index 00000000..bc25f479 --- /dev/null +++ b/cli/common/errors.go @@ -0,0 +1,299 @@ +package common + +import ( + "fmt" + "reflect" + + "github.com/pkg/errors" +) + +type ErrorCode int + +const ( + ErrorCodeGeneral ErrorCode = iota + 1000 +) + +const ( + UnknownError ErrorCode = ErrorCodeGeneral + iota + InvalidEnclaveNameError + UnsupportedOSError + FileError + InvalidCommandError + InvalidEnclaveContextError + InvalidEnclaveConfigError +) + +var ( + ErrUnknown = NewBase(UnknownError, "UnknownError") + ErrInvalidEnclaveName = NewBase(InvalidEnclaveNameError, "InvalidEnclaveName") + ErrUnsupportedOS = NewBase(UnsupportedOSError, "UnsupportedOS") + ErrFile = NewBase(FileError, "FileError") + ErrInvalidCommand = NewBase(InvalidCommandError, "InvalidCommand") + ErrInvalidEnclaveContext = NewBase(InvalidEnclaveContextError, "InvalidEnclaveContext") + ErrInvalidEnclaveConfig = NewBase(InvalidEnclaveConfigError, "InvalidEnclaveConfig") +) + +func (c ErrorCode) New(msg string) error { + return Errorc(c, msg) +} + +func (c ErrorCode) Errorf(f string, args ...interface{}) error { + return Errorcf(c, f, args...) +} + +func (c ErrorCode) Wrap(e error, msg string) error { + return WrapCodeToError(e, c, msg) +} + +func (c ErrorCode) Wrapf(e error, f string, args ...interface{}) error { + return WrapCodeToErrorf(e, c, f, args...) +} + +func (c ErrorCode) Equals(e error) bool { + if e == nil { + return false + } + return CodeOf(e) == c +} + +// Represent Error With Code and Message + +type baseError struct { + code ErrorCode + msg string +} + +func (e *baseError) Error() string { + return e.msg +} + +func (e *baseError) ErrorCode() ErrorCode { + return e.code +} + +func (e *baseError) Format(f fmt.State, c rune) { + switch c { + case 'v', 's', 'q': + fmt.Fprintf(f, "E%04d:%s", e.code, e.msg) + } +} + +func (e *baseError) Equals(err error) bool { + if err == nil { + return false + } + return CodeOf(err) == e.code +} + +func NewBase(code ErrorCode, msg string) *baseError { + return &baseError{code, msg} +} + +/* +Associate an error code with a standard Go error. +Create a new error with a code and a message +*/ + +type codedError struct { + code ErrorCode + error +} + +func (e *codedError) ErrorCode() ErrorCode { + return e.code +} + +func (e *codedError) Unwrap() error { + return e.error +} + +func Errorc(code ErrorCode, msg string) error { + return &codedError{ + code: code, + error: errors.New(msg), + } +} + +func Errorcf(code ErrorCode, f string, args ...interface{}) error { + return &codedError{ + code: code, + error: errors.Errorf(f, args...), + } +} + +func WithCode(err error, code ErrorCode) error { + if _, ok := CoderOf(err); ok { + return WrapCodeToError(err, code, err.Error()) + } + return &codedError{ + code: code, + error: err, + } +} + +/* + +Wrapping Existing Error with Additional Context like an error code and message. +Preserves Original error but adds the addtional Context to error messages +*/ + +type wrappedError struct { + error + code ErrorCode + origin error +} + +func (e *wrappedError) Format(f fmt.State, c rune) { + switch c { + case 'v': + if f.Flag('+') { + fmt.Fprintf(f, "E%04d:%+v", e.code, e.error) + fmt.Fprintf(f, "\nWrapping %+v", e.origin) + return + } + fallthrough + case 'q', 's': + fmt.Fprintf(f, "E%04d:%s", e.code, e.error) + } +} + +func (e *wrappedError) Unwrap() error { + return e.origin +} + +func (e *wrappedError) ErrorCode() ErrorCode { + return e.code +} + +func WrapCodeToError(e error, c ErrorCode, msg string) error { + return &wrappedError{ + error: errors.New(msg), + code: c, + origin: e, + } +} + +func WrapCodeToErrorf(e error, c ErrorCode, f string, args ...interface{}) error { + return &wrappedError{ + error: errors.Errorf(f, args...), + code: c, + origin: e, + } +} + +// To Add Messaage to an error without changing the error code. + +type messageError struct { + error + origin error +} + +func (e *messageError) Format(f fmt.State, c rune) { + switch c { + case 'v': + if f.Flag('+') { + fmt.Fprintf(f, "%+v", e.error) + fmt.Fprintf(f, "\nWrapping %+v", e.origin) + return + } + fallthrough + case 's', 'q': + fmt.Fprintf(f, "%s", e.error) + } +} + +func (e *messageError) Unwrap() error { + return e.origin +} + +func WrapMessageToError(e error, msg string) error { + return &messageError{ + error: errors.New(msg), + origin: e, + } +} + +func WrapMessageToErrorf(e error, f string, args ...interface{}) error { + return &messageError{ + error: errors.Errorf(f, args...), + origin: e, + } +} + +// Extract Code from Custom Error Messages +type ErrorCoder interface { + error + ErrorCode() ErrorCode +} + +func CoderOf(e error) (ErrorCoder, bool) { + var coder ErrorCoder + if AsValue(&coder, e) { + return coder, true + } + return nil, false +} + +func CodeOf(e error) ErrorCode { + if coder, ok := CoderOf(e); ok { + return coder.ErrorCode() + } + return UnknownError +} + +func AsValue(ptr interface{}, err error) bool { + type causer interface { + Cause() error + } + + type unwrapper interface { + Unwrap() error + } + + value := reflect.ValueOf(ptr) + if value.Kind() != reflect.Ptr { + return false + } else { + value = value.Elem() + } + valueType := value.Type() + + for { + errValue := reflect.ValueOf(err) + if errValue.Type().AssignableTo(valueType) { + value.Set(errValue) + return true + } + if cause, ok := err.(causer); ok { + err = cause.Cause() + } else if unwrap, ok := err.(unwrapper); ok { + err = unwrap.Unwrap() + } else { + return false + } + } +} + +// Is checks whether err is caused by the target. +func Is(err, target error) bool { + type causer interface { + Cause() error + } + + type unwrapper interface { + Unwrap() error + } + + for { + if err == target { + return true + } + if cause, ok := err.(causer); ok { + err = cause.Cause() + } else if unwrap, ok := err.(unwrapper); ok { + err = unwrap.Unwrap() + } else { + return false + } + } +} diff --git a/cli/common/errors_test.go b/cli/common/errors_test.go new file mode 100644 index 00000000..27695375 --- /dev/null +++ b/cli/common/errors_test.go @@ -0,0 +1,61 @@ +package common + +import ( + "testing" +) + +func TestNewBase(t *testing.T) { + + err := NewBase(UnknownError, "UnknownError") + + if c := CodeOf(err); c != UnknownError { + t.Error("Code of NewBase() isn't codeUnknownError") + } +} + +func TestCodedError(t *testing.T) { + msg := "Unknown Error Message" + err := Errorc(UnknownError, msg) + if c := CodeOf(err); c != UnknownError { + t.Errorf("Expected code of error to be %d, got %d", UnknownError, c) + } +} + +func TestWrap(t *testing.T) { + + err := Errorc(InvalidCommandError, "Usage of Invalid Command") + + err2 := WrapMessageToError(err, "Invalid Usage") + + if c := CodeOf(err2); c != InvalidCommandError { + t.Error("Code of Wrap() isn't codeInvalidCommandError") + } + + err3 := WrapCodeToError(WithCode(err, UnknownError), InvalidCommandError, "InvalidUsage") + + if c := CodeOf(err3); c != InvalidCommandError { + t.Errorf("Code of WithCode() isn't %d, got %d", InvalidCommandError, c) + } + +} + +func TestIs(t *testing.T) { + + error1 := Errorc(InvalidCommandError, "Usage of Invalid Command") + + error2 := WrapMessageToError(error1, "Invalid Usage") + + if Is(error1, error2) { + t.Error("error1 is not originated from error2") + } + + if !Is(error2, error1) { + t.Errorf("error2 is originated from error1") + } + + error3 := WrapCodeToError(WithCode(error2, UnknownError), InvalidCommandError, "Invalid Usage") + + if !Is(error3, error1) { + t.Errorf("error3 is originated from error1") + } +} diff --git a/cli/common/interfaces.go b/cli/common/interfaces.go index d31a4307..6ec34cda 100644 --- a/cli/common/interfaces.go +++ b/cli/common/interfaces.go @@ -9,13 +9,14 @@ import ( type Logger interface { SetOutput(*os.File) - Debug(errorCode int8, errorMessage string) - Info(errorCode int8, errorMessage string) - Warn(errorCode int8, errorMessage string) - Error(errorCode int8, errorMessage string) - Fatal(errorCode int8, errorMessage string) - Infof(format string, errorCode int8, errorMessage string) - Warnf(format string, errorCode int8, errorMessage string) + Debug(message string) + Info(message string) + Warn(message string) + Error(errorCode int, errorMessage string) + Fatal(errorCode int, errorMessage string) + Infof(message string) + Warnf(message string) + Debugf(message string) Errorf(format string, errorCode int8, errorMessage string) Fatalf(format string, errorCode int8, errorMessage string) } diff --git a/cli/go.mod b/cli/go.mod index 8b543105..149c9ca1 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -46,6 +46,7 @@ require ( github.com/mholt/archiver/v3 v3.5.1 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect + github.com/pkg/errors v0.9.1 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.11 // indirect From a1d98ef3f30d31e3b0e9655b88b4110606e291af Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Wed, 29 Nov 2023 15:18:20 +0530 Subject: [PATCH 3/5] feat: update unit tests --- cli/common/errors_test.go | 99 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/cli/common/errors_test.go b/cli/common/errors_test.go index 27695375..9c4da8fc 100644 --- a/cli/common/errors_test.go +++ b/cli/common/errors_test.go @@ -1,6 +1,7 @@ package common import ( + "errors" "testing" ) @@ -21,20 +22,20 @@ func TestCodedError(t *testing.T) { } } -func TestWrap(t *testing.T) { +func TestWrapMessage(t *testing.T) { - err := Errorc(InvalidCommandError, "Usage of Invalid Command") + error1 := Errorc(InvalidCommandError, "Usage of Invalid Command") - err2 := WrapMessageToError(err, "Invalid Usage") + error2 := WrapMessageToError(error1, "Invalid Usage") - if c := CodeOf(err2); c != InvalidCommandError { + if c := CodeOf(error2); c != InvalidCommandError { t.Error("Code of Wrap() isn't codeInvalidCommandError") } - err3 := WrapCodeToError(WithCode(err, UnknownError), InvalidCommandError, "InvalidUsage") + err3 := WithCode(error1, UnknownError) - if c := CodeOf(err3); c != InvalidCommandError { - t.Errorf("Code of WithCode() isn't %d, got %d", InvalidCommandError, c) + if c := CodeOf(err3); c != UnknownError { + t.Errorf("with code doesn't change the Error code") } } @@ -58,4 +59,88 @@ func TestIs(t *testing.T) { if !Is(error3, error1) { t.Errorf("error3 is originated from error1") } + + if Is(error1, error3) { + t.Errorf("error1 is originated from error3") + } +} + +func TestAsValue(t *testing.T) { + var coder ErrorCoder + if AsValue(&coder, Errorc(InvalidCommandError, "Test")) { + if coder == nil { + t.Error("Returned object is nil") + } + if c := coder.ErrorCode(); c != InvalidCommandError { + t.Error("Fail to find ErrorCoder") + } + } else { + t.Error("Fail to get ErrorCoder from result of Errorc()") + } +} + +func TestWithCode(t *testing.T) { + + errorCodes := []ErrorCode{ + InvalidCommandError, InvalidEnclaveNameError, + } + + tests := map[string]error{ + + "Errorc": Errorc(UnsupportedOSError, "OS Error"), + "NewBaseError": NewBase(FileError, "Invalid File"), + "WrapMessage": WrapMessageToError(Errorc(UnknownError, "Error Parsing Arguments"), "Invalid Usage"), + "WrapCode": WrapCodeToError(Errorc(UnsupportedOSError, "Unknown Platform"), UnsupportedOSError, "Error"), + "WithCode": WithCode(errors.New("test"), FileError), + } + + for errName, err := range tests { + t.Run(errName, func(t *testing.T) { + for _, code := range errorCodes { + error1 := WithCode(err, code) + if c := CodeOf(error1); code != c { + t.Errorf("Returned code=%d exp=%d", code, c) + } + } + }) + } +} + +func TestCodeOf(t *testing.T) { + tests := []struct { + name string + err error + expectedCode ErrorCode + }{ + { + "test_new_base", + NewBase(UnsupportedOSError, "Invalid OS"), + UnsupportedOSError, + }, + { + "test_with_code", + WithCode(errors.New("test"), FileError), + FileError, + }, + { + "test_with_wrapwessage", + WrapMessageToError(Errorc(UnknownError, "Error Parsing Arguments"), "Invalid Usage"), + UnknownError, + }, + { + "test_with_wrap_code", + WrapCodeToError(Errorc(UnsupportedOSError, "Unknown Platform"), UnsupportedOSError, "Error"), + UnsupportedOSError, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + code := CodeOf(test.err) + + if code != test.expectedCode { + t.Errorf("Expected %d got %d", test.expectedCode, code) + } + }) + } } From 597c2ed62fdf4b62a47f90b6f56b4b8a1bba3b8e Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Wed, 29 Nov 2023 18:13:01 +0530 Subject: [PATCH 4/5] feat: implement logger and spinner interface --- cli/common/context.go | 25 ++++++++ cli/common/interfaces.go | 13 ++-- cli/common/logs.go | 133 +++++++++++++++++++++++++++++++++++++++ cli/common/spinner.go | 24 +++++++ 4 files changed, 188 insertions(+), 7 deletions(-) diff --git a/cli/common/context.go b/cli/common/context.go index 3427388e..7411fc76 100644 --- a/cli/common/context.go +++ b/cli/common/context.go @@ -18,6 +18,31 @@ import ( log "github.com/sirupsen/logrus" ) +type Dive struct { + log Logger + spinner Spinner + context Context +} + +func InitDive() *Dive { + logger := NewDiveLogger("file.txt", "error.txt") + + return &Dive{log: logger} +} + +func (d *Dive) Log() Logger { + + return d.log +} + +func (d *Dive) Spinner() Spinner { + return d.spinner +} + +func (d *Dive) Context() Context { + return d.context +} + type DiveContext struct { Ctx context.Context KurtosisContext *kurtosis_context.KurtosisContext diff --git a/cli/common/interfaces.go b/cli/common/interfaces.go index 6ec34cda..4b342924 100644 --- a/cli/common/interfaces.go +++ b/cli/common/interfaces.go @@ -1,24 +1,23 @@ package common import ( - "os" - "github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings" "github.com/spf13/cobra" ) type Logger interface { - SetOutput(*os.File) + SetErrorToStderr() + SetOutputToStdout() Debug(message string) Info(message string) Warn(message string) - Error(errorCode int, errorMessage string) - Fatal(errorCode int, errorMessage string) + Error(errorCode ErrorCode, errorMessage string) + Fatal(errorCode ErrorCode, errorMessage string) Infof(message string) Warnf(message string) Debugf(message string) - Errorf(format string, errorCode int8, errorMessage string) - Fatalf(format string, errorCode int8, errorMessage string) + Errorf(errorCode ErrorCode, errorMessage string) + Fatalf(errorCode ErrorCode, errorMessage string) } type Spinner interface { diff --git a/cli/common/logs.go b/cli/common/logs.go index 805d0c79..5ee15f03 100644 --- a/cli/common/logs.go +++ b/cli/common/logs.go @@ -1 +1,134 @@ package common + +import ( + "io" + "os" + "path/filepath" + + "github.com/natefinch/lumberjack" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" +) + +type diveLogger struct { + log *logrus.Logger +} + +func NewDiveLogger(infoFilePath string, errorFilePath string) *diveLogger { + + log := logrus.New() + + log.SetOutput(io.Discard) + log.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: "2006-01-02 15:04:05", + FullTimestamp: true, + ForceColors: true, + PadLevelText: true, + }) + + ditLogger := &lumberjack.Logger{ + // Log file abbsolute path, os agnostic + Filename: filepath.ToSlash(infoFilePath), + LocalTime: true, + } + + // Fork writing into two outputs + ditWriter := io.MultiWriter(ditLogger) + + errorLogger := &lumberjack.Logger{ + Filename: filepath.ToSlash(errorFilePath), + LocalTime: true, + } + + // Fork writing into two outputs + errorWriter := io.MultiWriter(errorLogger) + + log.AddHook(lfshook.NewHook( + lfshook.WriterMap{ + logrus.InfoLevel: ditWriter, + logrus.DebugLevel: ditWriter, + logrus.TraceLevel: ditWriter, + logrus.WarnLevel: ditWriter, + logrus.ErrorLevel: errorWriter, + logrus.FatalLevel: errorWriter, + }, + &logrus.JSONFormatter{ + TimestampFormat: "2006-01-02 15:04:05", + }, + )) + + return &diveLogger{log: log} +} + +func (d *diveLogger) SetErrorToStderr() { + d.log.SetOutput(os.Stderr) +} +func (d *diveLogger) SetOutputToStdout() { + d.log.SetOutput(os.Stdout) +} +func (d *diveLogger) Debug(message string) { + + d.log.WithFields(logrus.Fields{ + "level": "🐞 debug", + }).Debug(message) + +} +func (d *diveLogger) Info(message string) { + d.log.WithFields(logrus.Fields{ + "level": "ℹī¸ info", + }).Info(message) + +} +func (d *diveLogger) Warn(message string) { + d.log.WithFields(logrus.Fields{ + "level": "⚠ī¸ warn", + }).Warn(message) + +} +func (d *diveLogger) Error(errorCode ErrorCode, errorMessage string) { + d.log.WithFields(logrus.Fields{ + "level": "🛑 error", + "error_code": errorCode, + }).Error(errorMessage) + +} +func (d *diveLogger) Fatal(errorCode ErrorCode, errorMessage string) { + d.log.WithFields(logrus.Fields{ + "level": "💀 fatal", + "error_code": errorCode, + }).Fatal(errorMessage) + +} +func (d *diveLogger) Infof(message string) { + d.log.WithFields(logrus.Fields{ + "level": "ℹī¸ info", + }).Infof("%s", message) + +} +func (d *diveLogger) Warnf(message string) { + d.log.WithFields(logrus.Fields{ + "level": "⚠ī¸ warn", + }).Warnf("%s", message) + +} +func (d *diveLogger) Debugf(message string) { + + d.log.WithFields(logrus.Fields{ + "level": "🐞 debug", + }).Debugf("%s", message) + +} +func (d *diveLogger) Errorf(errorCode ErrorCode, errorMessage string) { + d.log.WithFields(logrus.Fields{ + "level": "🛑 error", + "error_code": errorCode, + }).Errorf("%s", errorMessage) + +} +func (d *diveLogger) Fatalf(errorCode ErrorCode, errorMessage string) { + d.log.WithFields(logrus.Fields{ + "level": "💀 fatal", + "error_code": errorCode, + }).Fatalf("%s", errorMessage) + +} diff --git a/cli/common/spinner.go b/cli/common/spinner.go index 805d0c79..033408f6 100644 --- a/cli/common/spinner.go +++ b/cli/common/spinner.go @@ -1 +1,25 @@ package common + +import ( + "os" + "time" + + "github.com/briandowns/spinner" +) + +type diveSpinner struct { + spinner *spinner.Spinner +} + +func NewDiveSpinner() *diveSpinner { + + spinner := spinner.New(spinner.CharSets[80], 100*time.Millisecond, spinner.WithWriter(os.Stdin)) + + return &diveSpinner{spinner: spinner} + +} + +func (s *diveSpinner) Start(message string) {} +func (s *diveSpinner) Stop() {} +func (s *diveSpinner) SetMessage(message string) {} +func (s *diveSpinner) SetColor(color string) {} From 6ddc583d6f233484ccea556111c483e31840badd Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Thu, 30 Nov 2023 11:07:22 +0530 Subject: [PATCH 5/5] feat: update file handler interface --- cli/common/files.go | 30 ++++++++++++++++++++++++++++++ cli/common/interfaces.go | 13 +++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/cli/common/files.go b/cli/common/files.go index 805d0c79..6a42a539 100644 --- a/cli/common/files.go +++ b/cli/common/files.go @@ -1 +1,31 @@ package common + +import "os" + +type diveFileHandler struct{} + +func NewDiveFileHandler() *diveFileHandler { + return &diveFileHandler{} +} + +func (f *diveFileHandler) ReadFile(filePath string) ([]byte, error) { + return nil, nil +} +func (f *diveFileHandler) ReadJson(filePath string, obj interface{}) (string, error) { + return "nil", nil +} +func (f *diveFileHandler) WriteFile(filePath string, data []byte) error { + return nil +} +func (f *diveFileHandler) WriteJson(filePath string, data interface{}) error { + return nil +} +func (f *diveFileHandler) GetPwd() string { + return "" +} +func (f *diveFileHandler) MkdirAll(dirPath string, permission string) error { + return nil +} +func (f *diveFileHandler) OpenFile(filePath string, fileOpenMode string, permission int) (*os.File, error) { + return nil, nil +} diff --git a/cli/common/interfaces.go b/cli/common/interfaces.go index 4b342924..6ed54d8c 100644 --- a/cli/common/interfaces.go +++ b/cli/common/interfaces.go @@ -1,6 +1,8 @@ package common import ( + "os" + "github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings" "github.com/spf13/cobra" ) @@ -40,10 +42,13 @@ type Context interface { } type FileHandler interface { - ReadFromFile(filePath string) ([]byte, error) - ReadFromJson(filePath string, obj interface{}) (string, error) - WriteToFile(filePath string, data []byte) error - WriteToJson(filePath string, data interface{}) error + ReadFile(filePath string) ([]byte, error) + ReadJson(filePath string, obj interface{}) (string, error) + WriteFile(filePath string, data []byte) error + WriteJson(filePath string, data interface{}) error + GetPwd() string + MkdirAll(dirPath string, permission string) error + OpenFile(filePath string, fileOpenMode string, permission int) (*os.File, error) } // CommandBuilder is an interface for building a Cobra command.