// Publisher returns a LogPublisher for the given subscription // as well as a cancel function that should be called when the log stream // is completed. func (a *Agent) Publisher(ctx context.Context, subscriptionID string) (exec.LogPublisher, func(), error) { // TODO(stevvooe): The level of coordination here is WAY too much for logs. // These should only be best effort and really just buffer until a session is // ready. Ideally, they would use a separate connection completely. var ( err error publisher api.LogBroker_PublishLogsClient ) err = a.withSession(ctx, func(session *session) error { publisher, err = api.NewLogBrokerClient(session.conn.ClientConn).PublishLogs(ctx) return err }) if err != nil { return nil, nil, err } return exec.LogPublisherFunc(func(ctx context.Context, message api.LogMessage) error { select { case <-ctx.Done(): publisher.CloseSend() return ctx.Err() default: } return publisher.Send(&api.PublishLogsMessage{ SubscriptionID: subscriptionID, Messages: []api.LogMessage{message}, }) }), func() { publisher.CloseSend() }, nil }
func (tpp *testPublisherProvider) Publisher(ctx context.Context, subscriptionID string) (exec.LogPublisher, func(), error) { return exec.LogPublisherFunc(func(ctx context.Context, message api.LogMessage) error { log.G(ctx).WithFields(logrus.Fields{ "subscription": subscriptionID, "task.id": message.Context.TaskID, "node.id": message.Context.NodeID, "service.id": message.Context.ServiceID, }).Info(message.Data) return nil }), func() { }, nil }
// TestControllerFlowIntegration simply runs the Controller flow against a docker // instance to make sure we don't blow up. // // This is great for ad-hoc testing while doing development. We can add more // verification but it solves the problem of not being able to run tasks // without a swarm setup. // // Run with something like this: // // go test -run TestControllerFlowIntegration -test.docker.addr unix:///var/run/docker.sock // func TestControllerFlowIntegration(t *testing.T) { if dockerTestAddr == "" { t.Skip("specify docker address to run integration") } ctx := context.Background() client, err := engineapi.NewClient(dockerTestAddr, "", nil, nil) assert.NoError(t, err) assert.NotNil(t, client) task := &api.Task{ ID: "dockerexec-integration-task-id", ServiceID: "dockerexec-integration-service-id", NodeID: "dockerexec-integration-node-id", ServiceAnnotations: api.Annotations{ Name: "dockerexec-integration", }, Spec: api.TaskSpec{ Runtime: &api.TaskSpec_Container{ Container: &api.ContainerSpec{ Command: []string{"sh", "-c", "sleep 5; echo hello; echo stderr >&2"}, Image: "alpine", }, }, }, } var receivedLogs bool publisher := exec.LogPublisherFunc(func(ctx context.Context, message api.LogMessage) error { receivedLogs = true switch message.Stream { case api.LogStreamStdout: assert.Equal(t, "hello\n", string(message.Data)) case api.LogStreamStderr: assert.Equal(t, "stderr\n", string(message.Data)) } t.Log(message) return nil }) ctlr, err := newController(client, task, nil) assert.NoError(t, err) assert.NotNil(t, ctlr) assert.NoError(t, ctlr.Prepare(ctx)) assert.NoError(t, ctlr.Start(ctx)) assert.NoError(t, ctlr.(exec.ControllerLogs).Logs(ctx, publisher, api.LogSubscriptionOptions{ Follow: true, })) assert.NoError(t, ctlr.Wait(ctx)) assert.True(t, receivedLogs) assert.NoError(t, ctlr.Shutdown(ctx)) assert.NoError(t, ctlr.Remove(ctx)) assert.NoError(t, ctlr.Close()) // NOTE(stevvooe): testify has no clue how to correctly do error equality. if err := ctlr.Close(); err != exec.ErrControllerClosed { t.Fatalf("expected controller to be closed: %v", err) } }