- Throw an error if there is more than one known socket on the host. - Remove the special handling for docker+containerd. - Remove the local instances of constants for endpoints for Windows / Unix and use the defaultKnownCRISockets variable which is populated from OS specific constants. - Update error message in detectCRISocketImpl to have more details. - Make detectCRISocketImpl accept a list of "known" sockets - Update unit tests for detectCRISocketImpl and make them use generic paths such as "unix:///foo/bar.sock".
153 lines
5.1 KiB
Go
153 lines
5.1 KiB
Go
/*
|
|
Copyright 2018 The Kubernetes 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 runtime
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
errorsutil "k8s.io/apimachinery/pkg/util/errors"
|
|
utilsexec "k8s.io/utils/exec"
|
|
|
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
|
)
|
|
|
|
// defaultKnownCRISockets holds the set of known CRI endpoints
|
|
var defaultKnownCRISockets = []string{
|
|
constants.CRISocketContainerd,
|
|
constants.CRISocketCRIO,
|
|
constants.CRISocketDocker,
|
|
}
|
|
|
|
// ContainerRuntime is an interface for working with container runtimes
|
|
type ContainerRuntime interface {
|
|
Socket() string
|
|
IsRunning() error
|
|
ListKubeContainers() ([]string, error)
|
|
RemoveContainers(containers []string) error
|
|
PullImage(image string) error
|
|
ImageExists(image string) (bool, error)
|
|
}
|
|
|
|
// CRIRuntime is a struct that interfaces with the CRI
|
|
type CRIRuntime struct {
|
|
exec utilsexec.Interface
|
|
criSocket string
|
|
}
|
|
|
|
// NewContainerRuntime sets up and returns a ContainerRuntime struct
|
|
func NewContainerRuntime(execer utilsexec.Interface, criSocket string) (ContainerRuntime, error) {
|
|
toolName := "crictl"
|
|
runtime := &CRIRuntime{execer, criSocket}
|
|
if _, err := execer.LookPath(toolName); err != nil {
|
|
return nil, errors.Wrapf(err, "%s is required by the container runtime", toolName)
|
|
}
|
|
return runtime, nil
|
|
}
|
|
|
|
// Socket returns the CRI socket endpoint
|
|
func (runtime *CRIRuntime) Socket() string {
|
|
return runtime.criSocket
|
|
}
|
|
|
|
// IsRunning checks if runtime is running
|
|
func (runtime *CRIRuntime) IsRunning() error {
|
|
if out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "info").CombinedOutput(); err != nil {
|
|
return errors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListKubeContainers lists running k8s CRI pods
|
|
func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) {
|
|
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pods", "-q").CombinedOutput()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "output: %s, error", string(out))
|
|
}
|
|
pods := []string{}
|
|
pods = append(pods, strings.Fields(string(out))...)
|
|
return pods, nil
|
|
}
|
|
|
|
// RemoveContainers removes running k8s pods
|
|
func (runtime *CRIRuntime) RemoveContainers(containers []string) error {
|
|
errs := []error{}
|
|
for _, container := range containers {
|
|
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "stopp", container).CombinedOutput()
|
|
if err != nil {
|
|
// don't stop on errors, try to remove as many containers as possible
|
|
errs = append(errs, errors.Wrapf(err, "failed to stop running pod %s: output: %s, error", container, string(out)))
|
|
} else {
|
|
out, err = runtime.exec.Command("crictl", "-r", runtime.criSocket, "rmp", container).CombinedOutput()
|
|
if err != nil {
|
|
errs = append(errs, errors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
|
|
}
|
|
}
|
|
}
|
|
return errorsutil.NewAggregate(errs)
|
|
}
|
|
|
|
// PullImage pulls the image
|
|
func (runtime *CRIRuntime) PullImage(image string) error {
|
|
var err error
|
|
var out []byte
|
|
for i := 0; i < constants.PullImageRetry; i++ {
|
|
out, err = runtime.exec.Command("crictl", "-r", runtime.criSocket, "pull", image).CombinedOutput()
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.Wrapf(err, "output: %s, error", out)
|
|
}
|
|
|
|
// ImageExists checks to see if the image exists on the system
|
|
func (runtime *CRIRuntime) ImageExists(image string) (bool, error) {
|
|
err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "inspecti", image).Run()
|
|
return err == nil, nil
|
|
}
|
|
|
|
// detectCRISocketImpl is separated out only for test purposes, DON'T call it directly, use DetectCRISocket instead
|
|
func detectCRISocketImpl(isSocket func(string) bool, knownCRISockets []string) (string, error) {
|
|
foundCRISockets := []string{}
|
|
|
|
for _, socket := range knownCRISockets {
|
|
if isSocket(socket) {
|
|
foundCRISockets = append(foundCRISockets, socket)
|
|
}
|
|
}
|
|
|
|
switch len(foundCRISockets) {
|
|
case 0:
|
|
// Fall back to the default socket if no CRI is detected, we can error out later on if we need it
|
|
return constants.DefaultCRISocket, nil
|
|
case 1:
|
|
// Precisely one CRI found, use that
|
|
return foundCRISockets[0], nil
|
|
default:
|
|
// Multiple CRIs installed?
|
|
return "", errors.Errorf("Found multiple CRI endpoints on the host. Please define which one do you wish "+
|
|
"to use by setting the 'criSocket' field in the kubeadm configuration file: %s",
|
|
strings.Join(foundCRISockets, ", "))
|
|
}
|
|
}
|
|
|
|
// DetectCRISocket uses a list of known CRI sockets to detect one. If more than one or none is discovered, an error is returned.
|
|
func DetectCRISocket() (string, error) {
|
|
return detectCRISocketImpl(isExistingSocket, defaultKnownCRISockets)
|
|
}
|