github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/k8s.go (about) 1 // Copyright 2019-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 containercollection 16 17 import ( 18 "context" 19 "fmt" 20 "math" 21 "strings" 22 23 log "github.com/sirupsen/logrus" 24 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/fields" 28 "k8s.io/client-go/kubernetes" 29 "k8s.io/client-go/rest" 30 31 containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils" 32 runtimeclient "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/runtime-client" 33 containerutilsTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/types" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 35 ) 36 37 type K8sClient struct { 38 clientset *kubernetes.Clientset 39 nodeName string 40 fieldSelector string 41 runtimeClient runtimeclient.ContainerRuntimeClient 42 } 43 44 func NewK8sClient(nodeName string) (*K8sClient, error) { 45 config, err := rest.InClusterConfig() 46 if err != nil { 47 return nil, err 48 } 49 clientset, err := kubernetes.NewForConfig(config) 50 if err != nil { 51 return nil, err 52 } 53 54 fieldSelector := fields.OneTermEqualSelector("spec.nodeName", nodeName).String() 55 56 node, err := clientset.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) 57 if err != nil { 58 return nil, fmt.Errorf("getting node %w", err) 59 } 60 61 // Get a runtime client to talk to the container runtime handling pods in 62 // this node. 63 list := strings.SplitN(node.Status.NodeInfo.ContainerRuntimeVersion, "://", 2) 64 runtimeClient, err := containerutils.NewContainerRuntimeClient( 65 &containerutilsTypes.RuntimeConfig{ 66 Name: types.String2RuntimeName(list[0]), 67 }) 68 if err != nil { 69 return nil, err 70 } 71 72 return &K8sClient{ 73 clientset: clientset, 74 nodeName: nodeName, 75 fieldSelector: fieldSelector, 76 runtimeClient: runtimeClient, 77 }, nil 78 } 79 80 func (k *K8sClient) Close() { 81 k.runtimeClient.Close() 82 } 83 84 // trimRuntimePrefix removes the runtime prefix from a container ID. 85 func trimRuntimePrefix(id string) string { 86 parts := strings.SplitN(id, "//", 2) 87 if len(parts) != 2 { 88 return "" 89 } 90 91 return parts[1] 92 } 93 94 // GetNonRunningContainers returns the list of containers IDs that are not running. 95 func (k *K8sClient) GetNonRunningContainers(pod *v1.Pod) []string { 96 ret := []string{} 97 98 containerStatuses := append([]v1.ContainerStatus{}, pod.Status.InitContainerStatuses...) 99 containerStatuses = append(containerStatuses, pod.Status.ContainerStatuses...) 100 containerStatuses = append(containerStatuses, pod.Status.EphemeralContainerStatuses...) 101 102 for _, s := range containerStatuses { 103 if s.ContainerID != "" && s.State.Running == nil { 104 id := trimRuntimePrefix(s.ContainerID) 105 if id == "" { 106 continue 107 } 108 109 ret = append(ret, id) 110 } 111 } 112 113 return ret 114 } 115 116 // GetRunningContainers returns a list of the containers of a given Pod that are running. 117 func (k *K8sClient) GetRunningContainers(pod *v1.Pod) []Container { 118 containers := []Container{} 119 120 labels := map[string]string{} 121 for k, v := range pod.ObjectMeta.Labels { 122 labels[k] = v 123 } 124 125 containerStatuses := append([]v1.ContainerStatus{}, pod.Status.InitContainerStatuses...) 126 containerStatuses = append(containerStatuses, pod.Status.ContainerStatuses...) 127 containerStatuses = append(containerStatuses, pod.Status.EphemeralContainerStatuses...) 128 129 for _, s := range containerStatuses { 130 if s.ContainerID == "" || s.State.Running == nil { 131 continue 132 } 133 134 containerData, err := k.runtimeClient.GetContainerDetails(s.ContainerID) 135 if err != nil { 136 log.Warnf("Skip pod %s/%s: cannot find container (ID: %s): %v", 137 pod.GetNamespace(), pod.GetName(), s.ContainerID, err) 138 continue 139 } 140 141 pid := containerData.Pid 142 if pid > math.MaxUint32 { 143 log.Errorf("Container PID (%d) exceeds math.MaxUint32 (%d), skipping this container", pid, math.MaxUint32) 144 continue 145 } 146 147 containerDef := Container{ 148 Runtime: RuntimeMetadata{ 149 BasicRuntimeMetadata: containerData.Runtime.BasicRuntimeMetadata, 150 }, 151 Pid: uint32(pid), 152 K8s: K8sMetadata{ 153 BasicK8sMetadata: types.BasicK8sMetadata{ 154 Namespace: pod.GetNamespace(), 155 PodName: pod.GetName(), 156 ContainerName: s.Name, 157 PodLabels: labels, 158 }, 159 }, 160 } 161 containers = append(containers, containerDef) 162 } 163 164 return containers 165 } 166 167 // ListContainers return a list of the current containers that are 168 // running in the node. 169 func (k *K8sClient) ListContainers() (arr []Container, err error) { 170 // List pods 171 pods, err := k.clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{ 172 FieldSelector: k.fieldSelector, 173 }) 174 if err != nil { 175 return nil, err 176 } 177 178 for _, pod := range pods.Items { 179 containers := k.GetRunningContainers(&pod) 180 arr = append(arr, containers...) 181 } 182 return arr, nil 183 }