...

Source file src/k8s.io/kubernetes/pkg/kubelet/kuberuntime/helpers.go

Documentation: k8s.io/kubernetes/pkg/kubelet/kuberuntime

     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  }
   335  

View as plain text