1
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
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
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
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
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
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
118
119
120
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
133
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
152 return new(int64), "", nil
153 }
154
155
156
157
158
159 func isInitContainerFailed(status *kubecontainer.Status) bool {
160
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
177
178
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
185 const logPathDelimiter = "_"
186
187
188 func buildContainerLogsPath(containerName string, restartCount int) string {
189 return filepath.Join(containerName, fmt.Sprintf("%d.log", restartCount))
190 }
191
192
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
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
204
205
206 func parsePodUIDFromLogsDirectory(name string) types.UID {
207 parts := strings.Split(name, logPathDelimiter)
208 return types.UID(parts[len(parts)-1])
209 }
210
211
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
274 if containerSecContext != nil && containerSecContext.SeccompProfile != nil {
275 return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault)
276 }
277
278
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
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
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