Selaa lähdekoodia

Follow logs on ACI.

This is not ideal, the ACI API doesn't give us a stream of logs, so we
need to fake it by moving the cursor up and rewriting the logs to
stdout. This means that, on gRPC side, we will stream the whole logs
each time. This is ok for now but we need to push Azure to give us a
real streaming API for logs
Djordje Lukic 5 vuotta sitten
vanhempi
sitoutus
06596dcd90
2 muutettua tiedostoa jossa 45 lisäystä ja 5 poistoa
  1. 38 0
      azure/aci.go
  2. 7 5
      azure/backend.go

+ 38 - 0
azure/aci.go

@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"strings"
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
@@ -29,6 +30,7 @@ import (
 	tm "github.com/buger/goterm"
 	"github.com/gobwas/ws"
 	"github.com/gobwas/ws/wsutil"
+	"github.com/morikuni/aec"
 	"github.com/pkg/errors"
 
 	"github.com/docker/api/azure/login"
@@ -234,6 +236,42 @@ func getACIContainerLogs(ctx context.Context, aciContext store.AciContext, conta
 	return *logs.Content, err
 }
 
+func streamLogs(ctx context.Context, aciContext store.AciContext, containerGroupName, containerName string, out io.Writer) error {
+	lastOutput := 0
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		default:
+			logs, err := getACIContainerLogs(ctx, aciContext, containerGroupName, containerName, nil)
+			if err != nil {
+				return err
+			}
+			logLines := strings.Split(logs, "\n")
+			currentOutput := len(logLines)
+
+			b := aec.EmptyBuilder
+			for i := 0; i < lastOutput; i++ {
+				b = b.Up(1)
+			}
+
+			// Note: a backend should not do this normally, this breaks the log
+			// streaming over gRPC but this is the only thing we can do with
+			// the kind of logs ACI is giving us. Hopefully Azue will give us
+			// a real logs streaming api soon.
+			fmt.Fprint(out, b.Column(0).ANSI)
+
+			for i := 0; i < currentOutput-1; i++ {
+				fmt.Fprintln(out, logLines[i])
+			}
+
+			lastOutput = currentOutput - 1
+
+			time.Sleep(2 * time.Second)
+		}
+	}
+}
+
 func getContainerGroupsClient(subscriptionID string) (containerinstance.ContainerGroupsClient, error) {
 	containerGroupsClient := containerinstance.NewContainerGroupsClient(subscriptionID)
 	err := setupClient(&containerGroupsClient.Client)

+ 7 - 5
azure/backend.go

@@ -24,12 +24,8 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/Azure/go-autorest/autorest/to"
-
-	"github.com/docker/api/context/cloud"
-	"github.com/docker/api/errdefs"
-
 	"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
+	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/compose-spec/compose-go/types"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -40,7 +36,9 @@ import (
 	"github.com/docker/api/compose"
 	"github.com/docker/api/containers"
 	apicontext "github.com/docker/api/context"
+	"github.com/docker/api/context/cloud"
 	"github.com/docker/api/context/store"
+	"github.com/docker/api/errdefs"
 )
 
 const singleContainerName = "single--container--aci"
@@ -238,6 +236,10 @@ func (cs *aciContainerService) Logs(ctx context.Context, containerName string, r
 	groupName, containerAciName := getGroupAndContainerName(containerName)
 	var tail *int32
 
+	if req.Follow {
+		return streamLogs(ctx, cs.ctx, groupName, containerAciName, req.Writer)
+	}
+
 	if req.Tail != "all" {
 		reqTail, err := strconv.Atoi(req.Tail)
 		if err != nil {