runtime.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package util
  14. import (
  15. "path/filepath"
  16. goruntime "runtime"
  17. "strings"
  18. "github.com/pkg/errors"
  19. errorsutil "k8s.io/apimachinery/pkg/util/errors"
  20. "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  21. utilsexec "k8s.io/utils/exec"
  22. )
  23. // ContainerRuntime is an interface for working with container runtimes
  24. type ContainerRuntime interface {
  25. IsDocker() bool
  26. IsRunning() error
  27. ListKubeContainers() ([]string, error)
  28. RemoveContainers(containers []string) error
  29. PullImage(image string) error
  30. ImageExists(image string) (bool, error)
  31. }
  32. // CRIRuntime is a struct that interfaces with the CRI
  33. type CRIRuntime struct {
  34. exec utilsexec.Interface
  35. criSocket string
  36. }
  37. // DockerRuntime is a struct that interfaces with the Docker daemon
  38. type DockerRuntime struct {
  39. exec utilsexec.Interface
  40. }
  41. // NewContainerRuntime sets up and returns a ContainerRuntime struct
  42. func NewContainerRuntime(execer utilsexec.Interface, criSocket string) (ContainerRuntime, error) {
  43. var toolName string
  44. var runtime ContainerRuntime
  45. if criSocket != constants.DefaultDockerCRISocket {
  46. toolName = "crictl"
  47. // !!! temporary work around crictl warning:
  48. // Using "/var/run/crio/crio.sock" as endpoint is deprecated,
  49. // please consider using full url format "unix:///var/run/crio/crio.sock"
  50. if filepath.IsAbs(criSocket) && goruntime.GOOS != "windows" {
  51. criSocket = "unix://" + criSocket
  52. }
  53. runtime = &CRIRuntime{execer, criSocket}
  54. } else {
  55. toolName = "docker"
  56. runtime = &DockerRuntime{execer}
  57. }
  58. if _, err := execer.LookPath(toolName); err != nil {
  59. return nil, errors.Wrapf(err, "%s is required for container runtime", toolName)
  60. }
  61. return runtime, nil
  62. }
  63. // IsDocker returns true if the runtime is docker
  64. func (runtime *CRIRuntime) IsDocker() bool {
  65. return false
  66. }
  67. // IsDocker returns true if the runtime is docker
  68. func (runtime *DockerRuntime) IsDocker() bool {
  69. return true
  70. }
  71. // IsRunning checks if runtime is running
  72. func (runtime *CRIRuntime) IsRunning() error {
  73. if out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "info").CombinedOutput(); err != nil {
  74. return errors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
  75. }
  76. return nil
  77. }
  78. // IsRunning checks if runtime is running
  79. func (runtime *DockerRuntime) IsRunning() error {
  80. if out, err := runtime.exec.Command("docker", "info").CombinedOutput(); err != nil {
  81. return errors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
  82. }
  83. return nil
  84. }
  85. // ListKubeContainers lists running k8s CRI pods
  86. func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) {
  87. out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pods", "-q").CombinedOutput()
  88. if err != nil {
  89. return nil, errors.Wrapf(err, "output: %s, error", string(out))
  90. }
  91. pods := []string{}
  92. pods = append(pods, strings.Fields(string(out))...)
  93. return pods, nil
  94. }
  95. // ListKubeContainers lists running k8s containers
  96. func (runtime *DockerRuntime) ListKubeContainers() ([]string, error) {
  97. output, err := runtime.exec.Command("docker", "ps", "-a", "--filter", "name=k8s_", "-q").CombinedOutput()
  98. return strings.Fields(string(output)), err
  99. }
  100. // RemoveContainers removes running k8s pods
  101. func (runtime *CRIRuntime) RemoveContainers(containers []string) error {
  102. errs := []error{}
  103. for _, container := range containers {
  104. out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "stopp", container).CombinedOutput()
  105. if err != nil {
  106. // don't stop on errors, try to remove as many containers as possible
  107. errs = append(errs, errors.Wrapf(err, "failed to stop running pod %s: output: %s, error", container, string(out)))
  108. } else {
  109. out, err = runtime.exec.Command("crictl", "-r", runtime.criSocket, "rmp", container).CombinedOutput()
  110. if err != nil {
  111. errs = append(errs, errors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
  112. }
  113. }
  114. }
  115. return errorsutil.NewAggregate(errs)
  116. }
  117. // RemoveContainers removes running containers
  118. func (runtime *DockerRuntime) RemoveContainers(containers []string) error {
  119. errs := []error{}
  120. for _, container := range containers {
  121. out, err := runtime.exec.Command("docker", "rm", "--force", "--volumes", container).CombinedOutput()
  122. if err != nil {
  123. // don't stop on errors, try to remove as many containers as possible
  124. errs = append(errs, errors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
  125. }
  126. }
  127. return errorsutil.NewAggregate(errs)
  128. }
  129. // PullImage pulls the image
  130. func (runtime *CRIRuntime) PullImage(image string) error {
  131. out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pull", image).CombinedOutput()
  132. if err != nil {
  133. return errors.Wrapf(err, "output: %s, error", string(out))
  134. }
  135. return nil
  136. }
  137. // PullImage pulls the image
  138. func (runtime *DockerRuntime) PullImage(image string) error {
  139. out, err := runtime.exec.Command("docker", "pull", image).CombinedOutput()
  140. if err != nil {
  141. return errors.Wrapf(err, "output: %s, error", string(out))
  142. }
  143. return nil
  144. }
  145. // ImageExists checks to see if the image exists on the system
  146. func (runtime *CRIRuntime) ImageExists(image string) (bool, error) {
  147. err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "inspecti", image).Run()
  148. return err == nil, nil
  149. }
  150. // ImageExists checks to see if the image exists on the system
  151. func (runtime *DockerRuntime) ImageExists(image string) (bool, error) {
  152. err := runtime.exec.Command("docker", "inspect", image).Run()
  153. return err == nil, nil
  154. }
  155. // detectCRISocketImpl is separated out only for test purposes, DON'T call it directly, use DetectCRISocket instead
  156. func detectCRISocketImpl(isSocket func(string) bool) (string, error) {
  157. foundCRISockets := []string{}
  158. knownCRISockets := []string{
  159. // Docker and containerd sockets are special cased below, hence not to be included here
  160. "/var/run/crio/crio.sock",
  161. }
  162. if isSocket(dockerSocket) {
  163. // the path in dockerSocket is not CRI compatible, hence we should replace it with a CRI compatible socket
  164. foundCRISockets = append(foundCRISockets, constants.DefaultDockerCRISocket)
  165. } else if isSocket(containerdSocket) {
  166. // Docker 18.09 gets bundled together with containerd, thus having both dockerSocket and containerdSocket present.
  167. // For compatibility reasons, we use the containerd socket only if Docker is not detected.
  168. foundCRISockets = append(foundCRISockets, containerdSocket)
  169. }
  170. for _, socket := range knownCRISockets {
  171. if isSocket(socket) {
  172. foundCRISockets = append(foundCRISockets, socket)
  173. }
  174. }
  175. switch len(foundCRISockets) {
  176. case 0:
  177. // Fall back to Docker if no CRI is detected, we can error out later on if we need it
  178. return constants.DefaultDockerCRISocket, nil
  179. case 1:
  180. // Precisely one CRI found, use that
  181. return foundCRISockets[0], nil
  182. default:
  183. // Multiple CRIs installed?
  184. return "", errors.Errorf("Found multiple CRI sockets, please use --cri-socket to select one: %s", strings.Join(foundCRISockets, ", "))
  185. }
  186. }
  187. // DetectCRISocket uses a list of known CRI sockets to detect one. If more than one or none is discovered, an error is returned.
  188. func DetectCRISocket() (string, error) {
  189. return detectCRISocketImpl(isExistingSocket)
  190. }