Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion modules/cli/cmd/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,15 @@ var logsCmd = &cobra.Command{
after, _ := flags.GetString("after")
before, _ := flags.GetString("before")

allNamespaces, _ := flags.GetBool("all-namespaces")

grep, _ := flags.GetString("grep")
regionList, _ := flags.GetStringSlice("region")
zoneList, _ := flags.GetStringSlice("zone")
osList, _ := flags.GetStringSlice("os")
archList, _ := flags.GetStringSlice("arch")
nodeList, _ := flags.GetStringSlice("node")
namespace, _ := flags.GetString("namespace")

hideHeader, _ := flags.GetBool("hide-header")
hideTs, _ := flags.GetBool("hide-ts")
Expand Down Expand Up @@ -327,6 +330,8 @@ var logsCmd = &cobra.Command{
logs.WithOSes(osList),
logs.WithArches(archList),
logs.WithNodes(nodeList),
logs.WithNamespace(namespace),
logs.WithAllNamespaces(allNamespaces),
}

switch streamMode {
Expand Down Expand Up @@ -633,6 +638,7 @@ func init() {
logsCmd.MarkFlagsMutuallyExclusive("until", "before")

flagset.StringP("grep", "g", "", "Filter records by a regular expression")
flagset.StringP("namespace", "n", "", "Filter source pods by namespace")

flagset.StringSlice("region", []string{}, "Filter source pods by region")
flagset.StringSlice("zone", []string{}, "Filter source pods by zone")
Expand All @@ -641,6 +647,7 @@ func init() {
flagset.StringSlice("node", []string{}, "Filter source pods by node name")

flagset.Bool("raw", false, "Output only raw log messages without metadata")
flagset.Bool("all-namespaces", false, "Include records from all namespaces (overrides --namespace)")
flagset.Bool("hide-ts", false, "Hide the timestamp of each record")
flagset.Bool("with-node", false, "Show the source node of each record")
flagset.Bool("with-region", false, "Show the source region of each record")
Expand All @@ -655,7 +662,7 @@ func init() {
flagset.Bool("hide-header", false, "Hide table header")
flagset.Bool("hide-dot", false, "Hide the dot indicator in the records")

//flagset.BoolP("reverse", "r", false, "List records in reverse order")
// flagset.BoolP("reverse", "r", false, "List records in reverse order")

flagset.Bool("force", false, "Force command (if necessary)")

Expand Down
41 changes: 41 additions & 0 deletions modules/shared/k8shelpers/connection-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type ConnectionManager interface {
GetOrCreateClientset(kubeContext string) (kubernetes.Interface, error)
GetOrCreateDynamicClient(kubeContext string) (dynamic.Interface, error)
GetDefaultNamespace(kubeContext string) string
GetNamespaceList(kubeContext string) ([]string, error)
DerefKubeContext(kubeContext *string) string
NewInformer(ctx context.Context, kubeContext string, token string, namespace string, gvr schema.GroupVersionResource) (informers.GenericInformer, func(), error)
WaitUntilReady(ctx context.Context, kubeContext string) error
Expand Down Expand Up @@ -230,6 +231,26 @@ func (cm *DesktopConnectionManager) GetDefaultNamespace(kubeContext string) stri
return context.Namespace
}

// GetNamespaceList returns a list of all namespaces in the cluster
func (cm *DesktopConnectionManager) GetNamespaceList(kubeContext string) ([]string, error) {
clientset, err := cm.GetOrCreateClientset(kubeContext)
if err != nil {
return nil, err
}

rawNamespaceList, err := clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}

namespaceList := make([]string, len(rawNamespaceList.Items))
for i, val := range rawNamespaceList.Items {
namespaceList[i] = val.Name
}

return namespaceList, nil
}

// DerefKubeContext
func (cm *DesktopConnectionManager) DerefKubeContext(kubeContextPtr *string) string {
cm.mu.Lock()
Expand Down Expand Up @@ -516,6 +537,26 @@ func (cm *InClusterConnectionManager) GetDefaultNamespace(kubeContext string) st
return metav1.NamespaceDefault
}

// GetNamespaceList returns a list of all namespaces in the cluster
func (cm *InClusterConnectionManager) GetNamespaceList(kubeContext string) ([]string, error) {
clientset, err := cm.GetOrCreateClientset(kubeContext)
if err != nil {
return nil, err
}

rawNamespaceList, err := clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}

namespaceList := make([]string, len(rawNamespaceList.Items))
for i, val := range rawNamespaceList.Items {
namespaceList[i] = val.Name
}

return namespaceList, nil
}

// DerefKubeContext
func (cm *InClusterConnectionManager) DerefKubeContext(kubeContext *string) string {
return ""
Expand Down
11 changes: 11 additions & 0 deletions modules/shared/k8shelpers/mock/connection-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ func (m *MockConnectionManager) GetDefaultNamespace(kubeContext string) string {
return ret.String(0)
}

func (m *MockConnectionManager) GetNamespaceList(kubeContext string) ([]string, error) {
ret := m.Called(kubeContext)

var r0 []string
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}

return r0, ret.Error(1)
}

func (m *MockConnectionManager) DerefKubeContext(kubeContextPtr *string) string {
ret := m.Called(kubeContextPtr)
return ret.String(0)
Expand Down
22 changes: 22 additions & 0 deletions modules/shared/logs/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,25 @@ func WithAllowedNamespaces(allowedNamespaces []string) Option {
return nil
}
}

// WithNamespace sets the namespace for source watcher
func WithNamespace(namespace string) Option {
return func(target any) error {
switch t := target.(type) {
case *sourceWatcher:
t.namespace = namespace
}
return nil
}
}

// WithAllNamespaces sets whether to return logs from all namespaces
func WithAllNamespaces(allNamespaces bool) Option {
return func(target any) error {
switch t := target.(type) {
case *sourceWatcher:
t.allNamespaces = allNamespaces
}
return nil
}
}
87 changes: 69 additions & 18 deletions modules/shared/logs/source-watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ type sourceWatcher struct {
cm k8shelpers.ConnectionManager
eventbus evbus.Bus

kubeContext string
bearerToken string
regions []string
zones []string
oses []string
arches []string
nodes []string
containers []string
kubeContext string
bearerToken string
namespace string
allNamespaces bool
regions []string
zones []string
oses []string
arches []string
nodes []string
containers []string

allowedNamespaces []string
parsedPaths []parsedPath
Expand Down Expand Up @@ -122,22 +124,29 @@ func NewSourceWatcher(cm k8shelpers.ConnectionManager, sourcePaths []string, opt
// Get default namespace
defaultNamespace := cm.GetDefaultNamespace(sw.kubeContext)

// Parse paths
parsedPaths := []parsedPath{}
for _, p := range sourcePaths {
pp, err := parsePath(p, defaultNamespace)
if err != nil {
return nil, err
}
// Get list of all namespaces
namespaceList, err := cm.GetNamespaceList(sw.kubeContext)
if err != nil {
return nil, err
}

parsedPaths, err := parsePaths(sourcePaths, defaultNamespace, sw.allNamespaces, namespaceList, sw.namespace)

if err != nil {
return nil, err
}

// Filter paths that do not match allowed namespaces
filteredPaths := []parsedPath{}
for _, pp := range parsedPaths {

// Remove paths that do not match allowed namespaces
if len(sw.allowedNamespaces) > 0 && !slices.Contains(sw.allowedNamespaces, pp.Namespace) {
continue
}

parsedPaths = append(parsedPaths, pp)
filteredPaths = append(filteredPaths, pp)
}
sw.parsedPaths = parsedPaths
sw.parsedPaths = filteredPaths

return sw, nil
}
Expand Down Expand Up @@ -474,6 +483,48 @@ type parsedPath struct {
ContainerName string
}

// Parse multiple source paths with namespace priority logic
func parsePaths(sourcePaths []string, defaultNamespace string, allNamespaces bool, namespaceList []string, namespace string) ([]parsedPath, error) {
var result []parsedPath

// Check if any source path has explicit namespace (highest priority)
sourceHasNamespace := false
for _, path := range sourcePaths {
if strings.Contains(path, ":") {
sourceHasNamespace = true
break
}
}

if allNamespaces && !sourceHasNamespace {
for _, namespace := range namespaceList {
for _, path := range sourcePaths {
pp, err := parsePath(path, namespace)
if err != nil {
return nil, err
}
result = append(result, pp)
}
}
} else {
// using namespace flag if it exists and source paths do not have explicit namespace
if namespace != "" && !sourceHasNamespace {
defaultNamespace = namespace
}

for _, path := range sourcePaths {
pp, err := parsePath(path, defaultNamespace)
if err != nil {
return nil, err
}
result = append(result, pp)
}

}

return result, nil
}

// Parse source path
func parsePath(path string, defaultNamespace string) (parsedPath, error) {
// Remove leading and trailing slashes
Expand Down
2 changes: 1 addition & 1 deletion modules/shared/logs/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Stream struct {
sinceTime time.Time
untilTime time.Time

//reverse bool
// reverse bool
follow bool
grep string
grepRegex *regexp.Regexp
Expand Down
Loading