...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/crclients/containerd/client.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/crclients/containerd

     1  // Copyright 2021 Chaos Mesh 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  
    16  package containerd
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"syscall"
    22  
    23  	"github.com/containerd/containerd"
    24  	"github.com/pkg/errors"
    25  
    26  	"github.com/chaos-mesh/chaos-mesh/pkg/mock"
    27  )
    28  
    29  const (
    30  	containerdProtocolPrefix = "containerd://"
    31  
    32  	// containerKindLabel is a label key intending to filter sandbox container
    33  	// ref: https://github.com/containerd/containerd/blob/main/pkg/cri/server/helpers.go#L74-L80
    34  	containerKindLabel     = "io.cri-containerd.kind"
    35  	containerKindContainer = "container"
    36  )
    37  
    38  // ContainerdClientInterface represents the ContainerClient, it's used to simply unit test
    39  type ContainerdClientInterface interface {
    40  	LoadContainer(ctx context.Context, id string) (containerd.Container, error)
    41  	Containers(ctx context.Context, filters ...string) ([]containerd.Container, error)
    42  }
    43  
    44  // ContainerdClient can get information from containerd
    45  type ContainerdClient struct {
    46  	client ContainerdClientInterface
    47  }
    48  
    49  // FormatContainerID strips protocol prefix from the container ID
    50  func (c ContainerdClient) FormatContainerID(ctx context.Context, containerID string) (string, error) {
    51  	if len(containerID) < len(containerdProtocolPrefix) {
    52  		return "", errors.Errorf("container id %s is not a containerd container id", containerID)
    53  	}
    54  	if containerID[0:len(containerdProtocolPrefix)] != containerdProtocolPrefix {
    55  		return "", errors.Errorf("expected %s but got %s", containerdProtocolPrefix, containerID[0:len(containerdProtocolPrefix)])
    56  	}
    57  	return containerID[len(containerdProtocolPrefix):], nil
    58  }
    59  
    60  // GetPidFromContainerID fetches PID according to container id
    61  func (c ContainerdClient) GetPidFromContainerID(ctx context.Context, containerID string) (uint32, error) {
    62  	id, err := c.FormatContainerID(ctx, containerID)
    63  	if err != nil {
    64  		return 0, err
    65  	}
    66  	container, err := c.client.LoadContainer(ctx, id)
    67  	if err != nil {
    68  		return 0, err
    69  	}
    70  	task, err := container.Task(ctx, nil)
    71  	if err != nil {
    72  		return 0, err
    73  	}
    74  	return task.Pid(), nil
    75  }
    76  
    77  // ContainerKillByContainerID kills container according to container id
    78  func (c ContainerdClient) ContainerKillByContainerID(ctx context.Context, containerID string) error {
    79  	containerID, err := c.FormatContainerID(ctx, containerID)
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	container, err := c.client.LoadContainer(ctx, containerID)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	task, err := container.Task(ctx, nil)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	err = task.Kill(ctx, syscall.SIGKILL)
    94  
    95  	return err
    96  }
    97  
    98  // ListContainerIDs lists all container IDs
    99  func (c ContainerdClient) ListContainerIDs(ctx context.Context) ([]string, error) {
   100  	// filter sandbox containers
   101  	// ref: https://github.com/containerd/containerd/blob/main/pkg/cri/server/helpers.go#L281-L285
   102  	filter := fmt.Sprintf("labels.%q==%q", containerKindLabel, containerKindContainer)
   103  	containers, err := c.client.Containers(ctx, filter)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	var ids []string
   109  	for _, container := range containers {
   110  		id := fmt.Sprintf("%s%s", containerdProtocolPrefix, container.ID())
   111  		ids = append(ids, id)
   112  	}
   113  	return ids, nil
   114  }
   115  
   116  // GetLabelsFromContainerID returns the labels according to container ID
   117  func (c ContainerdClient) GetLabelsFromContainerID(ctx context.Context, containerID string) (map[string]string, error) {
   118  	id, err := c.FormatContainerID(ctx, containerID)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	container, err := c.client.LoadContainer(ctx, id)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	labels, err := container.Labels(ctx)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	return labels, nil
   134  }
   135  
   136  func New(address string, opts ...containerd.ClientOpt) (*ContainerdClient, error) {
   137  	// Mock point to return error in unit test
   138  	if err := mock.On("NewContainerdClientError"); err != nil {
   139  		return nil, err.(error)
   140  	}
   141  	if client := mock.On("MockContainerdClient"); client != nil {
   142  		return &ContainerdClient{
   143  			client.(ContainerdClientInterface),
   144  		}, nil
   145  	}
   146  
   147  	c, err := containerd.New(address, opts...)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	// The real logic
   152  	return &ContainerdClient{
   153  		client: c,
   154  	}, nil
   155  }
   156  
   157  // WithDefaultNamespace is an alias for the function in containerd with the same name
   158  var WithDefaultNamespace = containerd.WithDefaultNamespace
   159