From e1a003f1d1ce77e68bdca649183dae78e0eb7d42 Mon Sep 17 00:00:00 2001 From: HappyTobi Date: Tue, 18 Jun 2019 07:13:51 +0200 Subject: [PATCH] [UPDATE] Readme and puppeteer main class (remove / move) code --- CHANGELOG.md | 35 +++---- puppeteer.go | 249 ++++++++++++---------------------------------- puppeteer_test.go | 168 ------------------------------- 3 files changed, 73 insertions(+), 379 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f04094f..ec2b19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/puppeteer.go b/puppeteer.go index 5e3d0c2..a9b6a9c 100644 --- a/puppeteer.go +++ b/puppeteer.go @@ -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) { @@ -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 @@ -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 } @@ -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 { @@ -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{ @@ -216,21 +202,19 @@ func (CfPuppeteerPlugin) GetMetadata() plugin.PluginMetadata { UsageDetails: plugin.Usage{ Usage: "$ cf zero-downtime-push [] -f [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", }, }, }, @@ -238,156 +222,6 @@ func (CfPuppeteerPlugin) GetMetadata() plugin.PluginMetadata { } } -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 @@ -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 +} diff --git a/puppeteer_test.go b/puppeteer_test.go index 2221cf6..96cbcf1 100644 --- a/puppeteer_test.go +++ b/puppeteer_test.go @@ -15,174 +15,6 @@ func TestPuppeteer(t *testing.T) { RunSpecs(t, "Puppeteer Suite") } -var _ = Describe("Flag Parsing", func() { - var ( - cliConn *pluginfakes.FakeCliConnection - repo *ApplicationRepo - ) - - BeforeEach(func() { - cliConn = &pluginfakes.FakeCliConnection{} - repo = NewApplicationRepo(cliConn, false) - }) - - It("parses args without appName", func() { - parsedArguments, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "-f", "./fixtures/manifest.yml", - "-p", "app-path", - "-s", "stack-name", - "-t", "120", - "-env", "foo=bar", - "-env", "baz=bob=true", - "--vendor-option", "stop", - "--invocation-timeout", "2211", - "--process", "process-name", - }, - ) - Expect(err).ToNot(HaveOccurred()) - - Expect(parsedArguments.AppName).Should(Equal("myApp")) - Expect(parsedArguments.ManifestPath).To(Equal("./fixtures/manifest.yml")) - Expect(parsedArguments.AppPath).To(Equal("app-path")) - Expect(parsedArguments.StackName).To(Equal("stack-name")) - Expect(parsedArguments.Envs).To(Equal([]string{"foo=bar", "baz=bob=true"})) - Expect(parsedArguments.VendorAppOption).Should(Equal("stop")) - Expect(parsedArguments.ShowLogs).To(Equal(false)) - Expect(parsedArguments.ShowCrashLogs).To(Equal(false)) - Expect(parsedArguments.Timeout).To(Equal(120)) - Expect(parsedArguments.InvocationTimeout).To(Equal(2211)) - Expect(parsedArguments.Process).To(Equal("process-name")) - Expect(parsedArguments.HealthCheckType).To(Equal("http")) - Expect(parsedArguments.HealthCheckHTTPEndpoint).To(Equal("/health")) - }) - - It("parses a all args without timeout", func() { - parsedArguments, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "appname", - "-f", "./fixtures/manifest.yml", - "-p", "app-path", - "-s", "stack-name", - "-env", "foo=bar", - "-env", "baz=bob", - "--vendor-option", "stop", - }, - ) - Expect(err).ToNot(HaveOccurred()) - - Expect(parsedArguments.AppName).To(Equal("appname")) - Expect(parsedArguments.ManifestPath).To(Equal("./fixtures/manifest.yml")) - Expect(parsedArguments.AppPath).To(Equal("app-path")) - Expect(parsedArguments.StackName).To(Equal("stack-name")) - Expect(parsedArguments.Envs).To(Equal([]string{"foo=bar", "baz=bob"})) - Expect(parsedArguments.VendorAppOption).Should(Equal("stop")) - Expect(parsedArguments.ShowLogs).To(Equal(false)) - Expect(parsedArguments.ShowCrashLogs).To(Equal(false)) - Expect(parsedArguments.Timeout).To(Equal(2)) - Expect(parsedArguments.InvocationTimeout).To(Equal(-1)) - Expect(parsedArguments.Process).To(Equal("")) - Expect(parsedArguments.HealthCheckType).To(Equal("http")) - Expect(parsedArguments.HealthCheckHTTPEndpoint).To(Equal("/health")) - }) - - It("parses a all args without timeout and no manifest timeout", func() { - parsedArguments, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "appname", - "-f", "./fixtures/multiManifest.yml", - "-p", "app-path", - "-s", "stack-name", - "-env", "foo=bar", - "-env", "baz=bob", - "--vendor-option", "stop", - "--show-crash-log", - }, - ) - Expect(err).ToNot(HaveOccurred()) - - Expect(parsedArguments.AppName).To(Equal("appname")) - Expect(parsedArguments.ManifestPath).To(Equal("./fixtures/multiManifest.yml")) - Expect(parsedArguments.AppPath).To(Equal("app-path")) - Expect(parsedArguments.StackName).To(Equal("stack-name")) - Expect(parsedArguments.Envs).To(Equal([]string{"foo=bar", "baz=bob"})) - Expect(parsedArguments.VendorAppOption).Should(Equal("stop")) - Expect(parsedArguments.ShowLogs).To(Equal(false)) - Expect(parsedArguments.ShowCrashLogs).To(Equal(true)) - Expect(parsedArguments.Timeout).To(Equal(60)) - Expect(parsedArguments.InvocationTimeout).To(Equal(-1)) - Expect(parsedArguments.Process).To(Equal("")) - Expect(parsedArguments.HealthCheckType).To(Equal("http")) - Expect(parsedArguments.HealthCheckHTTPEndpoint).To(Equal("/health")) - }) - - It("parses a complete set of args", func() { - parsedArguments, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "appname", - "-f", "./fixtures/manifest.yml", - "-p", "app-path", - "-s", "stack-name", - "-t", "120", - "-env", "foo=bar", - "-env", "baz=bob", - "--invocation-timeout", "2211", - "--process", "process-name", - "--health-check-type", "process", - "--health-check-http-endpoint", "/foo/bar", - "--show-app-log", - }, - ) - Expect(err).ToNot(HaveOccurred()) - - Expect(parsedArguments.AppName).To(Equal("appname")) - Expect(parsedArguments.ManifestPath).To(Equal("./fixtures/manifest.yml")) - Expect(parsedArguments.AppPath).To(Equal("app-path")) - Expect(parsedArguments.StackName).To(Equal("stack-name")) - Expect(parsedArguments.Envs).To(Equal([]string{"foo=bar", "baz=bob"})) - Expect(parsedArguments.VendorAppOption).Should(Equal("delete")) - Expect(parsedArguments.ShowLogs).To(Equal(true)) - Expect(parsedArguments.ShowCrashLogs).To(Equal(false)) - Expect(parsedArguments.Timeout).To(Equal(120)) - Expect(parsedArguments.InvocationTimeout).To(Equal(2211)) - Expect(parsedArguments.Process).To(Equal("process-name")) - Expect(parsedArguments.HealthCheckType).To(Equal("process")) - Expect(parsedArguments.HealthCheckHTTPEndpoint).To(Equal("/foo/bar")) - }) - - It("parses args without appName and wrong envs format", func() { - _, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "-f", "./fixtures/manifest.yml", - "-p", "app-path", - "-s", "stack-name", - "-t", "120", - "-env", "foo=bar", - "-env", "baz bob", - "--invocation-timeout", "2211", - "--process", "process-name", - }, - ) - Expect(err).To(MatchError(ErrWrongEnvFormat)) - }) - - It("requires a manifest", func() { - _, err := ParseArgs( - repo, []string{ - "zero-downtime-push", - "appname", - "-p", "app-path", - }, - ) - Expect(err).To(MatchError(ErrNoManifest)) - }) -}) - var _ = Describe("ApplicationRepo", func() { var ( cliConn *pluginfakes.FakeCliConnection