178 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package server
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
 | 
						|
 | 
						|
	sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
 | 
						|
	"github.com/containerd/containerd/v2/internal/cri/types"
 | 
						|
	"github.com/containerd/errdefs"
 | 
						|
)
 | 
						|
 | 
						|
// PodSandboxStatus returns the status of the PodSandbox.
 | 
						|
func (c *criService) PodSandboxStatus(ctx context.Context, r *runtime.PodSandboxStatusRequest) (*runtime.PodSandboxStatusResponse, error) {
 | 
						|
	sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("an error occurred when try to find sandbox: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	ip, additionalIPs, err := c.getIPs(sandbox)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to get sandbox ip: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	var (
 | 
						|
		createdAt time.Time
 | 
						|
		state     string
 | 
						|
		info      map[string]string
 | 
						|
	)
 | 
						|
	timestamp := time.Now().UnixNano()
 | 
						|
	cstatus, err := c.sandboxService.SandboxStatus(ctx, sandbox.Sandboxer, sandbox.ID, r.GetVerbose())
 | 
						|
	if err != nil {
 | 
						|
		// If the shim died unexpectedly (segfault etc.) let's set the state as
 | 
						|
		// NOTREADY and not just error out to make k8s and clients like crictl
 | 
						|
		// happy. If we get back ErrNotFound from controller.Status above while
 | 
						|
		// we're using the shim-mode controller, this is a decent indicator it
 | 
						|
		// exited unexpectedly. We can use the fact that we successfully retrieved
 | 
						|
		// the sandbox object from the store above to tell that this is true, otherwise
 | 
						|
		// if we followed the normal k8s convention of StopPodSandbox -> RemovePodSandbox,
 | 
						|
		// we wouldn't have that object in the store anymore.
 | 
						|
		if !errdefs.IsNotFound(err) {
 | 
						|
			return nil, fmt.Errorf("failed to query controller status: %w", err)
 | 
						|
		}
 | 
						|
		state = runtime.PodSandboxState_SANDBOX_NOTREADY.String()
 | 
						|
		if r.GetVerbose() {
 | 
						|
			info, err = toDeletedCRISandboxInfo(sandbox)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		state = cstatus.State
 | 
						|
		createdAt = cstatus.CreatedAt
 | 
						|
		info = cstatus.Info
 | 
						|
	}
 | 
						|
 | 
						|
	status := toCRISandboxStatus(sandbox.Metadata, state, createdAt, ip, additionalIPs)
 | 
						|
	if status.GetCreatedAt() == 0 {
 | 
						|
		// CRI doesn't allow CreatedAt == 0.
 | 
						|
		sandboxInfo, err := c.client.SandboxStore().Get(ctx, sandbox.ID)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get sandbox %q from metadata store: %w", sandbox.ID, err)
 | 
						|
		}
 | 
						|
		status.CreatedAt = sandboxInfo.CreatedAt.UnixNano()
 | 
						|
	}
 | 
						|
 | 
						|
	return &runtime.PodSandboxStatusResponse{
 | 
						|
		Status:    status,
 | 
						|
		Info:      info,
 | 
						|
		Timestamp: timestamp,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *criService) getIPs(sandbox sandboxstore.Sandbox) (string, []string, error) {
 | 
						|
	config := sandbox.Config
 | 
						|
 | 
						|
	// For sandboxes using the node network we are not
 | 
						|
	// responsible for reporting the IP.
 | 
						|
	if hostNetwork(config) {
 | 
						|
		return "", nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if closed, err := sandbox.NetNS.Closed(); err != nil {
 | 
						|
		return "", nil, fmt.Errorf("check network namespace closed: %w", err)
 | 
						|
	} else if closed {
 | 
						|
		return "", nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return sandbox.IP, sandbox.AdditionalIPs, nil
 | 
						|
}
 | 
						|
 | 
						|
// toCRISandboxStatus converts sandbox metadata into CRI pod sandbox status.
 | 
						|
func toCRISandboxStatus(meta sandboxstore.Metadata, status string, createdAt time.Time, ip string, additionalIPs []string) *runtime.PodSandboxStatus {
 | 
						|
	// Set sandbox state to NOTREADY by default.
 | 
						|
	state := runtime.PodSandboxState_SANDBOX_NOTREADY
 | 
						|
	if value, ok := runtime.PodSandboxState_value[status]; ok {
 | 
						|
		state = runtime.PodSandboxState(value)
 | 
						|
	}
 | 
						|
	nsOpts := meta.Config.GetLinux().GetSecurityContext().GetNamespaceOptions()
 | 
						|
	var ips []*runtime.PodIP
 | 
						|
	for _, additionalIP := range additionalIPs {
 | 
						|
		ips = append(ips, &runtime.PodIP{Ip: additionalIP})
 | 
						|
	}
 | 
						|
	return &runtime.PodSandboxStatus{
 | 
						|
		Id:        meta.ID,
 | 
						|
		Metadata:  meta.Config.GetMetadata(),
 | 
						|
		State:     state,
 | 
						|
		CreatedAt: createdAt.UnixNano(),
 | 
						|
		Network: &runtime.PodSandboxNetworkStatus{
 | 
						|
			Ip:            ip,
 | 
						|
			AdditionalIps: ips,
 | 
						|
		},
 | 
						|
		Linux: &runtime.LinuxPodSandboxStatus{
 | 
						|
			Namespaces: &runtime.Namespace{
 | 
						|
				Options: nsOpts,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		Labels:         meta.Config.GetLabels(),
 | 
						|
		Annotations:    meta.Config.GetAnnotations(),
 | 
						|
		RuntimeHandler: meta.RuntimeHandler,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// toDeletedCRISandboxInfo converts cached sandbox to CRI sandbox status response info map.
 | 
						|
// In most cases, controller.Status() with verbose=true should have SandboxInfo in the return,
 | 
						|
// but if controller.Status() returns a NotFound error,
 | 
						|
// we should fallback to get SandboxInfo from cached sandbox itself.
 | 
						|
func toDeletedCRISandboxInfo(sandbox sandboxstore.Sandbox) (map[string]string, error) {
 | 
						|
	si := &types.SandboxInfo{
 | 
						|
		Pid:       sandbox.Status.Get().Pid,
 | 
						|
		Config:    sandbox.Config,
 | 
						|
		CNIResult: sandbox.CNIResult,
 | 
						|
	}
 | 
						|
 | 
						|
	// If processStatus is empty, it means that the task is deleted. Apply "deleted"
 | 
						|
	// status which does not exist in containerd.
 | 
						|
	si.Status = "deleted"
 | 
						|
 | 
						|
	if sandbox.NetNS != nil {
 | 
						|
		// Add network closed information if sandbox is not using host network.
 | 
						|
		closed, err := sandbox.NetNS.Closed()
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to check network namespace closed: %w", err)
 | 
						|
		}
 | 
						|
		si.NetNSClosed = closed
 | 
						|
	}
 | 
						|
 | 
						|
	si.Metadata = &sandbox.Metadata
 | 
						|
 | 
						|
	infoBytes, err := json.Marshal(si)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to marshal info %v: %w", si, err)
 | 
						|
	}
 | 
						|
 | 
						|
	return map[string]string{
 | 
						|
		"info": string(infoBytes),
 | 
						|
	}, nil
 | 
						|
}
 |