1
0

backend.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package azure
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "strconv"
  7. "strings"
  8. "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
  9. "github.com/Azure/go-autorest/autorest/azure/auth"
  10. "github.com/compose-spec/compose-go/types"
  11. "github.com/pkg/errors"
  12. "github.com/sirupsen/logrus"
  13. "github.com/docker/api/azure/convert"
  14. "github.com/docker/api/backend"
  15. "github.com/docker/api/compose"
  16. "github.com/docker/api/containers"
  17. apicontext "github.com/docker/api/context"
  18. "github.com/docker/api/context/store"
  19. )
  20. const singleContainerName = "single--container--aci"
  21. func init() {
  22. backend.Register("aci", "aci", func(ctx context.Context) (backend.Service, error) {
  23. return New(ctx)
  24. })
  25. }
  26. func getter() interface{} {
  27. return &store.AciContext{}
  28. }
  29. // New creates a backend that can manage containers
  30. func New(ctx context.Context) (backend.Service, error) {
  31. currentContext := apicontext.CurrentContext(ctx)
  32. contextStore, err := store.New()
  33. if err != nil {
  34. return nil, err
  35. }
  36. metadata, err := contextStore.Get(currentContext, getter)
  37. if err != nil {
  38. return nil, errors.Wrap(err, "wrong context type")
  39. }
  40. aciContext, _ := metadata.Metadata.Data.(store.AciContext)
  41. auth, _ := auth.NewAuthorizerFromCLI()
  42. containerGroupsClient := containerinstance.NewContainerGroupsClient(aciContext.SubscriptionID)
  43. containerGroupsClient.Authorizer = auth
  44. return getAciAPIService(containerGroupsClient, aciContext), nil
  45. }
  46. func getAciAPIService(cgc containerinstance.ContainerGroupsClient, aciCtx store.AciContext) *aciAPIService {
  47. return &aciAPIService{
  48. aciContainerService: aciContainerService{
  49. containerGroupsClient: cgc,
  50. ctx: aciCtx,
  51. },
  52. aciComposeService: aciComposeService{
  53. containerGroupsClient: cgc,
  54. ctx: aciCtx,
  55. },
  56. }
  57. }
  58. type aciAPIService struct {
  59. aciContainerService
  60. aciComposeService
  61. }
  62. func (a *aciAPIService) ContainerService() containers.Service {
  63. return &aciContainerService{
  64. containerGroupsClient: a.aciContainerService.containerGroupsClient,
  65. ctx: a.aciContainerService.ctx,
  66. }
  67. }
  68. func (a *aciAPIService) ComposeService() compose.Service {
  69. return &aciComposeService{
  70. containerGroupsClient: a.aciComposeService.containerGroupsClient,
  71. ctx: a.aciComposeService.ctx,
  72. }
  73. }
  74. type aciContainerService struct {
  75. containerGroupsClient containerinstance.ContainerGroupsClient
  76. ctx store.AciContext
  77. }
  78. func (cs *aciContainerService) List(ctx context.Context) ([]containers.Container, error) {
  79. var containerGroups []containerinstance.ContainerGroup
  80. result, err := cs.containerGroupsClient.ListByResourceGroup(ctx, cs.ctx.ResourceGroup)
  81. if err != nil {
  82. return []containers.Container{}, err
  83. }
  84. for result.NotDone() {
  85. containerGroups = append(containerGroups, result.Values()...)
  86. if err := result.NextWithContext(ctx); err != nil {
  87. return []containers.Container{}, err
  88. }
  89. }
  90. var res []containers.Container
  91. for _, containerGroup := range containerGroups {
  92. group, err := cs.containerGroupsClient.Get(ctx, cs.ctx.ResourceGroup, *containerGroup.Name)
  93. if err != nil {
  94. return []containers.Container{}, err
  95. }
  96. for _, container := range *group.Containers {
  97. var containerID string
  98. if *container.Name == singleContainerName {
  99. containerID = *containerGroup.Name
  100. } else {
  101. containerID = *containerGroup.Name + "_" + *container.Name
  102. }
  103. status := "Unknown"
  104. if container.InstanceView != nil && container.InstanceView.CurrentState != nil {
  105. status = *container.InstanceView.CurrentState.State
  106. }
  107. res = append(res, containers.Container{
  108. ID: containerID,
  109. Image: *container.Image,
  110. Status: status,
  111. })
  112. }
  113. }
  114. return res, nil
  115. }
  116. func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerConfig) error {
  117. var ports []types.ServicePortConfig
  118. for _, p := range r.Ports {
  119. ports = append(ports, types.ServicePortConfig{
  120. Target: p.Destination,
  121. Published: p.Source,
  122. })
  123. }
  124. project := compose.Project{
  125. Name: r.ID,
  126. Config: types.Config{
  127. Services: []types.ServiceConfig{
  128. {
  129. Name: singleContainerName,
  130. Image: r.Image,
  131. Ports: ports,
  132. },
  133. },
  134. },
  135. }
  136. logrus.Debugf("Running container %q with name %q\n", r.Image, r.ID)
  137. groupDefinition, err := convert.ToContainerGroup(cs.ctx, project)
  138. if err != nil {
  139. return err
  140. }
  141. return createACIContainers(ctx, cs.ctx, groupDefinition)
  142. }
  143. func getGrouNameContainername(containerID string) (groupName string, containerName string) {
  144. tokens := strings.Split(containerID, "_")
  145. groupName = tokens[0]
  146. if len(tokens) > 1 {
  147. containerName = tokens[len(tokens)-1]
  148. groupName = containerID[:len(containerID)-(len(containerName)+1)]
  149. } else {
  150. containerName = singleContainerName
  151. }
  152. return groupName, containerName
  153. }
  154. func (cs *aciContainerService) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
  155. groupName, containerAciName := getGrouNameContainername(name)
  156. containerExecResponse, err := execACIContainer(ctx, cs.ctx, command, groupName, containerAciName)
  157. if err != nil {
  158. return err
  159. }
  160. return exec(
  161. context.Background(),
  162. *containerExecResponse.WebSocketURI,
  163. *containerExecResponse.Password,
  164. reader,
  165. writer,
  166. )
  167. }
  168. func (cs *aciContainerService) Logs(ctx context.Context, containerName string, req containers.LogsRequest) error {
  169. groupName, containerAciName := getGrouNameContainername(containerName)
  170. logs, err := getACIContainerLogs(ctx, cs.ctx, groupName, containerAciName)
  171. if err != nil {
  172. return err
  173. }
  174. if req.Tail != "all" {
  175. tail, err := strconv.Atoi(req.Tail)
  176. if err != nil {
  177. return err
  178. }
  179. lines := strings.Split(logs, "\n")
  180. // If asked for less lines than exist, take only those lines
  181. if tail <= len(lines) {
  182. logs = strings.Join(lines[len(lines)-tail:], "\n")
  183. }
  184. }
  185. _, err = fmt.Fprint(req.Writer, logs)
  186. return err
  187. }
  188. type aciComposeService struct {
  189. containerGroupsClient containerinstance.ContainerGroupsClient
  190. ctx store.AciContext
  191. }
  192. func (cs *aciComposeService) Up(ctx context.Context, opts compose.ProjectOptions) error {
  193. project, err := compose.ProjectFromOptions(&opts)
  194. if err != nil {
  195. return err
  196. }
  197. logrus.Debugf("Up on project with name %q\n", project.Name)
  198. groupDefinition, err := convert.ToContainerGroup(cs.ctx, *project)
  199. if err != nil {
  200. return err
  201. }
  202. return createACIContainers(ctx, cs.ctx, groupDefinition)
  203. }
  204. func (cs *aciComposeService) Down(ctx context.Context, opts compose.ProjectOptions) error {
  205. project, err := compose.ProjectFromOptions(&opts)
  206. if err != nil {
  207. return err
  208. }
  209. logrus.Debugf("Down on project with name %q\n", project.Name)
  210. _, err = deleteACIContainerGroup(ctx, cs.ctx, project.Name)
  211. return err
  212. }