github.com/inspektor-gadget/inspektor-gadget@v0.28.1/cmd/ig/containers/containers.go (about)

     1  // Copyright 2022 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package containers
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/moby/moby/pkg/stringid"
    25  	"github.com/spf13/cobra"
    26  
    27  	commonutils "github.com/inspektor-gadget/inspektor-gadget/cmd/common/utils"
    28  	"github.com/inspektor-gadget/inspektor-gadget/cmd/ig/utils"
    29  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns"
    30  	containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
    31  	igmanager "github.com/inspektor-gadget/inspektor-gadget/pkg/ig-manager"
    32  	"github.com/inspektor-gadget/inspektor-gadget/pkg/utils/host"
    33  )
    34  
    35  const igSubKey = "ig-key"
    36  
    37  func NewListContainersCmd() *cobra.Command {
    38  	var commonFlags utils.CommonFlags
    39  	var optionWatch bool
    40  
    41  	cmd := &cobra.Command{
    42  		Use:   "list-containers",
    43  		Short: "List all containers",
    44  		RunE: func(*cobra.Command, []string) error {
    45  			// The list-containers command is not a gadget, so the local
    46  			// runtime won't call host.Init().
    47  			err := host.Init(host.Config{})
    48  			if err != nil {
    49  				return err
    50  			}
    51  
    52  			igmanager, err := igmanager.NewManager(commonFlags.RuntimeConfigs)
    53  			if err != nil {
    54  				return commonutils.WrapInErrManagerInit(err)
    55  			}
    56  			defer igmanager.Close()
    57  
    58  			selector := containercollection.ContainerSelector{
    59  				Runtime: containercollection.RuntimeSelector{
    60  					ContainerName: commonFlags.Containername,
    61  				},
    62  			}
    63  
    64  			if !optionWatch {
    65  				parser, err := commonutils.NewGadgetParserWithK8sAndRuntimeInfo(&commonFlags.OutputConfig, columnsWithAdjustedVisibility(containercollection.GetColumns()))
    66  				if err != nil {
    67  					return commonutils.WrapInErrParserCreate(err)
    68  				}
    69  				containers := igmanager.GetContainersBySelector(&selector)
    70  
    71  				parser.Sort(containers, []string{"runtime.runtimeName", "runtime.containerName"})
    72  				if err = printContainers(parser, commonFlags, containers); err != nil {
    73  					return err
    74  				}
    75  				return nil
    76  			}
    77  
    78  			cols := columnsWithAdjustedVisibility(columns.MustCreateColumns[containercollection.PubSubEvent]())
    79  			cols.SetExtractor("event", func(event *containercollection.PubSubEvent) any {
    80  				return event.Type.String()
    81  			})
    82  			cols.MustSetExtractor("runtime.containerImageName", func(event *containercollection.PubSubEvent) any {
    83  				if event == nil || event.Container == nil {
    84  					return ""
    85  				}
    86  				if strings.Contains(event.Container.Runtime.ContainerImageName, "sha256") {
    87  					return stringid.TruncateID(event.Container.Runtime.ContainerImageName)
    88  				}
    89  				return event.Container.Runtime.ContainerImageName
    90  			})
    91  
    92  			parser, err := commonutils.NewGadgetParserWithK8sAndRuntimeInfo(&commonFlags.OutputConfig, cols)
    93  			if err != nil {
    94  				return commonutils.WrapInErrParserCreate(err)
    95  			}
    96  			containers := igmanager.ContainerCollection.Subscribe(
    97  				igSubKey,
    98  				selector,
    99  				func(event containercollection.PubSubEvent) {
   100  					if err = printPubSubEvent(parser, commonFlags, &event); err != nil {
   101  						fmt.Fprintf(os.Stderr, "Warning: %s\n", err)
   102  					}
   103  				},
   104  			)
   105  			defer igmanager.ContainerCollection.Unsubscribe(igSubKey)
   106  
   107  			if commonFlags.OutputMode != commonutils.OutputModeJSON {
   108  				fmt.Println(parser.BuildColumnsHeader())
   109  			}
   110  			timestamp := time.Now().Format(time.RFC3339)
   111  			for _, container := range containers {
   112  				e := containercollection.PubSubEvent{
   113  					Timestamp: timestamp,
   114  					Type:      containercollection.EventTypeAddContainer,
   115  					Container: container,
   116  				}
   117  				if err = printPubSubEvent(parser, commonFlags, &e); err != nil {
   118  					return err
   119  				}
   120  			}
   121  
   122  			utils.WaitForEnd(&commonFlags)
   123  			return nil
   124  		},
   125  	}
   126  
   127  	cmd.Flags().BoolVarP(
   128  		&optionWatch,
   129  		"watch", "w",
   130  		false,
   131  		"After listing the containers, watch for new containers")
   132  
   133  	utils.AddCommonFlags(cmd, &commonFlags)
   134  
   135  	return cmd
   136  }
   137  
   138  func printContainers(parser *commonutils.GadgetParser[containercollection.Container], commonFlags utils.CommonFlags, containers []*containercollection.Container) error {
   139  	switch commonFlags.OutputMode {
   140  	case commonutils.OutputModeJSON:
   141  		b, err := json.MarshalIndent(containers, "", "  ")
   142  		if err != nil {
   143  			return commonutils.WrapInErrMarshalOutput(err)
   144  		}
   145  
   146  		fmt.Printf("%s\n", b)
   147  	case commonutils.OutputModeColumns:
   148  		fmt.Println(parser.TransformIntoTable(containers))
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func printPubSubEvent(parser *commonutils.GadgetParser[containercollection.PubSubEvent], commonFlags utils.CommonFlags, event *containercollection.PubSubEvent) error {
   155  	switch commonFlags.OutputMode {
   156  	case commonutils.OutputModeJSON:
   157  		b, err := json.MarshalIndent(event, "", "  ")
   158  		if err != nil {
   159  			return commonutils.WrapInErrMarshalOutput(err)
   160  		}
   161  		fmt.Printf("%s\n", b)
   162  	case commonutils.OutputModeColumns:
   163  		fmt.Println(parser.TransformIntoColumns(event))
   164  	}
   165  
   166  	return nil
   167  }
   168  
   169  func columnsWithAdjustedVisibility[T containercollection.Container | containercollection.PubSubEvent](cols *columns.Columns[T]) *columns.Columns[T] {
   170  	for _, c := range cols.GetColumnMap(columns.WithTag("kubernetes")) {
   171  		c.Visible = false
   172  	}
   173  	for _, c := range cols.GetColumnMap(columns.WithTag("runtime")) {
   174  		c.Visible = true
   175  	}
   176  	return cols
   177  }