github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/containers.go (about) 1 // Copyright 2022-2024 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 containercollection 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "github.com/moby/moby/pkg/stringid" 23 ocispec "github.com/opencontainers/runtime-spec/specs-go" 24 "golang.org/x/sys/unix" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/client-go/dynamic" 27 "k8s.io/client-go/rest" 28 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 31 ) 32 33 // Container represents a container with its metadata. 34 type Container struct { 35 // Runtime contains the metadata of the container runtime 36 Runtime RuntimeMetadata `json:"runtime,omitempty" column:"runtime" columnTags:"runtime"` 37 38 // K8s contains the Kubernetes metadata of the container. 39 K8s K8sMetadata `json:"k8s,omitempty" column:"k8s" columnTags:"kubernetes"` 40 41 // Pid is the process id of the container 42 Pid uint32 `json:"pid,omitempty" column:"pid,template:pid,hide"` 43 44 // Container's configuration is the config.json from the OCI runtime 45 // spec 46 OciConfig *ocispec.Spec `json:"ociConfig,omitempty"` 47 48 // Bundle is the directory containing the config.json from the OCI 49 // runtime spec 50 // See https://github.com/opencontainers/runtime-spec/blob/main/bundle.md 51 Bundle string `json:"bundle,omitempty"` 52 53 // SandboxId is the sandbox id for the corresponding pod 54 SandboxId string `json:"sandboxId,omitempty"` 55 56 // Linux metadata can be derived from the pid via /proc/$pid/... 57 Mntns uint64 `json:"mntns,omitempty" column:"mntns,template:ns"` 58 Netns uint64 `json:"netns,omitempty" column:"netns,template:ns"` 59 HostNetwork bool `json:"hostNetwork,omitempty" column:"hostNetwork,width:11,fixed,hide"` 60 CgroupPath string `json:"cgroupPath,omitempty"` 61 CgroupID uint64 `json:"cgroupID,omitempty"` 62 // Data required to find the container to Pod association in the 63 // gadgettracermanager. 64 CgroupV1 string `json:"cgroupV1,omitempty"` 65 CgroupV2 string `json:"cgroupV2,omitempty"` 66 67 // We keep an open file descriptor of the containers mount and net namespaces to be sure the 68 // kernel doesn't reuse the inode id before we get rid of this container. This logic avoids 69 // a race condition when the ns inode id is reused by a new container and we erroneously 70 // pick events from it or enrich data using it. 71 // These are only used when cachedContainers are enabled through WithTracerCollection(). 72 mntNsFd int 73 netNsFd int 74 75 // when the container was removed. Useful for running cached containers. 76 deletionTimestamp time.Time 77 } 78 79 // close releases any resources (like file descriptors) the container is using. 80 func (c *Container) close() { 81 if c.mntNsFd != 0 { 82 unix.Close(c.mntNsFd) 83 c.mntNsFd = 0 84 } 85 if c.netNsFd != 0 { 86 unix.Close(c.netNsFd) 87 c.netNsFd = 0 88 } 89 } 90 91 type RuntimeMetadata struct { 92 types.BasicRuntimeMetadata `json:",inline"` 93 } 94 95 type K8sMetadata struct { 96 types.BasicK8sMetadata `json:",inline"` 97 PodUID string `json:"podUID,omitempty"` 98 99 ownerReference *metav1.OwnerReference 100 } 101 102 type K8sSelector struct { 103 types.BasicK8sMetadata 104 } 105 106 type RuntimeSelector struct { 107 // TODO: Support filtering by all the fields in BasicRuntimeMetadata 108 ContainerName string 109 } 110 111 type ContainerSelector struct { 112 K8s K8sSelector 113 Runtime RuntimeSelector 114 } 115 116 // GetOwnerReference returns the owner reference information of the 117 // container. Currently it's added to the seccomp profile as annotations 118 // to help users to identify the workflow of the profile. We "lazily 119 // enrich" this information because this operation is expensive and this 120 // information is only needed in some cases. 121 func (c *Container) GetOwnerReference() (*metav1.OwnerReference, error) { 122 if c.K8s.ownerReference != nil { 123 return c.K8s.ownerReference, nil 124 } 125 126 kubeconfig, err := rest.InClusterConfig() 127 if err != nil { 128 return nil, fmt.Errorf("getting Kubernetes config: %w", err) 129 } 130 131 dynamicClient, err := dynamic.NewForConfig(kubeconfig) 132 if err != nil { 133 return nil, fmt.Errorf("getting get dynamic Kubernetes client: %w", err) 134 } 135 136 err = ownerReferenceEnrichment(dynamicClient, c, nil) 137 if err != nil { 138 return nil, fmt.Errorf("enriching owner reference: %w", err) 139 } 140 141 return c.K8s.ownerReference, nil 142 } 143 144 func ownerReferenceEnrichment( 145 dynamicClient dynamic.Interface, 146 container *Container, 147 ownerReferences []metav1.OwnerReference, 148 ) error { 149 resGroupVersion := "v1" 150 resKind := "pods" 151 resName := container.K8s.PodName 152 resNamespace := container.K8s.Namespace 153 154 var highestOwnerRef *metav1.OwnerReference 155 156 // Iterate until we reach the highest level of reference with one of the 157 // expected resource kind. Take into account that if this logic is changed, 158 // the gadget cluster role needs to be updated accordingly. 159 for { 160 if len(ownerReferences) == 0 { 161 var err error 162 ownerReferences, err = getOwnerReferences(dynamicClient, 163 resNamespace, resKind, resGroupVersion, resName) 164 if err != nil { 165 return fmt.Errorf("getting %s/%s/%s/%s owner reference: %w", 166 resNamespace, resKind, resGroupVersion, resName, err) 167 } 168 169 // No owner reference found 170 if len(ownerReferences) == 0 { 171 break 172 } 173 } 174 175 ownerRef := getExpectedOwnerReference(ownerReferences) 176 if ownerRef == nil { 177 // None expected owner reference found 178 break 179 } 180 181 // Update parameters for next iteration (Namespace does not change) 182 highestOwnerRef = ownerRef 183 resGroupVersion = ownerRef.APIVersion 184 resKind = strings.ToLower(ownerRef.Kind) + "s" 185 resName = ownerRef.Name 186 ownerReferences = nil 187 } 188 189 // Update container's owner reference (If any) 190 if highestOwnerRef != nil { 191 container.K8s.ownerReference = &metav1.OwnerReference{ 192 APIVersion: highestOwnerRef.APIVersion, 193 Kind: highestOwnerRef.Kind, 194 Name: highestOwnerRef.Name, 195 UID: highestOwnerRef.UID, 196 } 197 } 198 199 return nil 200 } 201 202 func GetColumns() *columns.Columns[Container] { 203 cols := columns.MustCreateColumns[Container]() 204 205 cols.MustSetExtractor("runtime.containerImageName", func(container *Container) any { 206 if container == nil { 207 return "" 208 } 209 if strings.Contains(container.Runtime.ContainerImageName, "sha256") { 210 return stringid.TruncateID(container.Runtime.ContainerImageName) 211 } 212 return container.Runtime.ContainerImageName 213 }) 214 215 return cols 216 } 217 218 func (c *Container) K8sMetadata() *types.BasicK8sMetadata { 219 return &c.K8s.BasicK8sMetadata 220 } 221 222 func (c *Container) RuntimeMetadata() *types.BasicRuntimeMetadata { 223 return &c.Runtime.BasicRuntimeMetadata 224 } 225 226 func (c *Container) UsesHostNetwork() bool { 227 return c.HostNetwork 228 }