compose.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. Copyright 2020 Docker Compose CLI 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 aci
  14. import (
  15. "context"
  16. "fmt"
  17. "net/http"
  18. "github.com/compose-spec/compose-go/types"
  19. "github.com/pkg/errors"
  20. "github.com/sirupsen/logrus"
  21. "github.com/docker/compose-cli/aci/convert"
  22. "github.com/docker/compose-cli/aci/login"
  23. "github.com/docker/compose-cli/api/compose"
  24. "github.com/docker/compose-cli/api/context/store"
  25. "github.com/docker/compose-cli/api/errdefs"
  26. "github.com/docker/compose-cli/api/progress"
  27. "github.com/docker/compose-cli/utils/formatter"
  28. )
  29. type aciComposeService struct {
  30. ctx store.AciContext
  31. storageLogin login.StorageLoginImpl
  32. }
  33. func newComposeService(ctx store.AciContext) aciComposeService {
  34. return aciComposeService{
  35. ctx: ctx,
  36. storageLogin: login.StorageLoginImpl{AciContext: ctx},
  37. }
  38. }
  39. func (cs *aciComposeService) Build(ctx context.Context, project *types.Project, options compose.BuildOptions) error {
  40. return errdefs.ErrNotImplemented
  41. }
  42. func (cs *aciComposeService) Push(ctx context.Context, project *types.Project, options compose.PushOptions) error {
  43. return errdefs.ErrNotImplemented
  44. }
  45. func (cs *aciComposeService) Pull(ctx context.Context, project *types.Project, options compose.PullOptions) error {
  46. return errdefs.ErrNotImplemented
  47. }
  48. func (cs *aciComposeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error {
  49. return errdefs.ErrNotImplemented
  50. }
  51. func (cs *aciComposeService) Start(ctx context.Context, project *types.Project, options compose.StartOptions) error {
  52. return errdefs.ErrNotImplemented
  53. }
  54. func (cs *aciComposeService) Restart(ctx context.Context, project *types.Project, options compose.RestartOptions) error {
  55. return errdefs.ErrNotImplemented
  56. }
  57. func (cs *aciComposeService) Stop(ctx context.Context, project *types.Project, options compose.StopOptions) error {
  58. return errdefs.ErrNotImplemented
  59. }
  60. func (cs *aciComposeService) Pause(ctx context.Context, project string, options compose.PauseOptions) error {
  61. return errdefs.ErrNotImplemented
  62. }
  63. func (cs *aciComposeService) UnPause(ctx context.Context, project string, options compose.PauseOptions) error {
  64. return errdefs.ErrNotImplemented
  65. }
  66. func (cs *aciComposeService) Copy(ctx context.Context, project *types.Project, options compose.CopyOptions) error {
  67. return errdefs.ErrNotImplemented
  68. }
  69. func (cs *aciComposeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
  70. return progress.Run(ctx, func(ctx context.Context) error {
  71. return cs.up(ctx, project)
  72. })
  73. }
  74. func (cs *aciComposeService) up(ctx context.Context, project *types.Project) error {
  75. logrus.Debugf("Up on project with name %q", project.Name)
  76. if err := autocreateFileshares(ctx, project); err != nil {
  77. return err
  78. }
  79. groupDefinition, err := convert.ToContainerGroup(ctx, cs.ctx, *project, cs.storageLogin)
  80. if err != nil {
  81. return err
  82. }
  83. addTag(&groupDefinition, composeContainerTag)
  84. return createOrUpdateACIContainers(ctx, cs.ctx, groupDefinition)
  85. }
  86. func (cs aciComposeService) warnKeepVolumeOnDown(ctx context.Context, projectName string) error {
  87. cgClient, err := login.NewContainerGroupsClient(cs.ctx.SubscriptionID)
  88. if err != nil {
  89. return err
  90. }
  91. cg, err := cgClient.Get(ctx, cs.ctx.ResourceGroup, projectName)
  92. if err != nil {
  93. return err
  94. }
  95. if cg.Volumes == nil {
  96. return nil
  97. }
  98. for _, v := range *cg.Volumes {
  99. if v.AzureFile == nil || v.AzureFile.StorageAccountName == nil || v.AzureFile.ShareName == nil {
  100. continue
  101. }
  102. fmt.Printf("WARNING: fileshare \"%s/%s\" will NOT be deleted. Use 'docker volume rm' if you want to delete this volume\n",
  103. *v.AzureFile.StorageAccountName, *v.AzureFile.ShareName)
  104. }
  105. return nil
  106. }
  107. func (cs *aciComposeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
  108. if options.Volumes {
  109. return errors.Wrap(errdefs.ErrNotImplemented, "--volumes option is not supported on ACI")
  110. }
  111. if options.Images != "" {
  112. return errors.Wrap(errdefs.ErrNotImplemented, "--rmi option is not supported on ACI")
  113. }
  114. return progress.Run(ctx, func(ctx context.Context) error {
  115. logrus.Debugf("Down on project with name %q", projectName)
  116. if err := cs.warnKeepVolumeOnDown(ctx, projectName); err != nil {
  117. return err
  118. }
  119. cg, err := deleteACIContainerGroup(ctx, cs.ctx, projectName)
  120. if err != nil {
  121. return err
  122. }
  123. if cg.IsHTTPStatus(http.StatusNoContent) {
  124. return errdefs.ErrNotFound
  125. }
  126. return err
  127. })
  128. }
  129. func (cs *aciComposeService) Ps(ctx context.Context, projectName string, options compose.PsOptions) ([]compose.ContainerSummary, error) {
  130. groupsClient, err := login.NewContainerGroupsClient(cs.ctx.SubscriptionID)
  131. if err != nil {
  132. return nil, err
  133. }
  134. group, err := groupsClient.Get(ctx, cs.ctx.ResourceGroup, projectName)
  135. if err != nil {
  136. return nil, err
  137. }
  138. if group.Containers == nil || len(*group.Containers) == 0 {
  139. return nil, fmt.Errorf("no containers found in ACI container group %s", projectName)
  140. }
  141. res := []compose.ContainerSummary{}
  142. for _, container := range *group.Containers {
  143. if isContainerVisible(container, group, false) {
  144. continue
  145. }
  146. var publishers []compose.PortPublisher
  147. urls := formatter.PortsToStrings(convert.ToPorts(group.IPAddress, *container.Ports), convert.FQDN(group, cs.ctx.Location))
  148. for i, p := range *container.Ports {
  149. publishers = append(publishers, compose.PortPublisher{
  150. URL: urls[i],
  151. TargetPort: int(*p.Port),
  152. PublishedPort: int(*p.Port),
  153. Protocol: string(p.Protocol),
  154. })
  155. }
  156. id := getContainerID(group, container)
  157. res = append(res, compose.ContainerSummary{
  158. ID: id,
  159. Name: id,
  160. Project: projectName,
  161. Service: *container.Name,
  162. State: convert.GetStatus(container, group),
  163. Publishers: publishers,
  164. })
  165. }
  166. return res, nil
  167. }
  168. func (cs *aciComposeService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
  169. containerGroups, err := getACIContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup)
  170. if err != nil {
  171. return nil, err
  172. }
  173. var stacks []compose.Stack
  174. for _, group := range containerGroups {
  175. if _, found := group.Tags[composeContainerTag]; !found {
  176. continue
  177. }
  178. state := compose.RUNNING
  179. for _, container := range *group.ContainerGroupProperties.Containers {
  180. containerState := convert.GetStatus(container, group)
  181. if containerState != compose.RUNNING {
  182. state = containerState
  183. break
  184. }
  185. }
  186. stacks = append(stacks, compose.Stack{
  187. ID: *group.ID,
  188. Name: *group.Name,
  189. Status: state,
  190. })
  191. }
  192. return stacks, nil
  193. }
  194. func (cs *aciComposeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
  195. return errdefs.ErrNotImplemented
  196. }
  197. func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
  198. return nil, errdefs.ErrNotImplemented
  199. }
  200. func (cs *aciComposeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
  201. return errdefs.ErrNotImplemented
  202. }
  203. func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
  204. return 0, errdefs.ErrNotImplemented
  205. }
  206. func (cs *aciComposeService) Remove(ctx context.Context, project *types.Project, options compose.RemoveOptions) error {
  207. return errdefs.ErrNotImplemented
  208. }
  209. func (cs *aciComposeService) Exec(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
  210. return 0, errdefs.ErrNotImplemented
  211. }
  212. func (cs *aciComposeService) Top(ctx context.Context, projectName string, services []string) ([]compose.ContainerProcSummary, error) {
  213. return nil, errdefs.ErrNotImplemented
  214. }
  215. func (cs *aciComposeService) Events(ctx context.Context, project string, options compose.EventsOptions) error {
  216. return errdefs.ErrNotImplemented
  217. }
  218. func (cs *aciComposeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
  219. return "", 0, errdefs.ErrNotImplemented
  220. }
  221. func (cs *aciComposeService) Images(ctx context.Context, projectName string, options compose.ImagesOptions) ([]compose.ImageSummary, error) {
  222. return nil, errdefs.ErrNotImplemented
  223. }