compose.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // +build kube
  2. /*
  3. Copyright 2020 Docker Compose CLI authors
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package kube
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "github.com/compose-spec/compose-go/types"
  20. "github.com/docker/compose-cli/api/compose"
  21. apicontext "github.com/docker/compose-cli/api/context"
  22. "github.com/docker/compose-cli/api/context/store"
  23. "github.com/docker/compose-cli/api/errdefs"
  24. "github.com/docker/compose-cli/api/progress"
  25. "github.com/docker/compose-cli/kube/client"
  26. "github.com/docker/compose-cli/kube/helm"
  27. "github.com/docker/compose-cli/kube/resources"
  28. "github.com/docker/compose-cli/utils"
  29. )
  30. type composeService struct {
  31. sdk *helm.Actions
  32. client *client.KubeClient
  33. }
  34. // NewComposeService create a kubernetes implementation of the compose.Service API
  35. func NewComposeService() (compose.Service, error) {
  36. contextStore := store.Instance()
  37. currentContext := apicontext.Current()
  38. var kubeContext store.KubeContext
  39. if err := contextStore.GetEndpoint(currentContext, &kubeContext); err != nil {
  40. return nil, err
  41. }
  42. config, err := resources.LoadConfig(kubeContext)
  43. if err != nil {
  44. return nil, err
  45. }
  46. actions, err := helm.NewActions(config)
  47. if err != nil {
  48. return nil, err
  49. }
  50. apiClient, err := client.NewKubeClient(config)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return &composeService{
  55. sdk: actions,
  56. client: apiClient,
  57. }, nil
  58. }
  59. // Up executes the equivalent to a `compose up`
  60. func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
  61. w := progress.ContextWriter(ctx)
  62. eventName := "Convert to Helm charts"
  63. w.Event(progress.CreatingEvent(eventName))
  64. chart, err := helm.GetChartInMemory(project)
  65. if err != nil {
  66. return err
  67. }
  68. w.Event(progress.NewEvent(eventName, progress.Done, ""))
  69. eventName = "Install Helm charts"
  70. w.Event(progress.CreatingEvent(eventName))
  71. err = s.sdk.InstallChart(project.Name, chart, func(format string, v ...interface{}) {
  72. message := fmt.Sprintf(format, v...)
  73. w.Event(progress.NewEvent(eventName, progress.Done, message))
  74. })
  75. if err != nil {
  76. return err
  77. }
  78. w.Event(progress.NewEvent(eventName, progress.Done, ""))
  79. return s.client.WaitForPodState(ctx, client.WaitForStatusOptions{
  80. ProjectName: project.Name,
  81. Services: project.ServiceNames(),
  82. Status: compose.RUNNING,
  83. Log: func(pod string, stateReached bool, message string) {
  84. state := progress.Done
  85. if !stateReached {
  86. state = progress.Working
  87. }
  88. w.Event(progress.NewEvent(pod, state, message))
  89. },
  90. })
  91. }
  92. // Down executes the equivalent to a `compose down`
  93. func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
  94. w := progress.ContextWriter(ctx)
  95. eventName := fmt.Sprintf("Remove %s", projectName)
  96. w.Event(progress.CreatingEvent(eventName))
  97. logger := func(format string, v ...interface{}) {
  98. message := fmt.Sprintf(format, v...)
  99. if strings.Contains(message, "Starting delete") {
  100. action := strings.Replace(message, "Starting delete for", "Delete", 1)
  101. w.Event(progress.CreatingEvent(action))
  102. w.Event(progress.NewEvent(action, progress.Done, ""))
  103. return
  104. }
  105. w.Event(progress.NewEvent(eventName, progress.Working, message))
  106. }
  107. err := s.sdk.Uninstall(projectName, logger)
  108. if err != nil {
  109. return err
  110. }
  111. events := []string{}
  112. err = s.client.WaitForPodState(ctx, client.WaitForStatusOptions{
  113. ProjectName: projectName,
  114. Services: nil,
  115. Status: compose.REMOVING,
  116. Timeout: options.Timeout,
  117. Log: func(pod string, stateReached bool, message string) {
  118. state := progress.Done
  119. if !stateReached {
  120. state = progress.Working
  121. }
  122. w.Event(progress.NewEvent(pod, state, message))
  123. if !utils.StringContains(events, pod) {
  124. events = append(events, pod)
  125. }
  126. },
  127. })
  128. if err != nil {
  129. return err
  130. }
  131. for _, e := range events {
  132. w.Event(progress.NewEvent(e, progress.Done, ""))
  133. }
  134. w.Event(progress.NewEvent(eventName, progress.Done, ""))
  135. return nil
  136. }
  137. // List executes the equivalent to a `docker stack ls`
  138. func (s *composeService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
  139. return s.sdk.ListReleases()
  140. }
  141. // Build executes the equivalent to a `compose build`
  142. func (s *composeService) Build(ctx context.Context, project *types.Project, options compose.BuildOptions) error {
  143. return errdefs.ErrNotImplemented
  144. }
  145. // Push executes the equivalent ot a `compose push`
  146. func (s *composeService) Push(ctx context.Context, project *types.Project, options compose.PushOptions) error {
  147. return errdefs.ErrNotImplemented
  148. }
  149. // Pull executes the equivalent of a `compose pull`
  150. func (s *composeService) Pull(ctx context.Context, project *types.Project, options compose.PullOptions) error {
  151. return errdefs.ErrNotImplemented
  152. }
  153. // Create executes the equivalent to a `compose create`
  154. func (s *composeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error {
  155. return errdefs.ErrNotImplemented
  156. }
  157. // Start executes the equivalent to a `compose start`
  158. func (s *composeService) Start(ctx context.Context, project *types.Project, options compose.StartOptions) error {
  159. return errdefs.ErrNotImplemented
  160. }
  161. // Restart executes the equivalent to a `compose restart`
  162. func (s *composeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
  163. return errdefs.ErrNotImplemented
  164. }
  165. // Stop executes the equivalent to a `compose stop`
  166. func (s *composeService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
  167. return errdefs.ErrNotImplemented
  168. }
  169. // Logs executes the equivalent to a `compose logs`
  170. func (s *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
  171. if len(options.Services) > 0 {
  172. consumer = utils.FilteredLogConsumer(consumer, options.Services)
  173. }
  174. return s.client.GetLogs(ctx, projectName, consumer, options.Follow)
  175. }
  176. // Ps executes the equivalent to a `compose ps`
  177. func (s *composeService) Ps(ctx context.Context, projectName string, options compose.PsOptions) ([]compose.ContainerSummary, error) {
  178. return s.client.GetContainers(ctx, projectName, options.All)
  179. }
  180. // Convert translate compose model into backend's native format
  181. func (s *composeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
  182. chart, err := helm.GetChartInMemory(project)
  183. if err != nil {
  184. return nil, err
  185. }
  186. if options.Output != "" {
  187. _, err := helm.SaveChart(chart, options.Output)
  188. return nil, err
  189. }
  190. buff := []byte{}
  191. for _, f := range chart.Raw {
  192. header := "\n" + f.Name + "\n" + strings.Repeat("-", len(f.Name)) + "\n"
  193. buff = append(buff, []byte(header)...)
  194. buff = append(buff, f.Data...)
  195. buff = append(buff, []byte("\n")...)
  196. }
  197. return buff, nil
  198. }
  199. func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
  200. return errdefs.ErrNotImplemented
  201. }
  202. // RunOneOffContainer creates a service oneoff container and starts its dependencies
  203. func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
  204. return 0, errdefs.ErrNotImplemented
  205. }
  206. func (s *composeService) Remove(ctx context.Context, project *types.Project, options compose.RemoveOptions) ([]string, error) {
  207. return nil, errdefs.ErrNotImplemented
  208. }
  209. // Exec executes a command in a running service container
  210. func (s *composeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
  211. return errdefs.ErrNotImplemented
  212. }
  213. func (s *composeService) Pause(ctx context.Context, project *types.Project) error {
  214. return errdefs.ErrNotImplemented
  215. }
  216. func (s *composeService) UnPause(ctx context.Context, project *types.Project) error {
  217. return errdefs.ErrNotImplemented
  218. }
  219. func (s *composeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
  220. return nil, errdefs.ErrNotImplemented
  221. }
  222. func (s *composeService) Events(ctx context.Context, project string, options compose.EventsOptions) error {
  223. return errdefs.ErrNotImplemented
  224. }
  225. func (s *composeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
  226. return "", 0, errdefs.ErrNotImplemented
  227. }
  228. func (s *composeService) Images(ctx context.Context, projectName string, options compose.ImagesOptions) ([]compose.ImageSummary, error) {
  229. return nil, errdefs.ErrNotImplemented
  230. }