Skip to content

Commit

Permalink
[UPDATE] Readme and puppeteer main class (remove / move) code
Browse files Browse the repository at this point in the history
  • Loading branch information
HappyTobi committed Jun 18, 2019
1 parent 7bf9a31 commit e1a003f
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 379 deletions.
35 changes: 12 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Upcoming features [1.0.0]
## Upcoming features [1.x.x]

- add argument to cleanup unused enviroment variables
- add argument to cleanup unused environment variables
- log deployment time
- fix all linter problems
- sorted options
- deploy without routes
- deploy routes only

## [1.0.0.rc.1] - 2019-06-13
## [1.0.0.rc.0 - rc.2 - Final] - 2019-06-13

### Added

- new argument to show crash log before old application will be deleted
- push application with v3 api

### Changed

- cleanup code (refactor the complete plugin)
- colorized logging - use default cf terminal logging with color
- remove some parameters (like var, vars-file etc.)

### Fixed

- publishing applications
- fix some liner errors

### Features


## [1.0.0.rc.0] - 2019-05-09

### Added

- push application with v3 api

### Changed

- update ParseArgs to struct instead of multi return values
- Start code splitting
- code splitting / complete refactoring

### Fixed

- manifest parser to understand new format (https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#buildpack)
- publishing applications
- fix some linter errors
- implement new manifest parser to understand new manifest format (https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#buildpack)

### Features

- add argument to stop old service only instead of deletion
- choose between v2 and v3 push (v2 = legacy option)


## [0.0.14] - 2019-03-25

Expand Down
249 changes: 61 additions & 188 deletions puppeteer.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package main

import (
"code.cloudfoundry.org/cli/cf/api/logs"
"code.cloudfoundry.org/cli/plugin"
"errors"
"flag"
"context"
"fmt"
"github.com/cloudfoundry/noaa/consumer"
"github.com/happytobi/cf-puppeteer/arguments"
"github.com/happytobi/cf-puppeteer/cf"
v2 "github.com/happytobi/cf-puppeteer/cf/v2"
"github.com/happytobi/cf-puppeteer/manifest"
"github.com/happytobi/cf-puppeteer/rewind"
"github.com/happytobi/cf-puppeteer/ui"
"log"
"os"
"regexp"
"strings"
"time"
)

func fatalIf(err error) {
Expand All @@ -32,7 +34,7 @@ func venerableAppName(appName string) string {
return fmt.Sprintf("%s-venerable", appName)
}

func getActionsForApp(appRepo *ApplicationRepo, parsedArguments *ParserArguments) []rewind.Action {
func getActionsForApp(appRepo *ApplicationRepo, parsedArguments *arguments.ParserArguments) []rewind.Action {
venName := venerableAppName(parsedArguments.AppName)
var err error
var curApp, venApp *v2.AppResourcesEntity
Expand Down Expand Up @@ -106,29 +108,8 @@ func getActionsForApp(appRepo *ApplicationRepo, parsedArguments *ParserArguments
return err
}

applicationBuildpacks := parsedArguments.Manifest.ApplicationManifests[0].Buildpacks
applicationStack := parsedArguments.Manifest.ApplicationManifests[0].Stack
appName := parsedArguments.AppName
appPath := parsedArguments.AppPath
serviceNames := parsedArguments.Manifest.ApplicationManifests[0].Services
spaceGUID := space.Guid
manifestPath := parsedArguments.ManifestPath
routes := parsedArguments.Manifest.ApplicationManifests[0].Routes
healthCheckType := parsedArguments.HealthCheckType
healthCheckHttpEndpoint := parsedArguments.HealthCheckHTTPEndpoint
process := parsedArguments.Process
invocationTimeout := parsedArguments.InvocationTimeout

//move to own function
//TODO
var mergedEnvs []string
mergedEnvs = append(mergedEnvs, parsedArguments.Envs...)
for k, v := range parsedArguments.Manifest.ApplicationManifests[0].Env {
mergedEnvs = append(mergedEnvs, fmt.Sprintf("%s=%s", k, v))
}

var puppeteerPush cf.PuppeteerPush = cf.NewApplicationPush(appRepo.conn, appRepo.traceLogging)
err = puppeteerPush.PushApplication(appName, venName, appPath, serviceNames, spaceGUID, applicationBuildpacks, applicationStack, mergedEnvs, manifestPath, routes, healthCheckType, healthCheckHttpEndpoint, process, invocationTimeout)
err = puppeteerPush.PushApplication(venName, space.Guid, parsedArguments)
if err != nil {
return err
}
Expand All @@ -138,6 +119,11 @@ func getActionsForApp(appRepo *ApplicationRepo, parsedArguments *ParserArguments
// start
{
Forward: func() error {
ui.Say("show logs...")
if parsedArguments.ShowLogs {
// TODO not working anymore
_ = appRepo.ShowLogs(parsedArguments.AppName)
}
return appRepo.StartApplication(parsedArguments.AppName)
},
ReversePrevious: func() error {
Expand Down Expand Up @@ -185,7 +171,7 @@ func (plugin CfPuppeteerPlugin) Run(cliConnection plugin.CliConnection, args []s
traceLogging = true
}
appRepo := NewApplicationRepo(cliConnection, traceLogging)
parsedArguments, err := ParseArgs(appRepo, args)
parsedArguments, err := arguments.ParseArgs(args)
fatalIf(err)

fatalIf((&rewind.Actions{
Expand Down Expand Up @@ -216,178 +202,26 @@ func (CfPuppeteerPlugin) GetMetadata() plugin.PluginMetadata {
UsageDetails: plugin.Usage{
Usage: "$ cf zero-downtime-push [<App-Name>] -f <Manifest.yml> [options]",
Options: map[string]string{
"f": "path to application manifest",
"p": "path to application files",
"s": "name of the stack to use",
"t": "push timeout (in secounds)",
"-show-app-log": "tail and show application log during application start",
"env": "add environment key value pairs dynamic; can specify multiple times",
//"var": "variable key value pair for variable substitution; can specify multiple times",
//"vars-file": "Path to a variable substitution file for manifest; can specify multiple times",
"f": "path to application manifest",
"p": "path to application files",
"s": "name of the stack to use",
"t": "push timeout (in seconds)",
"env": "add environment key value pairs dynamic; can specify multiple times",
"-vendor-option": "option to delete or stop vendor application - default is delete",
"-health-check-type": "type of health check to perform",
"-health-check-http-endpoint": "endpoint for the 'http' health check type",
"-invocation-timeout": "timeout (in seconds) that controls individual health check invocations",
"-show-crash-log": "Show recent logs when applications crashes while the deployment",
//"-process": "application process to update",
//"v": "print additional details on the deployment process",
//"-show-app-log": "tail and show application log during application start",
"-process": "application process to update",
"-legacy-push": "use legacy push instead of new v3 api",
},
},
},
},
}
}

type stringSlice []string

func (s *stringSlice) String() string {
return fmt.Sprint(*s)
}

func (s *stringSlice) Set(value string) error {
*s = append(*s, value)
return nil
}

//ParserArguments struct where all arguments will be parsed into
type ParserArguments struct {
AppName string
ManifestPath string
AppPath string
HealthCheckType string
HealthCheckHTTPEndpoint string
Timeout int
InvocationTimeout int
Process string
StackName string
VendorAppOption string
Vars []string
VarsFiles []string
Envs []string
ShowLogs bool
ShowCrashLogs bool
DockerImage string
DockerUserName string
Manifest manifest.Manifest
}

// ParseArgs parses the command line arguments
func ParseArgs(repo *ApplicationRepo, args []string) (*ParserArguments, error) {
flags := flag.NewFlagSet("zero-downtime-push", flag.ContinueOnError)

var envs stringSlice
var vars stringSlice
var varsFiles stringSlice

pta := &ParserArguments{}
flags.StringVar(&pta.ManifestPath, "f", "", "path to an application manifest")
flags.StringVar(&pta.AppPath, "p", "", "path to application files")
flags.StringVar(&pta.StackName, "s", "", "name of the stack to use")
flags.StringVar(&pta.HealthCheckType, "health-check-type", "", "type of health check to perform")
flags.StringVar(&pta.HealthCheckHTTPEndpoint, "health-check-http-endpoint", "", "endpoint for the 'http' health check type")
flags.IntVar(&pta.Timeout, "t", 0, "push timeout in seconds (defaults to 60 seconds)")
flags.IntVar(&pta.InvocationTimeout, "invocation-timeout", -1, "health check invocation timeout in seconds")
flags.StringVar(&pta.Process, "process", "", "application process to update")
flags.BoolVar(&pta.ShowLogs, "show-app-log", false, "tail and show application log during application start")
flags.StringVar(&pta.VendorAppOption, "vendor-option", "delete", "option to delete or stop vendor application - default is delete")
flags.Var(&envs, "env", "Variable key value pair for adding dynamic environment variables; can specify multiple times")
flags.BoolVar(&pta.ShowCrashLogs, "show-crash-log", false, "Show recent logs when applications crashes while the deployment")

//flags.Var(&vars, "var", "Variable key value pair for variable substitution, (e.g., name=app1); can specify multiple times")
//flags.Var(&varsFiles, "vars-file", "Path to a variable substitution file for manifest; can specify multiple times")
//flags.StringVar(&pta.DockerImage, "docker-image", "", "url to docker image")
//flags.StringVar(&pta.DockerUserName, "docker-username", "", "pass docker username if image came from private repository")
//dockerPass := os.Getenv("CF_DOCKER_PASSWORD")

//first check if argument was passed
if len(args) < 2 {
return pta, ErrNoArgument
}

//default start index of parameters is 2 because 1 is the appName
argumentStartIndex := 2
//if first argument is not the appName we have to read the appName out of the manifest
noAppNameProvided, _ := regexp.MatchString("^-[a-z]{0,3}", args[1])
//noAppNameProvided := strings.Contains(args[1], "-")
if noAppNameProvided {
argumentStartIndex = 1
}

err := flags.Parse(args[argumentStartIndex:])
if err != nil {
return pta, err
}

if pta.ManifestPath == "" {
return pta, ErrNoManifest
}

//parse manifest
parsedManifest, err := manifest.Parse(pta.ManifestPath)
if err != nil {
return pta, err //ErrManifest
}
pta.Manifest = parsedManifest

/*if *dockerImage != "" && *dockerUserName != "" && dockerPass != "" {
//TODO use dockerImage stuff and pass to push command
}*/

//set timeout
manifestTimeout := parsedManifest.ApplicationManifests[0].Timeout
if manifestTimeout > 0 && pta.Timeout <= 0 {
pta.Timeout = manifestTimeout
} else if manifestTimeout <= 0 && pta.Timeout <= 0 {
pta.Timeout = 60
}

//parse first argument as appName
pta.AppName = args[1]
if noAppNameProvided {
pta.AppName = parsedManifest.ApplicationManifests[0].Name
}

// get health check settings from manifest if nothing else was specified in the command line
if pta.HealthCheckType == "" {
if parsedManifest.ApplicationManifests[0].HealthCheckType == "" {
pta.HealthCheckType = "port"
} else {
pta.HealthCheckType = parsedManifest.ApplicationManifests[0].HealthCheckType
}

}
if pta.HealthCheckHTTPEndpoint == "" {
pta.HealthCheckHTTPEndpoint = parsedManifest.ApplicationManifests[0].HealthCheckHTTPEndpoint
}

//validate envs format
if len(envs) > 0 {
for _, envPair := range envs {
if strings.Contains(envPair, "=") == false {
return pta, ErrWrongEnvFormat
}
}
}

pta.Envs = envs
pta.Vars = vars
pta.VarsFiles = varsFiles

return pta, nil
}

//all custom errors
var (
//ErrNoArgument error when zero-downtime-push without a argument called
ErrNoArgument = errors.New("no valid argument found, use --help / -h for more information")
//ErrNoManifest error when manifes on push application was not found
ErrNoManifest = errors.New("a manifest is required to push this application")
//ErrManifest error when manifes could not be parsed
ErrManifest = errors.New("could not parse manifest")
//ErrWrongEnvFormat error when env files was not in right format
ErrWrongEnvFormat = errors.New("--var would be in wrong format, use the vars like key=value")
)

type ApplicationRepo struct {
conn plugin.CliConnection
traceLogging bool
Expand Down Expand Up @@ -431,3 +265,42 @@ func (repo *ApplicationRepo) ListApplications() error {
_, err := repo.conn.CliCommand("apps")
return err
}

func (repo *ApplicationRepo) ShowLogs(appName string) error {
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, chanError := 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 := <-chanError:
log.Println("error reading logs:", e)
case <-ctx.Done():
return
}
}
}()
return nil
}
Loading

0 comments on commit e1a003f

Please sign in to comment.