mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-06-21 22:08:59 +00:00
Introducing mizu logs dump & Log prints alignment in API server using rlog (#165)
This commit is contained in:
parent
04579eb03c
commit
fa632b49a7
@ -2,9 +2,9 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/romana/rlog"
|
||||||
"github.com/up9inc/mizu/shared/debounce"
|
"github.com/up9inc/mizu/shared/debounce"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
@ -50,7 +50,7 @@ func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers) {
|
|||||||
func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers EventHandlers, isTapper bool) {
|
func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers EventHandlers, isTapper bool) {
|
||||||
conn, err := websocketUpgrader.Upgrade(w, r, nil)
|
conn, err := websocketUpgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to set websocket upgrade: %+v", err)
|
rlog.Errorf("Failed to set websocket upgrade: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
for {
|
for {
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Conn err: %v\n", err)
|
rlog.Errorf("Error reading message, socket id: %d, error: %v", socketId, err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
eventHandlers.WebSocketMessage(socketId, msg)
|
eventHandlers.WebSocketMessage(socketId, msg)
|
||||||
@ -81,7 +81,7 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
func socketCleanup(socketId int, socketConnection *SocketConnection) {
|
func socketCleanup(socketId int, socketConnection *SocketConnection) {
|
||||||
err := socketConnection.connection.Close()
|
err := socketConnection.connection.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error closing socket connection for socket id %d: %v\n", socketId, err)
|
rlog.Errorf("Error closing socket connection for socket id %d: %v\n", socketId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketIdsLock.Lock()
|
websocketIdsLock.Lock()
|
||||||
@ -92,7 +92,7 @@ func socketCleanup(socketId int, socketConnection *SocketConnection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var db = debounce.NewDebouncer(time.Second*5, func() {
|
var db = debounce.NewDebouncer(time.Second*5, func() {
|
||||||
fmt.Println("Successfully sent to socket")
|
rlog.Error("Successfully sent to socket")
|
||||||
})
|
})
|
||||||
|
|
||||||
func SendToSocket(socketId int, message []byte) error {
|
func SendToSocket(socketId int, message []byte) error {
|
||||||
@ -104,7 +104,7 @@ func SendToSocket(socketId int, message []byte) error {
|
|||||||
var sent = false
|
var sent = false
|
||||||
time.AfterFunc(time.Second*5, func() {
|
time.AfterFunc(time.Second*5, func() {
|
||||||
if !sent {
|
if !sent {
|
||||||
fmt.Println("Socket timed out")
|
rlog.Error("Socket timed out")
|
||||||
socketCleanup(socketId, socketObj)
|
socketCleanup(socketId, socketObj)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -52,7 +52,7 @@ func BroadcastToBrowserClients(message []byte) {
|
|||||||
go func(socketId int) {
|
go func(socketId int) {
|
||||||
err := SendToSocket(socketId, message)
|
err := SendToSocket(socketId, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error sending message to socket ID %d: %v", socketId, err)
|
rlog.Errorf("error sending message to socket ID %d: %v", socketId, err)
|
||||||
}
|
}
|
||||||
}(socketId)
|
}(socketId)
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func handleTLSLink(outboundLinkMessage models.WebsocketOutboundLinkMessage) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
rlog.Errorf("Error marshaling outbound link message for broadcasting: %v", err)
|
rlog.Errorf("Error marshaling outbound link message for broadcasting: %v", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Broadcasting outboundlink message %s\n", string(marshaledMessage))
|
rlog.Errorf("Broadcasting outboundlink message %s", string(marshaledMessage))
|
||||||
BroadcastToBrowserClients(marshaledMessage)
|
BroadcastToBrowserClients(marshaledMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/romana/rlog"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/debounce"
|
"github.com/up9inc/mizu/shared/debounce"
|
||||||
"github.com/up9inc/mizu/shared/units"
|
"github.com/up9inc/mizu/shared/units"
|
||||||
@ -47,7 +47,7 @@ func StartEnforcingDatabaseSize() {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return // closed channel
|
return // closed channel
|
||||||
}
|
}
|
||||||
fmt.Printf("filesystem watcher encountered error:%v\n", err)
|
rlog.Errorf("filesystem watcher encountered error:%v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -72,7 +72,7 @@ func getMaxEntriesDBByteSize() (int64, error) {
|
|||||||
func checkFileSize(maxSizeBytes int64) {
|
func checkFileSize(maxSizeBytes int64) {
|
||||||
fileStat, err := os.Stat(DBPath)
|
fileStat, err := os.Stat(DBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error checking %s file size: %v\n", DBPath, err)
|
rlog.Errorf("Error checking %s file size: %v", DBPath, err)
|
||||||
} else {
|
} else {
|
||||||
if fileStat.Size() > maxSizeBytes {
|
if fileStat.Size() > maxSizeBytes {
|
||||||
pruneOldEntries(fileStat.Size())
|
pruneOldEntries(fileStat.Size())
|
||||||
@ -83,13 +83,13 @@ func checkFileSize(maxSizeBytes int64) {
|
|||||||
func pruneOldEntries(currentFileSize int64) {
|
func pruneOldEntries(currentFileSize int64) {
|
||||||
// sqlite locks the database while delete or VACUUM are running and sqlite is terrible at handling its own db lock while a lot of inserts are attempted, we prevent a significant bottleneck by handling the db lock ourselves here
|
// sqlite locks the database while delete or VACUUM are running and sqlite is terrible at handling its own db lock while a lot of inserts are attempted, we prevent a significant bottleneck by handling the db lock ourselves here
|
||||||
IsDBLocked = true
|
IsDBLocked = true
|
||||||
defer func() {IsDBLocked = false}()
|
defer func() { IsDBLocked = false }()
|
||||||
|
|
||||||
amountOfBytesToTrim := currentFileSize / (100 / percentageOfMaxSizeBytesToPrune)
|
amountOfBytesToTrim := currentFileSize / (100 / percentageOfMaxSizeBytesToPrune)
|
||||||
|
|
||||||
rows, err := GetEntriesTable().Limit(10000).Order("id").Rows()
|
rows, err := GetEntriesTable().Limit(10000).Order("id").Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error getting 10000 first db rows: %v\n", err)
|
rlog.Errorf("Error getting 10000 first db rows: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ func pruneOldEntries(currentFileSize int64) {
|
|||||||
var entry models.MizuEntry
|
var entry models.MizuEntry
|
||||||
err = DB.ScanRows(rows, &entry)
|
err = DB.ScanRows(rows, &entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error scanning db row: %v\n", err)
|
rlog.Errorf("Error scanning db row: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,8 +114,8 @@ func pruneOldEntries(currentFileSize int64) {
|
|||||||
GetEntriesTable().Where(entryIdsToRemove).Delete(models.MizuEntry{})
|
GetEntriesTable().Where(entryIdsToRemove).Delete(models.MizuEntry{})
|
||||||
// VACUUM causes sqlite to shrink the db file after rows have been deleted, the db file will not shrink without this
|
// VACUUM causes sqlite to shrink the db file after rows have been deleted, the db file will not shrink without this
|
||||||
DB.Exec("VACUUM")
|
DB.Exec("VACUUM")
|
||||||
fmt.Printf("Removed %d rows and cleared %s\n", len(entryIdsToRemove), units.BytesToHumanReadable(bytesToBeRemoved))
|
rlog.Errorf("Removed %d rows and cleared %s", len(entryIdsToRemove), units.BytesToHumanReadable(bytesToBeRemoved))
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Found no rows to remove when pruning")
|
rlog.Error("Found no rows to remove when pruning")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ Now you will be able to import `github.com/up9inc/mizu/resolver` in any `.go` fi
|
|||||||
errOut := make(chan error, 100)
|
errOut := make(chan error, 100)
|
||||||
k8sResolver, err := resolver.NewFromOutOfCluster("", errOut)
|
k8sResolver, err := resolver.NewFromOutOfCluster("", errOut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error creating k8s resolver %s", err)
|
rlog.Errorf("error creating k8s resolver %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@ -40,15 +40,15 @@ k8sResolver.Start(ctx)
|
|||||||
|
|
||||||
resolvedName := k8sResolver.Resolve("10.107.251.91") // will always return `nil` in real scenarios as the internal map takes a moment to populate after `Start` is called
|
resolvedName := k8sResolver.Resolve("10.107.251.91") // will always return `nil` in real scenarios as the internal map takes a moment to populate after `Start` is called
|
||||||
if resolvedName != nil {
|
if resolvedName != nil {
|
||||||
fmt.Printf("resolved 10.107.251.91=%s", *resolvedName)
|
rlog.Errorf("resolved 10.107.251.91=%s", *resolvedName)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Could not find a resolved name for 10.107.251.91")
|
rlog.Error("Could not find a resolved name for 10.107.251.91")
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <- errOut:
|
case err := <- errOut:
|
||||||
fmt.Printf("name resolving error %s", err)
|
rlog.Errorf("name resolving error %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -3,6 +3,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/romana/rlog"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
"gorm.io/gorm/utils"
|
"gorm.io/gorm/utils"
|
||||||
"time"
|
"time"
|
||||||
@ -10,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// TruncatingLogger implements the gorm logger.Interface interface. Its purpose is to act as gorm's logger while truncating logs to a max of 50 characters to minimise the performance impact
|
// TruncatingLogger implements the gorm logger.Interface interface. Its purpose is to act as gorm's logger while truncating logs to a max of 50 characters to minimise the performance impact
|
||||||
type TruncatingLogger struct {
|
type TruncatingLogger struct {
|
||||||
LogLevel logger.LogLevel
|
LogLevel logger.LogLevel
|
||||||
SlowThreshold time.Duration
|
SlowThreshold time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,21 +24,21 @@ func (truncatingLogger *TruncatingLogger) Info(_ context.Context, message string
|
|||||||
if truncatingLogger.LogLevel < logger.Info {
|
if truncatingLogger.LogLevel < logger.Info {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("gorm info: %.150s\n", message)
|
rlog.Errorf("gorm info: %.150s", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (truncatingLogger *TruncatingLogger) Warn(_ context.Context, message string, __ ...interface{}) {
|
func (truncatingLogger *TruncatingLogger) Warn(_ context.Context, message string, __ ...interface{}) {
|
||||||
if truncatingLogger.LogLevel < logger.Warn {
|
if truncatingLogger.LogLevel < logger.Warn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("gorm warning: %.150s\n", message)
|
rlog.Errorf("gorm warning: %.150s", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (truncatingLogger *TruncatingLogger) Error(_ context.Context, message string, __ ...interface{}) {
|
func (truncatingLogger *TruncatingLogger) Error(_ context.Context, message string, __ ...interface{}) {
|
||||||
if truncatingLogger.LogLevel < logger.Error {
|
if truncatingLogger.LogLevel < logger.Error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("gorm error: %.150s\n", message)
|
rlog.Errorf("gorm error: %.150s", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (truncatingLogger *TruncatingLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
func (truncatingLogger *TruncatingLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
||||||
|
46
cli/cmd/logs.go
Normal file
46
cli/cmd/logs.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/up9inc/mizu/cli/kubernetes"
|
||||||
|
"github.com/up9inc/mizu/cli/logsUtils"
|
||||||
|
"github.com/up9inc/mizu/cli/mizu"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
var filePath string
|
||||||
|
|
||||||
|
var logsCmd = &cobra.Command{
|
||||||
|
Use: "logs",
|
||||||
|
Short: "Create a zip file with logs for Github issue or troubleshoot",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.View.KubeConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx, _ := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
if filePath == "" {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
mizu.Log.Errorf("Failed to get PWD, %v (try using `mizu logs -f <full path dest zip file>)`", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
filePath = path.Join(pwd, "mizu_logs.zip")
|
||||||
|
}
|
||||||
|
mizu.Log.Debugf("Using file path %s", filePath)
|
||||||
|
|
||||||
|
if err := logsUtils.DumpLogs(kubernetesProvider, ctx, filePath); err != nil {
|
||||||
|
mizu.Log.Errorf("Failed dump logs %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(logsCmd)
|
||||||
|
logsCmd.Flags().StringVarP(&filePath, "file", "f", "", "Path for zip file (default current <pwd>\\mizu_logs.zip)")
|
||||||
|
}
|
@ -9,12 +9,14 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/cli/errormessage"
|
"github.com/up9inc/mizu/cli/errormessage"
|
||||||
"github.com/up9inc/mizu/cli/kubernetes"
|
"github.com/up9inc/mizu/cli/kubernetes"
|
||||||
|
"github.com/up9inc/mizu/cli/logsUtils"
|
||||||
"github.com/up9inc/mizu/cli/mizu"
|
"github.com/up9inc/mizu/cli/mizu"
|
||||||
"github.com/up9inc/mizu/cli/uiUtils"
|
"github.com/up9inc/mizu/cli/uiUtils"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
@ -22,7 +24,6 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
core "k8s.io/api/core/v1"
|
core "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -56,14 +57,8 @@ func RunMizuTap() {
|
|||||||
|
|
||||||
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.Tap.KubeConfigPath)
|
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.Tap.KubeConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if clientcmd.IsEmptyConfig(err) {
|
mizu.Log.Error(err)
|
||||||
mizu.Log.Errorf(uiUtils.Error, "Couldn't find the kube config file, or file is empty. Try adding '--kube-config=<path to kube config file>'\n")
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
if clientcmd.IsConfigurationInvalid(err) {
|
|
||||||
mizu.Log.Errorf(uiUtils.Error, "Invalid kube config file. Try using a different config with '--kube-config=<path to kube config file>'\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer cleanUpMizuResources(kubernetesProvider)
|
defer cleanUpMizuResources(kubernetesProvider)
|
||||||
@ -244,11 +239,20 @@ func updateMizuTappers(ctx context.Context, kubernetesProvider *kubernetes.Provi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cleanUpMizuResources(kubernetesProvider *kubernetes.Provider) {
|
func cleanUpMizuResources(kubernetesProvider *kubernetes.Provider) {
|
||||||
mizu.Log.Infof("\nRemoving mizu resources\n")
|
|
||||||
|
|
||||||
removalCtx, cancel := context.WithTimeout(context.Background(), cleanupTimeout)
|
removalCtx, cancel := context.WithTimeout(context.Background(), cleanupTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
if mizu.Config.DumpLogs {
|
||||||
|
mizuDir := mizu.GetMizuFolderPath()
|
||||||
|
filePath = path.Join(mizuDir, fmt.Sprintf("mizu_logs_%s.zip", time.Now().Format("2006_01_02__15_04_05")))
|
||||||
|
if err := logsUtils.DumpLogs(kubernetesProvider, removalCtx, filePath); err != nil {
|
||||||
|
mizu.Log.Errorf("Failed dump logs %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mizu.Log.Infof("\nRemoving mizu resources\n")
|
||||||
|
|
||||||
if mizu.Config.IsOwnNamespace() {
|
if mizu.Config.IsOwnNamespace() {
|
||||||
if err := kubernetesProvider.RemoveNamespace(removalCtx, mizu.Config.ResourcesNamespace()); err != nil {
|
if err := kubernetesProvider.RemoveNamespace(removalCtx, mizu.Config.ResourcesNamespace()); err != nil {
|
||||||
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error removing Namespace %s: %v", mizu.Config.ResourcesNamespace(), errormessage.FormatError(err)))
|
mizu.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error removing Namespace %s: %v", mizu.Config.ResourcesNamespace(), errormessage.FormatError(err)))
|
||||||
|
@ -3,25 +3,16 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/up9inc/mizu/cli/kubernetes"
|
"github.com/up9inc/mizu/cli/kubernetes"
|
||||||
"github.com/up9inc/mizu/cli/mizu"
|
"github.com/up9inc/mizu/cli/mizu"
|
||||||
"github.com/up9inc/mizu/cli/uiUtils"
|
"net/http"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func runMizuView() {
|
func runMizuView() {
|
||||||
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.View.KubeConfigPath)
|
kubernetesProvider, err := kubernetes.NewProvider(mizu.Config.View.KubeConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if clientcmd.IsEmptyConfig(err) {
|
mizu.Log.Error(err)
|
||||||
mizu.Log.Infof("Couldn't find the kube config file, or file is empty. Try adding '--kube-config=<path to kube config file>'")
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
if clientcmd.IsConfigurationInvalid(err) {
|
|
||||||
mizu.Log.Infof(uiUtils.Red, "Invalid kube config file. Try using a different config with '--kube-config=<path to kube config file>'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
_ "bytes"
|
_ "bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -12,13 +13,17 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/cli/mizu"
|
"github.com/up9inc/mizu/cli/mizu"
|
||||||
|
"io"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/util/homedir"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
core "k8s.io/api/core/v1"
|
core "k8s.io/api/core/v1"
|
||||||
rbac "k8s.io/api/rbac/v1"
|
rbac "k8s.io/api/rbac/v1"
|
||||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
resource "k8s.io/apimachinery/pkg/api/resource"
|
resource "k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
applyconfapp "k8s.io/client-go/applyconfigurations/apps/v1"
|
applyconfapp "k8s.io/client-go/applyconfigurations/apps/v1"
|
||||||
@ -30,11 +35,9 @@ import (
|
|||||||
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
_ "k8s.io/client-go/tools/portforward"
|
_ "k8s.io/client-go/tools/portforward"
|
||||||
watchtools "k8s.io/client-go/tools/watch"
|
watchtools "k8s.io/client-go/tools/watch"
|
||||||
"k8s.io/client-go/util/homedir"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
@ -52,7 +55,12 @@ func NewProvider(kubeConfigPath string) (*Provider, error) {
|
|||||||
kubernetesConfig := loadKubernetesConfiguration(kubeConfigPath)
|
kubernetesConfig := loadKubernetesConfiguration(kubeConfigPath)
|
||||||
restClientConfig, err := kubernetesConfig.ClientConfig()
|
restClientConfig, err := kubernetesConfig.ClientConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if clientcmd.IsEmptyConfig(err) {
|
||||||
|
return nil, fmt.Errorf("Couldn't find the kube config file, or file is empty. Try adding '--kube-config=<path to kube config file>'\n")
|
||||||
|
}
|
||||||
|
if clientcmd.IsConfigurationInvalid(err) {
|
||||||
|
return nil, fmt.Errorf("Invalid kube config file. Try using a different config with '--kube-config=<path to kube config file>'\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clientSet := getClientSet(restClientConfig)
|
clientSet := getClientSet(restClientConfig)
|
||||||
|
|
||||||
@ -551,10 +559,22 @@ func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string,
|
|||||||
if _, err := provider.clientSet.CoreV1().ConfigMaps(namespace).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
|
if _, err := provider.clientSet.CoreV1().ConfigMaps(namespace).Create(ctx, configMap, metav1.CreateOptions{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) ListPods(ctx context.Context, namespace string) ([]shared.PodInfo, error) {
|
||||||
|
podInfos := make([]shared.PodInfo, 0)
|
||||||
|
listOptions := metav1.ListOptions{}
|
||||||
|
pods, err := provider.clientSet.CoreV1().Pods(namespace).List(ctx, listOptions)
|
||||||
|
if err != nil {
|
||||||
|
return podInfos, fmt.Errorf("error getting pods in ns: %s, %w", namespace, err)
|
||||||
|
}
|
||||||
|
for _, pod := range pods.Items {
|
||||||
|
podInfos = append(podInfos, shared.PodInfo{Name: pod.Name, Namespace: pod.Namespace})
|
||||||
|
}
|
||||||
|
return podInfos, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodIPMap map[string][]string, serviceAccountName string, tapOutgoing bool) error {
|
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodIPMap map[string][]string, serviceAccountName string, tapOutgoing bool) error {
|
||||||
mizu.Log.Debugf("Applying %d tapper deamonsets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodIPMap), namespace, daemonSetName, podImage, tapperPodName)
|
mizu.Log.Debugf("Applying %d tapper deamonsets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodIPMap), namespace, daemonSetName, podImage, tapperPodName)
|
||||||
|
|
||||||
@ -685,6 +705,22 @@ func (provider *Provider) GetAllRunningPodsMatchingRegex(ctx context.Context, re
|
|||||||
return matchingPods, nil
|
return matchingPods, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetPodLogs(namespace string, podName string, ctx context.Context) (string, error) {
|
||||||
|
podLogOpts := core.PodLogOptions{}
|
||||||
|
req := provider.clientSet.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts)
|
||||||
|
podLogs, err := req.Stream(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error opening log stream on ns: %s, pod: %s, %w", namespace, podName, err)
|
||||||
|
}
|
||||||
|
defer podLogs.Close()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if _, err = io.Copy(buf, podLogs); err != nil {
|
||||||
|
return "", fmt.Errorf("error copy information from podLogs to buf, ns: %s, pod: %s, %w", namespace, podName, err)
|
||||||
|
}
|
||||||
|
str := buf.String()
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getClientSet(config *restclient.Config) *kubernetes.Clientset {
|
func getClientSet(config *restclient.Config) *kubernetes.Clientset {
|
||||||
clientSet, err := kubernetes.NewForConfig(config)
|
clientSet, err := kubernetes.NewForConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
104
cli/logsUtils/mizuLogsUtils.go
Normal file
104
cli/logsUtils/mizuLogsUtils.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package logsUtils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/up9inc/mizu/cli/kubernetes"
|
||||||
|
"github.com/up9inc/mizu/cli/mizu"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DumpLogs(provider *kubernetes.Provider, ctx context.Context, filePath string) error {
|
||||||
|
pods, err := provider.ListPods(ctx, mizu.Config.ResourcesNamespace())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pods) == 0 {
|
||||||
|
return fmt.Errorf("no pods found in namespace %s", mizu.Config.ResourcesNamespace())
|
||||||
|
}
|
||||||
|
|
||||||
|
newZipFile, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer newZipFile.Close()
|
||||||
|
zipWriter := zip.NewWriter(newZipFile)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
for _, pod := range pods {
|
||||||
|
logs, err := provider.GetPodLogs(pod.Namespace, pod.Name, ctx)
|
||||||
|
if err != nil {
|
||||||
|
mizu.Log.Errorf("Failed to get logs, %v", err)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
mizu.Log.Debugf("Successfully read log length %d for pod: %s.%s", len(logs), pod.Namespace, pod.Name)
|
||||||
|
}
|
||||||
|
if err := addLogsToZip(zipWriter, logs, fmt.Sprintf("%s.%s.log", pod.Namespace, pod.Name)); err != nil {
|
||||||
|
mizu.Log.Errorf("Failed write logs, %v", err)
|
||||||
|
} else {
|
||||||
|
mizu.Log.Infof("Successfully added log length %d from pod: %s.%s", len(logs), pod.Namespace, pod.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := addFileToZip(zipWriter, mizu.GetConfigFilePath()); err != nil {
|
||||||
|
mizu.Log.Errorf("Failed write file, %v", err)
|
||||||
|
} else {
|
||||||
|
mizu.Log.Infof("Successfully added file %s", mizu.GetConfigFilePath())
|
||||||
|
}
|
||||||
|
if err := addFileToZip(zipWriter, mizu.GetLogFilePath()); err != nil {
|
||||||
|
mizu.Log.Errorf("Failed write file, %v", err)
|
||||||
|
} else {
|
||||||
|
mizu.Log.Infof("Successfully added file %s", mizu.GetLogFilePath())
|
||||||
|
}
|
||||||
|
mizu.Log.Infof("You can find the zip with all logs in %s\n", filePath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFileToZip(zipWriter *zip.Writer, filename string) error {
|
||||||
|
|
||||||
|
fileToZip, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open file %s, %w", filename, err)
|
||||||
|
}
|
||||||
|
defer fileToZip.Close()
|
||||||
|
|
||||||
|
// Get the file information
|
||||||
|
info, err := fileToZip.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get file information %s, %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := zip.FileInfoHeader(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using FileInfoHeader() above only uses the basename of the file. If we want
|
||||||
|
// to preserve the folder structure we can overwrite this with the full path.
|
||||||
|
header.Name = filepath.Base(filename)
|
||||||
|
|
||||||
|
// Change to deflate to gain better compression
|
||||||
|
// see http://golang.org/pkg/archive/zip/#pkg-constants
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
|
||||||
|
writer, err := zipWriter.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create header in zip for %s, %w", filename, err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(writer, fileToZip)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLogsToZip(writer *zip.Writer, logs string, fileName string) error {
|
||||||
|
if zipFile, err := writer.Create(fileName); err != nil {
|
||||||
|
return fmt.Errorf("couldn't create a log file inside zip for %s, %w", fileName, err)
|
||||||
|
} else {
|
||||||
|
if _, err = zipFile.Write([]byte(logs)); err != nil {
|
||||||
|
return fmt.Errorf("couldn't write logs to zip file: %s, %w", fileName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -51,7 +51,7 @@ func GetConfigWithDefaults() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigFilePath() string {
|
func GetConfigFilePath() string {
|
||||||
return path.Join(getMizuFolderPath(), "config.yaml")
|
return path.Join(GetMizuFolderPath(), "config.yaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeConfigFile() error {
|
func mergeConfigFile() error {
|
||||||
|
@ -14,6 +14,7 @@ type ConfigStruct struct {
|
|||||||
MizuImage string `yaml:"mizu-image"`
|
MizuImage string `yaml:"mizu-image"`
|
||||||
MizuNamespace string `yaml:"mizu-namespace"`
|
MizuNamespace string `yaml:"mizu-namespace"`
|
||||||
Telemetry bool `yaml:"telemetry" default:"true"`
|
Telemetry bool `yaml:"telemetry" default:"true"`
|
||||||
|
DumpLogs bool `yaml:"dump-logs" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *ConfigStruct) SetDefaults() {
|
func (config *ConfigStruct) SetDefaults() {
|
||||||
|
@ -27,7 +27,7 @@ const (
|
|||||||
ConfigMapName = "mizu-policy"
|
ConfigMapName = "mizu-policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getMizuFolderPath() string {
|
func GetMizuFolderPath() string {
|
||||||
home, homeDirErr := os.UserHomeDir()
|
home, homeDirErr := os.UserHomeDir()
|
||||||
if homeDirErr != nil {
|
if homeDirErr != nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -13,12 +13,12 @@ var format = logging.MustStringFormatter(
|
|||||||
`%{time} %{level:.5s} ▶ %{pid} %{shortfile} %{shortfunc} ▶ %{message}`,
|
`%{time} %{level:.5s} ▶ %{pid} %{shortfile} %{shortfunc} ▶ %{message}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetLogFilePath() string {
|
||||||
|
return path.Join(GetMizuFolderPath(), "mizu_cli.log")
|
||||||
|
}
|
||||||
|
|
||||||
func InitLogger() {
|
func InitLogger() {
|
||||||
mizuDirPath := getMizuFolderPath()
|
logPath := GetLogFilePath()
|
||||||
if err := os.MkdirAll(mizuDirPath, os.ModePerm); err != nil {
|
|
||||||
panic(fmt.Sprintf("Failed creating mizu dir: %v, err %v", mizuDirPath, err))
|
|
||||||
}
|
|
||||||
logPath := path.Join(mizuDirPath, "log.log")
|
|
||||||
f, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Failed mizu log file: %v, err %v", logPath, err))
|
panic(fmt.Sprintf("Failed mizu log file: %v, err %v", logPath, err))
|
||||||
|
Loading…
Reference in New Issue
Block a user