k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/kuberuntime/helpers.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package kuberuntime 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "path/filepath" 24 "strconv" 25 "strings" 26 27 v1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/types" 29 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 30 "k8s.io/klog/v2" 31 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 32 "k8s.io/kubernetes/pkg/security/apparmor" 33 ) 34 35 type podsByID []*kubecontainer.Pod 36 37 func (b podsByID) Len() int { return len(b) } 38 func (b podsByID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 39 func (b podsByID) Less(i, j int) bool { return b[i].ID < b[j].ID } 40 41 type containersByID []*kubecontainer.Container 42 43 func (b containersByID) Len() int { return len(b) } 44 func (b containersByID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 45 func (b containersByID) Less(i, j int) bool { return b[i].ID.ID < b[j].ID.ID } 46 47 // Newest first. 48 type podSandboxByCreated []*runtimeapi.PodSandbox 49 50 func (p podSandboxByCreated) Len() int { return len(p) } 51 func (p podSandboxByCreated) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 52 func (p podSandboxByCreated) Less(i, j int) bool { return p[i].CreatedAt > p[j].CreatedAt } 53 54 type containerStatusByCreated []*kubecontainer.Status 55 56 func (c containerStatusByCreated) Len() int { return len(c) } 57 func (c containerStatusByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] } 58 func (c containerStatusByCreated) Less(i, j int) bool { return c[i].CreatedAt.After(c[j].CreatedAt) } 59 60 // toKubeContainerState converts runtimeapi.ContainerState to kubecontainer.State. 61 func toKubeContainerState(state runtimeapi.ContainerState) kubecontainer.State { 62 switch state { 63 case runtimeapi.ContainerState_CONTAINER_CREATED: 64 return kubecontainer.ContainerStateCreated 65 case runtimeapi.ContainerState_CONTAINER_RUNNING: 66 return kubecontainer.ContainerStateRunning 67 case runtimeapi.ContainerState_CONTAINER_EXITED: 68 return kubecontainer.ContainerStateExited 69 case runtimeapi.ContainerState_CONTAINER_UNKNOWN: 70 return kubecontainer.ContainerStateUnknown 71 } 72 73 return kubecontainer.ContainerStateUnknown 74 } 75 76 // toRuntimeProtocol converts v1.Protocol to runtimeapi.Protocol. 77 func toRuntimeProtocol(protocol v1.Protocol) runtimeapi.Protocol { 78 switch protocol { 79 case v1.ProtocolTCP: 80 return runtimeapi.Protocol_TCP 81 case v1.ProtocolUDP: 82 return runtimeapi.Protocol_UDP 83 case v1.ProtocolSCTP: 84 return runtimeapi.Protocol_SCTP 85 } 86 87 klog.InfoS("Unknown protocol, defaulting to TCP", "protocol", protocol) 88 return runtimeapi.Protocol_TCP 89 } 90 91 // toKubeContainer converts runtimeapi.Container to kubecontainer.Container. 92 func (m *kubeGenericRuntimeManager) toKubeContainer(c *runtimeapi.Container) (*kubecontainer.Container, error) { 93 if c == nil || c.Id == "" || c.Image == nil { 94 return nil, fmt.Errorf("unable to convert a nil pointer to a runtime container") 95 } 96 97 // Keep backwards compatibility to older runtimes, c.ImageId has been added in v1.30 98 imageID := c.ImageRef 99 if c.ImageId != "" { 100 imageID = c.ImageId 101 } 102 103 annotatedInfo := getContainerInfoFromAnnotations(c.Annotations) 104 return &kubecontainer.Container{ 105 ID: kubecontainer.ContainerID{Type: m.runtimeName, ID: c.Id}, 106 Name: c.GetMetadata().GetName(), 107 ImageID: imageID, 108 ImageRef: c.ImageRef, 109 ImageRuntimeHandler: c.Image.RuntimeHandler, 110 Image: c.Image.Image, 111 Hash: annotatedInfo.Hash, 112 HashWithoutResources: annotatedInfo.HashWithoutResources, 113 State: toKubeContainerState(c.State), 114 }, nil 115 } 116 117 // sandboxToKubeContainer converts runtimeapi.PodSandbox to kubecontainer.Container. 118 // This is only needed because we need to return sandboxes as if they were 119 // kubecontainer.Containers to avoid substantial changes to PLEG. 120 // TODO: Remove this once it becomes obsolete. 121 func (m *kubeGenericRuntimeManager) sandboxToKubeContainer(s *runtimeapi.PodSandbox) (*kubecontainer.Container, error) { 122 if s == nil || s.Id == "" { 123 return nil, fmt.Errorf("unable to convert a nil pointer to a runtime container") 124 } 125 126 return &kubecontainer.Container{ 127 ID: kubecontainer.ContainerID{Type: m.runtimeName, ID: s.Id}, 128 State: kubecontainer.SandboxToContainerState(s.State), 129 }, nil 130 } 131 132 // getImageUser gets uid or user name that will run the command(s) from image. The function 133 // guarantees that only one of them is set. 134 func (m *kubeGenericRuntimeManager) getImageUser(ctx context.Context, image string) (*int64, string, error) { 135 resp, err := m.imageService.ImageStatus(ctx, &runtimeapi.ImageSpec{Image: image}, false) 136 if err != nil { 137 return nil, "", err 138 } 139 imageStatus := resp.GetImage() 140 141 if imageStatus != nil { 142 if imageStatus.Uid != nil { 143 return &imageStatus.GetUid().Value, "", nil 144 } 145 146 if imageStatus.Username != "" { 147 return nil, imageStatus.Username, nil 148 } 149 } 150 151 // If non of them is set, treat it as root. 152 return new(int64), "", nil 153 } 154 155 // isInitContainerFailed returns true under the following conditions: 156 // 1. container has exited and exitcode is not zero. 157 // 2. container is in unknown state. 158 // 3. container gets OOMKilled. 159 func isInitContainerFailed(status *kubecontainer.Status) bool { 160 // When oomkilled occurs, init container should be considered as a failure. 161 if status.Reason == "OOMKilled" { 162 return true 163 } 164 165 if status.State == kubecontainer.ContainerStateExited && status.ExitCode != 0 { 166 return true 167 } 168 169 if status.State == kubecontainer.ContainerStateUnknown { 170 return true 171 } 172 173 return false 174 } 175 176 // getStableKey generates a key (string) to uniquely identify a 177 // (pod, container) tuple. The key should include the content of the 178 // container, so that any change to the container generates a new key. 179 func getStableKey(pod *v1.Pod, container *v1.Container) string { 180 hash := strconv.FormatUint(kubecontainer.HashContainer(container), 16) 181 return fmt.Sprintf("%s_%s_%s_%s_%s", pod.Name, pod.Namespace, string(pod.UID), container.Name, hash) 182 } 183 184 // logPathDelimiter is the delimiter used in the log path. 185 const logPathDelimiter = "_" 186 187 // buildContainerLogsPath builds log path for container relative to pod logs directory. 188 func buildContainerLogsPath(containerName string, restartCount int) string { 189 return filepath.Join(containerName, fmt.Sprintf("%d.log", restartCount)) 190 } 191 192 // BuildContainerLogsDirectory builds absolute log directory path for a container in pod. 193 func BuildContainerLogsDirectory(podLogsDir, podNamespace, podName string, podUID types.UID, containerName string) string { 194 return filepath.Join(BuildPodLogsDirectory(podLogsDir, podNamespace, podName, podUID), containerName) 195 } 196 197 // BuildPodLogsDirectory builds absolute log directory path for a pod sandbox. 198 func BuildPodLogsDirectory(podLogsDir, podNamespace, podName string, podUID types.UID) string { 199 return filepath.Join(podLogsDir, strings.Join([]string{podNamespace, podName, 200 string(podUID)}, logPathDelimiter)) 201 } 202 203 // parsePodUIDFromLogsDirectory parses pod logs directory name and returns the pod UID. 204 // It supports both the old pod log directory /var/log/pods/UID, and the new pod log 205 // directory /var/log/pods/NAMESPACE_NAME_UID. 206 func parsePodUIDFromLogsDirectory(name string) types.UID { 207 parts := strings.Split(name, logPathDelimiter) 208 return types.UID(parts[len(parts)-1]) 209 } 210 211 // toKubeRuntimeStatus converts the runtimeapi.RuntimeStatus to kubecontainer.RuntimeStatus. 212 func toKubeRuntimeStatus(status *runtimeapi.RuntimeStatus, handlers []*runtimeapi.RuntimeHandler) *kubecontainer.RuntimeStatus { 213 conditions := []kubecontainer.RuntimeCondition{} 214 for _, c := range status.GetConditions() { 215 conditions = append(conditions, kubecontainer.RuntimeCondition{ 216 Type: kubecontainer.RuntimeConditionType(c.Type), 217 Status: c.Status, 218 Reason: c.Reason, 219 Message: c.Message, 220 }) 221 } 222 retHandlers := make([]kubecontainer.RuntimeHandler, len(handlers)) 223 for i, h := range handlers { 224 supportsRRO := false 225 supportsUserns := false 226 if h.Features != nil { 227 supportsRRO = h.Features.RecursiveReadOnlyMounts 228 supportsUserns = h.Features.UserNamespaces 229 } 230 retHandlers[i] = kubecontainer.RuntimeHandler{ 231 Name: h.Name, 232 SupportsRecursiveReadOnlyMounts: supportsRRO, 233 SupportsUserNamespaces: supportsUserns, 234 } 235 } 236 return &kubecontainer.RuntimeStatus{Conditions: conditions, Handlers: retHandlers} 237 } 238 239 func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) { 240 if scmp == nil { 241 if fallbackToRuntimeDefault { 242 return &runtimeapi.SecurityProfile{ 243 ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, 244 }, nil 245 } 246 return &runtimeapi.SecurityProfile{ 247 ProfileType: runtimeapi.SecurityProfile_Unconfined, 248 }, nil 249 } 250 if scmp.Type == v1.SeccompProfileTypeRuntimeDefault { 251 return &runtimeapi.SecurityProfile{ 252 ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, 253 }, nil 254 } 255 if scmp.Type == v1.SeccompProfileTypeLocalhost { 256 if scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { 257 fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) 258 return &runtimeapi.SecurityProfile{ 259 ProfileType: runtimeapi.SecurityProfile_Localhost, 260 LocalhostRef: fname, 261 }, nil 262 } else { 263 return nil, fmt.Errorf("localhostProfile must be set if seccompProfile type is Localhost.") 264 } 265 } 266 return &runtimeapi.SecurityProfile{ 267 ProfileType: runtimeapi.SecurityProfile_Unconfined, 268 }, nil 269 } 270 271 func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]string, containerName string, 272 podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) { 273 // container fields are applied first 274 if containerSecContext != nil && containerSecContext.SeccompProfile != nil { 275 return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault) 276 } 277 278 // when container seccomp is not defined, try to apply from pod field 279 if podSecContext != nil && podSecContext.SeccompProfile != nil { 280 return fieldSeccompProfile(podSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault) 281 } 282 283 if fallbackToRuntimeDefault { 284 return &runtimeapi.SecurityProfile{ 285 ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, 286 }, nil 287 } 288 289 return &runtimeapi.SecurityProfile{ 290 ProfileType: runtimeapi.SecurityProfile_Unconfined, 291 }, nil 292 } 293 294 func getAppArmorProfile(pod *v1.Pod, container *v1.Container) (*runtimeapi.SecurityProfile, string, error) { 295 profile := apparmor.GetProfile(pod, container) 296 if profile == nil { 297 return nil, "", nil 298 } 299 300 var ( 301 securityProfile *runtimeapi.SecurityProfile 302 deprecatedProfile string // Deprecated apparmor profile format, still provided for backwards compatibility with older runtimes. 303 ) 304 305 switch profile.Type { 306 case v1.AppArmorProfileTypeRuntimeDefault: 307 securityProfile = &runtimeapi.SecurityProfile{ 308 ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, 309 } 310 deprecatedProfile = v1.DeprecatedAppArmorBetaProfileRuntimeDefault 311 312 case v1.AppArmorProfileTypeUnconfined: 313 securityProfile = &runtimeapi.SecurityProfile{ 314 ProfileType: runtimeapi.SecurityProfile_Unconfined, 315 } 316 deprecatedProfile = v1.DeprecatedAppArmorBetaProfileNameUnconfined 317 318 case v1.AppArmorProfileTypeLocalhost: 319 if profile.LocalhostProfile == nil { 320 return nil, "", errors.New("missing localhost apparmor profile name") 321 } 322 securityProfile = &runtimeapi.SecurityProfile{ 323 ProfileType: runtimeapi.SecurityProfile_Localhost, 324 LocalhostRef: *profile.LocalhostProfile, 325 } 326 deprecatedProfile = v1.DeprecatedAppArmorBetaProfileNamePrefix + *profile.LocalhostProfile 327 328 default: 329 // Shouldn't happen. 330 return nil, "", fmt.Errorf("unknown apparmor profile type: %q", profile.Type) 331 } 332 333 return securityProfile, deprecatedProfile, nil 334 }