client.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2017 Google Inc. All Rights Reserved.
  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. package crio
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "net"
  19. "net/http"
  20. "syscall"
  21. "time"
  22. )
  23. const (
  24. CrioSocket = "/var/run/crio/crio.sock"
  25. maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
  26. )
  27. // Info represents CRI-O information as sent by the CRI-O server
  28. type Info struct {
  29. StorageDriver string `json:"storage_driver"`
  30. StorageRoot string `json:"storage_root"`
  31. }
  32. // ContainerInfo represents a given container information
  33. type ContainerInfo struct {
  34. Name string `json:"name"`
  35. Pid int `json:"pid"`
  36. Image string `json:"image"`
  37. CreatedTime int64 `json:"created_time"`
  38. Labels map[string]string `json:"labels"`
  39. Annotations map[string]string `json:"annotations"`
  40. LogPath string `json:"log_path"`
  41. Root string `json:"root"`
  42. IP string `json:"ip_address"`
  43. }
  44. type crioClient interface {
  45. Info() (Info, error)
  46. ContainerInfo(string) (*ContainerInfo, error)
  47. }
  48. type crioClientImpl struct {
  49. client *http.Client
  50. }
  51. func configureUnixTransport(tr *http.Transport, proto, addr string) error {
  52. if len(addr) > maxUnixSocketPathSize {
  53. return fmt.Errorf("Unix socket path %q is too long", addr)
  54. }
  55. // No need for compression in local communications.
  56. tr.DisableCompression = true
  57. tr.Dial = func(_, _ string) (net.Conn, error) {
  58. return net.DialTimeout(proto, addr, 32*time.Second)
  59. }
  60. return nil
  61. }
  62. // Client returns a new configured CRI-O client
  63. func Client() (crioClient, error) {
  64. tr := new(http.Transport)
  65. configureUnixTransport(tr, "unix", CrioSocket)
  66. c := &http.Client{
  67. Transport: tr,
  68. }
  69. return &crioClientImpl{
  70. client: c,
  71. }, nil
  72. }
  73. func getRequest(path string) (*http.Request, error) {
  74. req, err := http.NewRequest("GET", path, nil)
  75. if err != nil {
  76. return nil, err
  77. }
  78. // For local communications over a unix socket, it doesn't matter what
  79. // the host is. We just need a valid and meaningful host name.
  80. req.Host = "crio"
  81. req.URL.Host = CrioSocket
  82. req.URL.Scheme = "http"
  83. return req, nil
  84. }
  85. // Info returns generic info from the CRI-O server
  86. func (c *crioClientImpl) Info() (Info, error) {
  87. info := Info{}
  88. req, err := getRequest("/info")
  89. if err != nil {
  90. return info, err
  91. }
  92. resp, err := c.client.Do(req)
  93. if err != nil {
  94. return info, err
  95. }
  96. defer resp.Body.Close()
  97. if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
  98. return info, err
  99. }
  100. return info, nil
  101. }
  102. // ContainerInfo returns information about a given container
  103. func (c *crioClientImpl) ContainerInfo(id string) (*ContainerInfo, error) {
  104. req, err := getRequest("/containers/" + id)
  105. if err != nil {
  106. return nil, err
  107. }
  108. resp, err := c.client.Do(req)
  109. if err != nil {
  110. return nil, err
  111. }
  112. defer resp.Body.Close()
  113. // golang's http.Do doesn't return an error if non 200 response code is returned
  114. // handle this case here, rather than failing to decode the body
  115. if resp.StatusCode != http.StatusOK {
  116. return nil, fmt.Errorf("Error finding container %s: Status %d returned error %s", id, resp.StatusCode, resp.Body)
  117. }
  118. cInfo := ContainerInfo{}
  119. if err := json.NewDecoder(resp.Body).Decode(&cInfo); err != nil {
  120. return nil, err
  121. }
  122. return &cInfo, nil
  123. }