containers.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2019 Yunion
  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 compute
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "os/exec"
  20. "path"
  21. "strings"
  22. "github.com/ghodss/yaml"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/onecloud/cmd/climc/shell"
  26. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  27. "yunion.io/x/onecloud/pkg/mcclient"
  28. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  29. options "yunion.io/x/onecloud/pkg/mcclient/options/compute"
  30. "yunion.io/x/onecloud/pkg/util/fileutils2"
  31. "yunion.io/x/onecloud/pkg/util/pod/stream/cp"
  32. )
  33. func init() {
  34. cmd := shell.NewResourceCmd(&modules.Containers)
  35. cmd.Create(new(options.ContainerCreateOptions))
  36. cmd.List(new(options.ContainerListOptions))
  37. cmd.Show(new(options.ContainerShowOptions))
  38. cmd.GetMetadata(new(options.ServerIdOptions))
  39. cmd.BatchDelete(new(options.ContainerDeleteOptions))
  40. cmd.BatchPerform("stop", new(options.ContainerStopOptions))
  41. cmd.BatchPerform("start", new(options.ContainerStartOptions))
  42. cmd.BatchPerform("restart", new(options.ContainerRestartOptions))
  43. cmd.BatchPerform("syncstatus", new(options.ContainerIdsOptions))
  44. cmd.Perform("save-volume-mount-image", new(options.ContainerSaveVolumeMountImage))
  45. cmd.Perform("exec-sync", new(options.ContainerExecSyncOptions))
  46. cmd.BatchPerform("set-resources-limit", new(options.ContainerSetResourcesLimitOptions))
  47. cmd.Perform("commit", new(options.ContainerCommitOptions))
  48. cmd.Perform("add-volume-mount-post-overlay", new(options.ContainerAddVolumeMountPostOverlayOptions))
  49. cmd.Perform("remove-volume-mount-post-overlay", new(options.ContainerRemoveVolumeMountPostOverlayOptions))
  50. type UpdateSpecOptions struct {
  51. ID string `help:"ID or name of server" json:"-"`
  52. }
  53. R(&UpdateSpecOptions{}, "container-update-spec", "Update spec of a container", func(s *mcclient.ClientSession, opts *UpdateSpecOptions) error {
  54. result, err := modules.Containers.Get(s, opts.ID, nil)
  55. if err != nil {
  56. return errors.Wrap(err, "get container id")
  57. }
  58. yamlBytes := result.YAMLString()
  59. tempfile, err := ioutil.TempFile("", fmt.Sprintf("container-%s*.yaml", opts.ID))
  60. if err != nil {
  61. return err
  62. }
  63. defer os.Remove(tempfile.Name())
  64. if _, err := tempfile.Write([]byte(yamlBytes)); err != nil {
  65. return err
  66. }
  67. if err := tempfile.Close(); err != nil {
  68. return err
  69. }
  70. cmd := exec.Command("vim", tempfile.Name())
  71. cmd.Stdin = os.Stdin
  72. cmd.Stdout = os.Stdout
  73. if err := cmd.Run(); err != nil {
  74. return err
  75. }
  76. content, err := ioutil.ReadFile(tempfile.Name())
  77. if err != nil {
  78. return err
  79. }
  80. jsonBytes, err := yaml.YAMLToJSON(content)
  81. if err != nil {
  82. return err
  83. }
  84. body, err := jsonutils.Parse(jsonBytes)
  85. if err != nil {
  86. return err
  87. }
  88. if _, err := modules.Containers.Update(s, opts.ID, body); err != nil {
  89. return errors.Wrap(err, "update spec")
  90. }
  91. return nil
  92. })
  93. type SetSpecOptions struct {
  94. ID string `help:"ID or name of server" json:"-"`
  95. EnableSimulateCpu bool `help:"Enable simulating /sys/devices/system/cpu directory"`
  96. }
  97. R(&SetSpecOptions{}, "container-set-spec", "Set spec of a container", func(s *mcclient.ClientSession, opts *SetSpecOptions) error {
  98. result, err := modules.Containers.Get(s, opts.ID, nil)
  99. if err != nil {
  100. return errors.Wrap(err, "get container id")
  101. }
  102. spec := new(computeapi.ContainerSpec)
  103. if err := result.Unmarshal(spec, "spec"); err != nil {
  104. return errors.Wrap(err, "unmarshal to spec")
  105. }
  106. spec.SimulateCpu = opts.EnableSimulateCpu
  107. result.(*jsonutils.JSONDict).Set("spec", jsonutils.Marshal(spec))
  108. if _, err := modules.Containers.Update(s, opts.ID, result); err != nil {
  109. return errors.Wrap(err, "update spec")
  110. }
  111. return nil
  112. })
  113. R(new(options.ContainerExecOptions), "container-exec", "Container exec", func(s *mcclient.ClientSession, opts *options.ContainerExecOptions) error {
  114. man := modules.Containers
  115. return man.Exec(s, opts.ID, opts.ToAPIInput())
  116. })
  117. R(new(options.ContainerLogOptions), "container-log", "Get container log", func(s *mcclient.ClientSession, opts *options.ContainerLogOptions) error {
  118. man := modules.Containers
  119. input, err := opts.ToAPIInput()
  120. if err != nil {
  121. return err
  122. }
  123. if err := man.LogToWriter(s, opts.ID, input, os.Stdout); err != nil {
  124. return errors.Wrap(err, "get container log")
  125. }
  126. return nil
  127. })
  128. copyToContainer := func(s *mcclient.ClientSession, src, dst string, rawFile bool) error {
  129. parts := strings.Split(dst, ":")
  130. if len(parts) != 2 {
  131. return fmt.Errorf("invalid container id: %s", dst)
  132. }
  133. if rawFile {
  134. if fileutils2.IsDir(src) {
  135. return fmt.Errorf("source path cannot be a directory when raw file is true: %s", src)
  136. }
  137. fr, err := os.Open(src)
  138. if err != nil {
  139. return errors.Wrapf(err, "open file: %v", src)
  140. }
  141. defer fr.Close()
  142. if err := modules.Containers.CopyTo(s, parts[0], parts[1], fr); err != nil {
  143. return errors.Wrapf(err, "copy file to container")
  144. }
  145. return nil
  146. } else {
  147. return cp.NewCopy().CopyToContainer(s, src, cp.ContainerFileOpt{
  148. ContainerId: parts[0],
  149. File: parts[1],
  150. })
  151. }
  152. }
  153. copyFromContainer := func(s *mcclient.ClientSession, src, dst string, rawFile bool) error {
  154. parts := strings.Split(src, ":")
  155. if len(parts) != 2 {
  156. return fmt.Errorf("invalid container id: %s", src)
  157. }
  158. ctrId := parts[0]
  159. ctrFile := parts[1]
  160. destFile := dst
  161. if fileutils2.IsDir(destFile) {
  162. destFile = path.Join(destFile, path.Base(ctrFile))
  163. }
  164. if rawFile {
  165. fw, err := os.Create(destFile)
  166. if err != nil {
  167. return errors.Wrapf(err, "open file: %v", destFile)
  168. }
  169. defer fw.Close()
  170. if err := modules.Containers.CopyFrom(s, ctrId, ctrFile, fw); err != nil {
  171. return errors.Wrap(err, "copy from")
  172. }
  173. return nil
  174. } else {
  175. return cp.NewCopy().CopyFromContainer(s, cp.ContainerFileOpt{
  176. ContainerId: ctrId,
  177. File: ctrFile,
  178. }, destFile)
  179. }
  180. }
  181. R(new(options.ContainerCopyOptions), "container-cp", "Container copy", func(s *mcclient.ClientSession, opts *options.ContainerCopyOptions) error {
  182. if strings.Contains(opts.SRC, ":") && !strings.Contains(opts.DST, ":") {
  183. // copy from container to host
  184. err := copyFromContainer(s, opts.SRC, opts.DST, opts.RawFile)
  185. if err != nil {
  186. return errors.Wrapf(err, "copy from container to host")
  187. }
  188. return nil
  189. } else if !strings.Contains(opts.SRC, ":") && strings.Contains(opts.DST, ":") {
  190. // copy from host to container
  191. err := copyToContainer(s, opts.SRC, opts.DST, opts.RawFile)
  192. if err != nil {
  193. return errors.Wrapf(err, "copy to container")
  194. }
  195. return nil
  196. } else if strings.Contains(opts.SRC, ":") && strings.Contains(opts.DST, ":") {
  197. // copy from container to container
  198. return fmt.Errorf("copy from container to container is not supported")
  199. } else {
  200. return fmt.Errorf("copy between local files is not supported")
  201. }
  202. })
  203. R(new(options.ContainerCopyFromOptions), "container-cp-from", "Container copy", func(s *mcclient.ClientSession, opts *options.ContainerCopyFromOptions) error {
  204. err := copyFromContainer(s, opts.CONTAINER_ID_PATH, opts.DST_PATH, opts.RawFile)
  205. if err != nil {
  206. return errors.Wrapf(err, "copy from container to host")
  207. }
  208. return nil
  209. })
  210. }