compose.go 8.4 KB

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