Skip to content

Commit

Permalink
Add ability to tail logs during app start.
Browse files Browse the repository at this point in the history
  • Loading branch information
aeijdenberg committed Nov 1, 2017
1 parent 71a1359 commit 1f38d36
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 23 deletions.
95 changes: 79 additions & 16 deletions autopilot.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package main

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"log"
"net/url"
"os"
"strings"
"time"

"code.cloudfoundry.org/cli/cf/api/logs"
"code.cloudfoundry.org/cli/plugin"
"github.com/cloudfoundry/noaa/consumer"
"github.com/contraband/autopilot/rewind"
)

Expand All @@ -30,7 +35,7 @@ func venerableAppName(appName string) string {
return fmt.Sprintf("%s-venerable", appName)
}

func getActionsForExistingApp(appRepo *ApplicationRepo, appName, manifestPath, appPath string) []rewind.Action {
func getActionsForExistingApp(appRepo *ApplicationRepo, appName, manifestPath, appPath string, showLogs bool) []rewind.Action {
return []rewind.Action{
// rename
{
Expand All @@ -41,7 +46,7 @@ func getActionsForExistingApp(appRepo *ApplicationRepo, appName, manifestPath, a
// push
{
Forward: func() error {
return appRepo.PushApplication(appName, manifestPath, appPath)
return appRepo.PushApplication(appName, manifestPath, appPath, showLogs)
},
ReversePrevious: func() error {
// If the app cannot start we'll have a lingering application
Expand All @@ -60,20 +65,25 @@ func getActionsForExistingApp(appRepo *ApplicationRepo, appName, manifestPath, a
}
}

func getActionsForNewApp(appRepo *ApplicationRepo, appName, manifestPath, appPath string) []rewind.Action {
func getActionsForNewApp(appRepo *ApplicationRepo, appName, manifestPath, appPath string, showLogs bool) []rewind.Action {
return []rewind.Action{
// push
{
Forward: func() error {
return appRepo.PushApplication(appName, manifestPath, appPath)
return appRepo.PushApplication(appName, manifestPath, appPath, showLogs)
},
},
}
}

func (plugin AutopilotPlugin) Run(cliConnection plugin.CliConnection, args []string) {
// only handle if actually invoked, else it can't be uninstalled cleanly
if args[0] != "zero-downtime-push" {
return
}

appRepo := NewApplicationRepo(cliConnection)
appName, manifestPath, appPath, err := ParseArgs(args)
appName, manifestPath, appPath, showLogs, err := ParseArgs(args)
fatalIf(err)

appExists, err := appRepo.DoesAppExist(appName)
Expand All @@ -82,9 +92,9 @@ func (plugin AutopilotPlugin) Run(cliConnection plugin.CliConnection, args []str
var actionList []rewind.Action

if appExists {
actionList = getActionsForExistingApp(appRepo, appName, manifestPath, appPath)
actionList = getActionsForExistingApp(appRepo, appName, manifestPath, appPath, showLogs)
} else {
actionList = getActionsForNewApp(appRepo, appName, manifestPath, appPath)
actionList = getActionsForNewApp(appRepo, appName, manifestPath, appPath, showLogs)
}

actions := rewind.Actions{
Expand All @@ -108,7 +118,7 @@ func (AutopilotPlugin) GetMetadata() plugin.PluginMetadata {
Version: plugin.VersionType{
Major: 0,
Minor: 0,
Build: 2,
Build: 3,
},
Commands: []plugin.Command{
{
Expand All @@ -122,26 +132,33 @@ func (AutopilotPlugin) GetMetadata() plugin.PluginMetadata {
}
}

func ParseArgs(args []string) (string, string, string, error) {
func ParseArgs(args []string) (string, string, string, bool, error) {
flags := flag.NewFlagSet("zero-downtime-push", flag.ContinueOnError)
manifestPath := flags.String("f", "", "path to an application manifest")
appPath := flags.String("p", "", "path to application files")
showLogs := flags.Bool("show-app-log", false, "tail and show application log during application start")

if len(args) < 2 {
return "", "", "", false, ErrNoArgs
}
err := flags.Parse(args[2:])
if err != nil {
return "", "", "", err
return "", "", "", false, err
}

appName := args[1]

if *manifestPath == "" {
return "", "", "", ErrNoManifest
return "", "", "", false, ErrNoManifest
}

return appName, *manifestPath, *appPath, nil
return appName, *manifestPath, *appPath, *showLogs, nil
}

var ErrNoManifest = errors.New("a manifest is required to push this application")
var (
ErrNoArgs = errors.New("app name must be specified")
ErrNoManifest = errors.New("a manifest is required to push this application")
)

type ApplicationRepo struct {
conn plugin.CliConnection
Expand All @@ -158,15 +175,61 @@ func (repo *ApplicationRepo) RenameApplication(oldName, newName string) error {
return err
}

func (repo *ApplicationRepo) PushApplication(appName, manifestPath, appPath string) error {
args := []string{"push", appName, "-f", manifestPath}
func (repo *ApplicationRepo) PushApplication(appName, manifestPath, appPath string, showLogs bool) error {
args := []string{"push", appName, "-f", manifestPath, "--no-start"}

if appPath != "" {
args = append(args, "-p", appPath)
}

_, err := repo.conn.CliCommand(args...)
return err
if err != nil {
return err
}

if showLogs {
app, err := repo.conn.GetApp(appName)
if err != nil {
return err
}
dopplerEndpoint, err := repo.conn.DopplerEndpoint()
if err != nil {
return err
}
token, err := repo.conn.AccessToken()
if err != nil {
return err
}

cons := consumer.New(dopplerEndpoint, nil, nil)
defer cons.Close()

messages, errors := cons.TailingLogs(app.Guid, token)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
for {
select {
case m := <-messages:
if m.GetSourceType() != "STG" { // skip STG messages as the cf tool already prints them
os.Stderr.WriteString(logs.NewNoaaLogMessage(m).ToLog(time.Local) + "\n")
}
case e := <-errors:
log.Println("error reading logs:", e)
case <-ctx.Done():
return
}
}
}()
}

_, err = repo.conn.CliCommand("start", appName)
if err != nil {
return err
}

return nil
}

func (repo *ApplicationRepo) DeleteApplication(appName string) error {
Expand Down
17 changes: 10 additions & 7 deletions autopilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestAutopilot(t *testing.T) {

var _ = Describe("Flag Parsing", func() {
It("parses a complete set of args", func() {
appName, manifestPath, appPath, err := ParseArgs(
appName, manifestPath, appPath, showLogs, err := ParseArgs(
[]string{
"zero-downtime-push",
"appname",
Expand All @@ -34,10 +34,11 @@ var _ = Describe("Flag Parsing", func() {
Expect(appName).To(Equal("appname"))
Expect(manifestPath).To(Equal("manifest-path"))
Expect(appPath).To(Equal("app-path"))
Expect(showLogs).To(Equal(false))
})

It("requires a manifest", func() {
_, _, _, err := ParseArgs(
_, _, _, _, err := ParseArgs(
[]string{
"zero-downtime-push",
"appname",
Expand Down Expand Up @@ -187,36 +188,38 @@ var _ = Describe("ApplicationRepo", func() {

Describe("PushApplication", func() {
It("pushes an application with both a manifest and a path", func() {
err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "/path/to/the/app")
err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "/path/to/the/app", false)
Expect(err).ToNot(HaveOccurred())

Expect(cliConn.CliCommandCallCount()).To(Equal(1))
Expect(cliConn.CliCommandCallCount()).To(Equal(2))
args := cliConn.CliCommandArgsForCall(0)
Expect(args).To(Equal([]string{
"push",
"appName",
"-f", "/path/to/a/manifest.yml",
"--no-start",
"-p", "/path/to/the/app",
}))
})

It("pushes an application with only a manifest", func() {
err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "")
err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "", false)
Expect(err).ToNot(HaveOccurred())

Expect(cliConn.CliCommandCallCount()).To(Equal(1))
Expect(cliConn.CliCommandCallCount()).To(Equal(2))
args := cliConn.CliCommandArgsForCall(0)
Expect(args).To(Equal([]string{
"push",
"appName",
"-f", "/path/to/a/manifest.yml",
"--no-start",
}))
})

It("returns errors from the push", func() {
cliConn.CliCommandReturns([]string{}, errors.New("bad app"))

err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "/path/to/the/app")
err := repo.PushApplication("appName", "/path/to/a/manifest.yml", "/path/to/the/app", false)
Expect(err).To(MatchError("bad app"))
})
})
Expand Down

0 comments on commit 1f38d36

Please sign in to comment.