Skip to content

Commit

Permalink
feat: add hidden verbose flag to deploy (#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
raulb authored Oct 28, 2022
1 parent 8512643 commit 815ffff
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 51 deletions.
66 changes: 46 additions & 20 deletions cmd/meroxa/root/apps/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Deploy struct {
DockerHubAccessToken string `long:"docker-hub-access-token" usage:"DockerHub access token to use to build and deploy the app" hidden:"true"` //nolint:lll
Spec string `long:"spec" usage:"Deployment specification version to use to build and deploy the app" hidden:"true"`
SkipCollectionValidation bool `long:"skip-collection-validation" usage:"Skips unique destination collection and looping validations"` //nolint:lll
Verbose bool `long:"verbose" usage:"Prints more logging messages" hidden:"true"`
}

client deployApplicationClient
Expand Down Expand Up @@ -195,7 +196,7 @@ func (d *Deploy) Logger(logger log.Logger) {
}

func (d *Deploy) getPlatformImage(ctx context.Context) (string, error) {
d.logger.StartSpinner("\t", " Fetching Meroxa Platform source...")
d.logger.StartSpinner("\t", "Fetching Meroxa Platform source...")

s, err := d.client.CreateSource(ctx)
if err != nil {
Expand All @@ -218,7 +219,7 @@ func (d *Deploy) getPlatformImage(ctx context.Context) (string, error) {
d.logger.StopSpinnerWithStatus("\t", log.Failed)
return "", err
}
d.logger.StartSpinner("\t", fmt.Sprintf(" Building Meroxa Process image (%q)...", build.Uuid))
d.logger.StartSpinner("\t", fmt.Sprintf("Building Meroxa Process image (%q)...", build.Uuid))

for {
b, err := d.client.GetBuild(ctx, build.Uuid)
Expand Down Expand Up @@ -276,7 +277,7 @@ func (d *Deploy) buildApp(ctx context.Context) (err error) {
// getAppImage will check what type of build needs to perform and ultimately will return the image name to use
// when deploying.
func (d *Deploy) getAppImage(ctx context.Context) (string, error) {
d.logger.StartSpinner("\t", " Checking if application has processes...")
d.logger.StartSpinner("\t", "Checking if application has processes...")
var fqImageName string

needsToBuild, err := d.turbineCLI.NeedsToBuild(ctx, d.appName)
Expand All @@ -287,7 +288,7 @@ func (d *Deploy) getAppImage(ctx context.Context) (string, error) {

// If no need to build, return empty imageName which won't be utilized by the deployment process anyway.
if !needsToBuild {
d.logger.StopSpinnerWithStatus("No need to create process image...\n", log.Successful)
d.logger.StopSpinnerWithStatus("No need to create process image\n", log.Successful)
return "", nil
}

Expand Down Expand Up @@ -396,7 +397,7 @@ func (d *Deploy) getResourceCheckErrorMessage(ctx context.Context, resources []t
}

func (d *Deploy) checkResourceAvailability(ctx context.Context) error {
resourceCheckMessage := fmt.Sprintf(" Checking resource availability for application %q (%s) before deployment...", d.appName, d.lang)
resourceCheckMessage := fmt.Sprintf("Checking resource availability for application %q (%s) before deployment...", d.appName, d.lang)

d.logger.StartSpinner("\t", resourceCheckMessage)

Expand Down Expand Up @@ -578,12 +579,12 @@ func (d *Deploy) prepareAppName(ctx context.Context) string {
func (d *Deploy) waitForDeployment(ctx context.Context, depUUID string) error {
cctx, cancel := context.WithTimeout(ctx, minutesToWaitForDeployment*time.Minute)
defer cancel()

checkLogsMsg := "Check `meroxa apps logs` for further information"

t := time.NewTicker(intervalCheckForDeployment)
defer t.Stop()

prevLine := ""

for {
select {
case <-t.C:
Expand All @@ -592,15 +593,26 @@ func (d *Deploy) waitForDeployment(ctx context.Context, depUUID string) error {
if err != nil {
return fmt.Errorf("couldn't fetch deployment status: %s", err.Error())
}

logs := strings.Split(deployment.Status.Details, "\n")

if d.flags.Verbose {
l := len(logs)
if l > 0 && logs[l-1] != prevLine {
prevLine = logs[l-1]
d.logger.Info(ctx, "\t"+logs[l-1])
}
}

switch {
case deployment.Status.State == meroxa.DeploymentStateDeployed:
return nil
case deployment.Status.State == meroxa.DeploymentStateDeployingError:
d.logger.Errorf(ctx, "\t %s Failed to deploy Application %q\n", d.logger.FailedMark(), d.appName)
for _, l := range logs {
d.logger.Errorf(ctx, "\t\t > %s", l)
if !d.flags.Verbose {
d.logger.Error(ctx, "\n")
for _, l := range logs {
d.logger.Errorf(ctx, "\t%s", l)
}
}
return fmt.Errorf("\n %s", checkLogsMsg)
}
Expand Down Expand Up @@ -686,25 +698,39 @@ func (d *Deploy) Execute(ctx context.Context) error {
return err
}

d.logger.StartSpinner("", fmt.Sprintf(" Deploying application %q...", d.appName))

var deployment *meroxa.Deployment
if deployment, err = d.deployApp(ctx, d.fnName, gitSha, d.specVersion); err != nil {
d.logger.StopSpinnerWithStatus("Couldn't complete the deployment", log.Failed)
return err
}

if err := d.waitForDeployment(ctx, deployment.UUID); err != nil {
d.logger.StopSpinnerWithStatus("Couldn't complete the deployment", log.Failed)
return err
deployMsg := fmt.Sprintf("Deploying application %q...", d.appName)
// In verbose mode, we'll use spinners for each deployment step
if d.flags.Verbose {
d.logger.Info(ctx, deployMsg+"\n")
} else {
d.logger.StartSpinner("", deployMsg)
}

d.logger.StopSpinnerWithStatus("Deploying completed!", log.Successful)
err = d.waitForDeployment(ctx, deployment.UUID)
if err != nil {
deploymentErroredMsg := "Couldn't complete the deployment"
if !d.flags.Verbose {
d.logger.StopSpinnerWithStatus(deploymentErroredMsg, log.Failed)
} else {
d.logger.Error(ctx, fmt.Sprintf("\t%s %s", d.logger.FailedMark(), deploymentErroredMsg))
}
return err
}

dashboardURL := fmt.Sprintf("https://dashboard.meroxa.io/apps/%s/detail", d.appName)
output := fmt.Sprintf("\t%s Application %q successfully deployed!\n\n ✨ To visualize your application visit %s",
d.logger.SuccessfulCheck(), d.appName, dashboardURL)
d.logger.Info(ctx, output)
output := fmt.Sprintf("Application %q successfully deployed!\n\n ✨ To visualize your application visit %s",
d.appName, dashboardURL)

if d.flags.Verbose {
d.logger.Info(ctx, fmt.Sprintf("\n\t%s %s", d.logger.SuccessfulCheck(), output))
} else {
d.logger.StopSpinnerWithStatus(output, log.Successful)
}

d.logger.JSON(ctx, app)
return nil
Expand Down
105 changes: 93 additions & 12 deletions cmd/meroxa/root/apps/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func TestDeployAppFlags(t *testing.T) {
{name: "docker-hub-access-token", required: false, hidden: true},
{name: "spec", required: false, hidden: true},
{name: "skip-collection-validation", required: false, hidden: false},
{name: "verbose", required: false, hidden: true},
}

c := builder.BuildCobraCommand(&Deploy{})
Expand Down Expand Up @@ -867,11 +868,11 @@ func TestWaitForDeployment(t *testing.T) {
name string
meroxaClient func() meroxa.Client
wantOutput func() string
noLogs bool
verboseFlag bool
err error
}{
{
name: "Deployment finishes successfully immediately",
name: "Deployment finishes successfully immediately (no verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

Expand All @@ -889,7 +890,26 @@ func TestWaitForDeployment(t *testing.T) {
err: nil,
},
{
name: "Deployment finishes successfully over time",
name: "Deployment finishes successfully immediately (with verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{
Status: meroxa.DeploymentStatus{
State: meroxa.DeploymentStateDeployed,
Details: strings.Join(outputLogs, "\n"),
},
}, nil)
return client
},
wantOutput: func() string { return "\tnailed it\n" },
verboseFlag: true,
err: nil,
},
{
name: "Deployment finishes successfully over time (no verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

Expand Down Expand Up @@ -924,7 +944,49 @@ func TestWaitForDeployment(t *testing.T) {
wantOutput: func() string { return "" },
},
{
name: "Deployment immediately failed",
name: "Deployment finishes successfully over time (with verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

first := client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{
Status: meroxa.DeploymentStatus{
State: meroxa.DeploymentStateDeploying,
Details: strings.Join(outputLogs[:1], "\n"),
},
}, nil)
second := client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{
Status: meroxa.DeploymentStatus{
State: meroxa.DeploymentStateDeploying,
Details: strings.Join(outputLogs[:2], "\n"),
},
}, nil)
third := client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{
Status: meroxa.DeploymentStatus{
State: meroxa.DeploymentStateDeployed,
Details: strings.Join(outputLogs, "\n"),
},
}, nil).AnyTimes()
gomock.InOrder(first, second, third)
return client
},
err: nil,
wantOutput: func() string {
errorMsg := ""
for _, l := range outputLogs {
errorMsg = fmt.Sprintf("%s\t%s\n", errorMsg, l)
}
return errorMsg
},
verboseFlag: true,
},
{
name: "Deployment immediately failed (no verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

Expand All @@ -938,31 +1000,48 @@ func TestWaitForDeployment(t *testing.T) {
}, nil)
return client
},
noLogs: false,
wantOutput: func() string {
errorMsg := fmt.Sprintf("\t x Failed to deploy Application %q\n", appName)
errorMsg := "\n"
for _, l := range outputLogs {
errorMsg = fmt.Sprintf("%s\t\t > %s\n", errorMsg, l)
errorMsg = fmt.Sprintf("%s\t%s\n", errorMsg, l)
}
return errorMsg
},
err: fmt.Errorf("\n Check `meroxa apps logs` for further information"),
},
{
name: "Failed to get latest deployment",
name: "Deployment immediately failed (with verbose flag)",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{}, fmt.Errorf("not today"))
Return(&meroxa.Deployment{
Status: meroxa.DeploymentStatus{
State: meroxa.DeploymentStateDeployingError,
Details: strings.Join(outputLogs, "\n"),
},
}, nil)
return client
},
noLogs: true,
wantOutput: func() string {
return ""
return "\tnailed it\n"
},
verboseFlag: true,
err: fmt.Errorf("\n Check `meroxa apps logs` for further information"),
},
{
name: "Failed to get latest deployment",
meroxaClient: func() meroxa.Client {
client := mock.NewMockClient(ctrl)

client.EXPECT().
GetDeployment(ctx, appName, uuid).
Return(&meroxa.Deployment{}, fmt.Errorf("not today"))
return client
},
err: errors.New("couldn't fetch deployment status: not today"),
wantOutput: func() string { return "" },
err: errors.New("couldn't fetch deployment status: not today"),
},
}

Expand All @@ -975,6 +1054,8 @@ func TestWaitForDeployment(t *testing.T) {
appName: appName,
}

d.flags.Verbose = tc.verboseFlag

err := d.waitForDeployment(ctx, uuid)
require.Equal(t, tc.err, err, "errors are not equal")

Expand Down
4 changes: 2 additions & 2 deletions cmd/meroxa/root/apps/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (u *Upgrade) Execute(ctx context.Context) error {
var err error
if u.config == nil {
u.path, err = turbine.GetPath(u.flags.Path)
u.logger.StartSpinner("\t", fmt.Sprintf(" Fetching details of application in %q...", u.path))
u.logger.StartSpinner("\t", fmt.Sprintf("Fetching details of application in %q...", u.path))
if err != nil {
u.logger.StopSpinnerWithStatus("\t", log.Failed)
return err
Expand Down Expand Up @@ -114,7 +114,7 @@ func (u *Upgrade) Execute(ctx context.Context) error {
return err
}

u.logger.StartSpinner("\t", " Testing upgrades locally...")
u.logger.StartSpinner("\t", "Testing upgrades locally...")
runOutput := ""
buf := bytes.NewBufferString(runOutput)
if u.run == nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/meroxa/turbine/golang/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func (t *turbineGoCLI) Build(ctx context.Context, appName string, platform bool) (string, error) {
var cmd *exec.Cmd

t.logger.StartSpinner("\t", " Building Golang binary...")
t.logger.StartSpinner("\t", "Building Golang binary...")
if platform {
cmd = exec.CommandContext(ctx, "go", "build", "--tags", "platform", "-o", appName, "./...")
} else {
Expand Down
2 changes: 1 addition & 1 deletion cmd/meroxa/turbine/golang/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (t *turbineGoCLI) GitInit(ctx context.Context, name string) error {
}

func GoInit(l log.Logger, appPath string, skipInit, vendor bool) error {
l.StartSpinner("\t", " Running golang module initializing...")
l.StartSpinner("\t", "Running golang module initializing...")
skipLog := "skipping go module initialization\n\tFor guidance, visit " +
"https://docs.meroxa.com/beta-overview#go-mod-init-for-a-new-golang-turbine-data-application"
goPath := os.Getenv("GOPATH")
Expand Down
2 changes: 1 addition & 1 deletion cmd/meroxa/turbine/golang/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (t *turbineGoCLI) Upgrade(vendor bool) error {

func (t *turbineGoCLI) tidy(vendor bool) error {
var err error
t.logger.StartSpinner("\t", " Tidying up Golang modules...")
t.logger.StartSpinner("\t", "Tidying up Golang modules...")
if vendor {
_, err = os.Stat("vendor")
if !os.IsNotExist(err) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/meroxa/turbine/golang/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// GoGetDeps updates the latest Meroxa mods.
func GoGetDeps(l log.Logger) error {
l.StartSpinner("\t", " Getting latest turbine-go and turbine-go/running dependencies...")
l.StartSpinner("\t", "Getting latest turbine-go and turbine-go/running dependencies...")
cmd := exec.Command("go", "get", "-u", "github.com/meroxa/turbine-go")
output, err := cmd.CombinedOutput()
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/meroxa/turbine/javascript/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func (t *turbineJsCLI) Upgrade(vendor bool) error {
cmd.Dir = t.appPath
err := cmd.Wait()
if err != nil {
t.logger.StartSpinner("\t", " Adding @meroxa/turbine-js-framework requirement...")
t.logger.StartSpinner("\t", "Adding @meroxa/turbine-js-framework requirement...")
cmd = exec.Command("npm", "install", "@meroxa/turbine-js-framework", "--save")
cmd.Dir = t.appPath
out, err := cmd.CombinedOutput()
Expand All @@ -38,7 +38,7 @@ func (t *turbineJsCLI) Upgrade(vendor bool) error {
}
t.logger.StopSpinnerWithStatus("Added @meroxa/turbine-js-framework requirement successfully!", log.Successful)
} else {
t.logger.StartSpinner("\t", " Upgrading @meroxa/turbine-js-framework...")
t.logger.StartSpinner("\t", "Upgrading @meroxa/turbine-js-framework...")
cmd = exec.Command("npm", "upgrade", "@meroxa/turbine-js-framework")
cmd.Dir = t.appPath
out, err := cmd.CombinedOutput()
Expand Down
2 changes: 1 addition & 1 deletion cmd/meroxa/turbine/python/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

// Build created the needed structure for a python app.
func (t *turbinePyCLI) Build(ctx context.Context, appName string, platform bool) (string, error) {
t.logger.StartSpinner("\t", " Building application...")
t.logger.StartSpinner("\t", "Building application...")
cmd := exec.CommandContext(ctx, "turbine-py", "clibuild", t.appPath)
output, err := cmd.CombinedOutput()
if err != nil {
Expand Down
Loading

0 comments on commit 815ffff

Please sign in to comment.