Skip to content
This repository has been archived by the owner on Jan 20, 2025. It is now read-only.

Commit

Permalink
Merge pull request #18 from HappyTobi/1.2.0
Browse files Browse the repository at this point in the history
1.2.0
  • Loading branch information
HappyTobi authored Feb 24, 2020
2 parents a943548 + ccb86d2 commit 1c85ade
Show file tree
Hide file tree
Showing 22 changed files with 421 additions and 638 deletions.
23 changes: 19 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Upcoming features [1.x.x]

- add argument to cleanup unused environment variables
- log deployment time
- refactor push - add rewind to all push things
- add parallel push to multi manifest
- multi manifest pushed in parallel

- multi manifest works correct.
- check space quota before deployment (if user pass the option)
- set timeout how long the deployment will wait for more / free space

## [1.2.0] - 2020-02-24

### Added
- --vars-file argument was available again

### Changed
- move all v2 operations into the v2 package
- delete cloud controller version check
- all routes will be switched lazy now (after starting up the new application)
- dropped CF_PUPPETEER_TRACE and replace them with CF_TRACE (read the README "Known issues section" for using it)
- clean up code
- move temp manifest file generator to the manifest file

### Fixed
- error while passing environment variables on legacy push (--env)

## [1.1.3] - 2019-11-26

Expand All @@ -34,7 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- documentation update
- back port "--no-route" and "--route-only" option to "--legacy-push" (v2).
- docker support was available again
- vendor-option droped
- vendor-option dropped
- options was sorted right now (go 1.12)

### Fixed
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

*cf plugin for hands-off, zero downtime application deploys*



## Documentation / Website
[CF-Puppeteer Website](https://cf-puppeteer.happytobi.com).

Expand All @@ -23,7 +25,7 @@ To get an overview of the changes between versions, read the [changelog](CHANGEL

## Version

The latest version of *CF-Puppeteer* is *1.1.2*. It works with and is based on Cloud Foundry CLI version 6.43.0.
The latest version of *CF-Puppeteer* is *1.2.0*. It works with and is based on Cloud Foundry CLI version 6.43.0.

For more details on the most recent release, check out the [changelog](CHANGELOG.md).

Expand Down Expand Up @@ -102,4 +104,9 @@ You can't push an Application-Manifest with a wildcard in tha `path` deceleratio
That only works with the option `--legacy-push`

To fix that issue, please remove the `path` from your manifest and pass the Artifact / Folder with the `-p` option.


If there is a problem while pushing you application you can see the complete trace by setting

Windows: `CF_TRACE=true`

Unix: `CF_TRACE=/dev/stdout`
16 changes: 9 additions & 7 deletions arguments/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import (
"github.com/happytobi/cf-puppeteer/manifest"
"os"
"regexp"
"strconv"
"strings"
)

//ParserArguments struct where all arguments will be parsed into
type ParserArguments struct {
AppName string
ManifestPath string
NoRouteManifestPath string
AppPath string
HealthCheckType string
HealthCheckHTTPEndpoint string
Expand All @@ -24,7 +26,6 @@ type ParserArguments struct {
StackName string
VenerableAction string
Envs map[string]string
ShowLogs bool
ShowCrashLogs bool
DockerImage string
DockerUserName string
Expand All @@ -33,6 +34,7 @@ type ParserArguments struct {
NoRoute bool
AddRoutes bool
NoStart bool
VarsFile string
}

type stringSlice []string
Expand Down Expand Up @@ -73,18 +75,17 @@ func ParseArgs(args []string) (*ParserArguments, error) {
flags.StringVar(&pta.StackName, "s", "", "name of the stack to use")
flags.StringVar(&pta.HealthCheckType, "health-check-type", "port", "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.Timeout, "t", 0, "push timeout in seconds (default 60 seconds)")
flags.IntVar(&pta.InvocationTimeout, "invocation-timeout", -1, "health check invocation timeout in seconds")
flags.StringVar(&pta.Process, "process", "", "use health check type process")
flags.BoolVar(&pta.ShowLogs, "show-app-log", false, "tail and show application log during application start")
flags.BoolVar(&pta.ShowCrashLogs, "show-crash-log", false, "Show recent logs when applications crashes while the deployment")
flags.StringVar(&pta.VenerableAction, "venerable-action", "delete", "option to delete,stop,none application action on venerable app default is delete")
flags.Var(&envs, "env", "Variable key value pair for adding dynamic environment variables; can specify multiple times")
flags.BoolVar(&pta.LegacyPush, "legacy-push", false, "use legacy push instead of new v3 api")
flags.BoolVar(&pta.NoRoute, "no-route", false, "deploy new application without adding routes")
flags.BoolVar(&pta.AddRoutes, "route-only", false, "only add routes from manifest to the application")
flags.BoolVar(&pta.NoStart, "no-start", false, "don't start application after deployment; venerable action is none")
//flags.BoolVar(&pta.ShowLogs, "show-app-log", false, "tail and show application log during application start")
flags.StringVar(&pta.VarsFile, "vars-file", "", "path to a variable substitution file for manifest")
flags.StringVar(&pta.DockerImage, "docker-image", "", "docker image url")
flags.StringVar(&pta.DockerUserName, "docker-username", "", "docker repository username; used with password from env CF_DOCKER_PASSWORD")
dockerPass := os.Getenv("CF_DOCKER_PASSWORD")
Expand All @@ -98,7 +99,7 @@ func ParseArgs(args []string) (*ParserArguments, error) {
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
}
Expand All @@ -113,7 +114,7 @@ func ParseArgs(args []string) (*ParserArguments, error) {
}

//parse manifest
parsedManifest, err := manifest.Parse(pta.ManifestPath)
parsedManifest, noRouteManifestPath, err := manifest.ParseApplicationManifest(pta.ManifestPath, pta.VarsFile)
if err != nil {
return pta, err //ErrManifest
}
Expand All @@ -123,14 +124,15 @@ func ParseArgs(args []string) (*ParserArguments, error) {
}

pta.Manifest = parsedManifest
pta.NoRouteManifestPath = noRouteManifestPath

//check if a docker image shouldbe pushed and verify passed args combination
if len(pta.DockerUserName) > 0 && (len(dockerPass) == 0 || len(pta.DockerImage) == 0) {
return nil, ErrWrongPrivateDockerRepoCombination
}

//set timeout
manifestTimeout := parsedManifest.ApplicationManifests[0].Timeout
manifestTimeout, _ := strconv.Atoi(parsedManifest.ApplicationManifests[0].Timeout)
if manifestTimeout > 0 && pta.Timeout <= 0 {
pta.Timeout = manifestTimeout
} else if manifestTimeout <= 0 && pta.Timeout <= 0 {
Expand Down
8 changes: 2 additions & 6 deletions arguments/arguments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.StackName).To(Equal("stack-name"))
Expect(parsedArguments.Envs).To(Equal(map[string]string{"foo": "bar", "baz": "bob=true"}))
Expect(parsedArguments.VenerableAction).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))
Expand All @@ -69,7 +68,6 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.AppPath).To(Equal("app-path"))
Expect(parsedArguments.StackName).To(Equal("stack-name"))
Expect(parsedArguments.VenerableAction).Should(Equal("delete"))
Expect(parsedArguments.ShowLogs).To(Equal(false))
Expect(parsedArguments.ShowCrashLogs).To(Equal(false))
Expect(parsedArguments.NoRoute).To(Equal(false))
Expect(parsedArguments.Timeout).To(Equal(2))
Expand Down Expand Up @@ -101,7 +99,6 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.StackName).To(Equal("stack-name"))
Expect(parsedArguments.Envs).To(Equal(map[string]string{"foo": "bar", "baz": "bob"}))
Expect(parsedArguments.VenerableAction).Should(Equal("stop"))
Expect(parsedArguments.ShowLogs).To(Equal(false))
Expect(parsedArguments.ShowCrashLogs).To(Equal(false))
Expect(parsedArguments.NoRoute).To(Equal(true))
Expect(parsedArguments.Timeout).To(Equal(2))
Expand Down Expand Up @@ -133,7 +130,6 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.StackName).To(Equal("stack-name"))
Expect(parsedArguments.Envs).To(Equal(map[string]string{"foo": "bar", "baz": "bob"}))
Expect(parsedArguments.VenerableAction).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))
Expand All @@ -157,9 +153,9 @@ var _ = Describe("Flag Parsing", func() {
"--process", "process-name",
"--health-check-type", "process",
"--health-check-http-endpoint", "/foo/bar",
"--show-app-log",
"--route-only",
"--no-start",
"--vars-file", "../fixtures/valid_vars_file.yml",
},
)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -170,7 +166,6 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.StackName).To(Equal("stack-name"))
Expect(parsedArguments.Envs).To(Equal(map[string]string{"foo": "bar", "baz": "bob"}))
Expect(parsedArguments.VenerableAction).Should(Equal("none"))
Expect(parsedArguments.ShowLogs).To(Equal(true))
Expect(parsedArguments.ShowCrashLogs).To(Equal(false))
Expect(parsedArguments.Timeout).To(Equal(120))
Expect(parsedArguments.InvocationTimeout).To(Equal(2211))
Expand All @@ -179,6 +174,7 @@ var _ = Describe("Flag Parsing", func() {
Expect(parsedArguments.HealthCheckHTTPEndpoint).To(Equal("/foo/bar"))
Expect(parsedArguments.AddRoutes).To(Equal(true))
Expect(parsedArguments.NoStart).To(Equal(true))
Expect(parsedArguments.VarsFile).To(Equal("../fixtures/valid_vars_file.yml"))
})

It("parses args without appName and wrong envs format", func() {
Expand Down
120 changes: 16 additions & 104 deletions cf/push.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package cf

import (
"encoding/json"
"fmt"
"os"

"github.com/happytobi/cf-puppeteer/arguments"
v2 "github.com/happytobi/cf-puppeteer/cf/v2"

v3 "github.com/happytobi/cf-puppeteer/cf/v3"

"code.cloudfoundry.org/cli/plugin"
"github.com/blang/semver"
"github.com/happytobi/cf-puppeteer/cf/cli"
)

//ApplicationPushData struct
Expand All @@ -24,14 +18,11 @@ type ApplicationPushData struct {
//PuppeteerPush push application interface
type PuppeteerPush interface {
PushApplication(venAppName string, venAppExists bool, spaceGUID string, parsedArguments *arguments.ParserArguments) error
SwitchRoutes(venAppName string, venAppExists bool, appName string, routes []map[string]string, legacyPush bool) error
}

var cliCalls cli.Calls

//NewApplicationPush generate new cf puppeteer push
func NewApplicationPush(conn plugin.CliConnection, traceLogging bool) *ApplicationPushData {
cliCalls = cli.NewCli(conn, traceLogging)

return &ApplicationPushData{
Connection: conn,
TraceLogging: traceLogging,
Expand All @@ -40,101 +31,22 @@ func NewApplicationPush(conn plugin.CliConnection, traceLogging bool) *Applicati

//PushApplication push application to cf
func (adp *ApplicationPushData) PushApplication(venAppName string, venAppExists bool, spaceGUID string, parsedArguments *arguments.ParserArguments) error {
v3Push, err := useV3Push()
if err != nil {
//fatal exit
os.Exit(1)
}

if v3Push && parsedArguments.LegacyPush != true {
var v2Resources v2.Resources = v2.NewV2Resources(adp.Connection, adp.TraceLogging)
var push v3.Push = v3.NewV3Push(adp.Connection, adp.TraceLogging)
if parsedArguments.AddRoutes {
//TODO loop over applications
return push.SwitchRoutesOnly(venAppName, parsedArguments.AppName, parsedArguments.Manifest.ApplicationManifests[0].Routes)
}
return push.PushApplication(venAppName, spaceGUID, parsedArguments, v2Resources)
}

var legacyPush v2.Push = v2.NewV2LegacyPush(adp.Connection, adp.TraceLogging)
if parsedArguments.AddRoutes {
return legacyPush.SwitchRoutesOnly(venAppName, venAppExists, parsedArguments.AppName, parsedArguments.Manifest.ApplicationManifests[0].Routes)
}
return legacyPush.PushApplication(venAppName, spaceGUID, parsedArguments)
}

func useV3Push() (bool, error) {
_, v3ServerVersion, err := getCloudControllerAPIVersion()
if err != nil {
return false, err
}
v3SerSemVer, err := semver.Make(v3ServerVersion)
if err != nil {
return false, nil
}

expectedRange, err := semver.ParseRange(fmt.Sprintf(">=%s", v3.MinControllerVersion))
//check if we can use the v3 push
if expectedRange(v3SerSemVer) {
return true, nil
}
return false, nil
}

// cloudControllerResponse
type cloudControllerResponse struct {
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
BitsService interface{} `json:"bits_service"`
CloudControllerV2 struct {
Href string `json:"href"`
Meta struct {
Version string `json:"version"`
} `json:"meta"`
} `json:"cloud_controller_v2"`
CloudControllerV3 struct {
Href string `json:"href"`
Meta struct {
Version string `json:"version"`
} `json:"meta"`
} `json:"cloud_controller_v3"`
NetworkPolicyV0 struct {
Href string `json:"href"`
} `json:"network_policy_v0"`
NetworkPolicyV1 struct {
Href string `json:"href"`
} `json:"network_policy_v1"`
Uaa struct {
Href string `json:"href"`
} `json:"uaa"`
Credhub interface{} `json:"credhub"`
Routing struct {
Href string `json:"href"`
} `json:"routing"`
Logging struct {
Href string `json:"href"`
} `json:"logging"`
LogStream struct {
Href string `json:"href"`
} `json:"log_stream"`
AppSSH struct {
Href string `json:"href"`
Meta struct {
HostKeyFingerprint string `json:"host_key_fingerprint"`
OauthClient string `json:"oauth_client"`
} `json:"meta"`
} `json:"app_ssh"`
} `json:"links"`
if parsedArguments.LegacyPush == true {
var legacyPush v2.Push = v2.NewV2LegacyPush(adp.Connection, adp.TraceLogging)
return legacyPush.PushApplication(parsedArguments)
}
//v3 push
var v2Resources v2.Resources = v2.NewV2Resources(adp.Connection, adp.TraceLogging)
var push v3.Push = v3.NewV3Push(adp.Connection, adp.TraceLogging)
return push.PushApplication(venAppName, spaceGUID, parsedArguments, v2Resources)
}

func getCloudControllerAPIVersion() (string, string, error) {
callResp, err := cliCalls.GetJSON("/")
var response cloudControllerResponse
err = json.Unmarshal([]byte(callResp), &response)
if err != nil {
return "", "", err
//handle route switch
func (adp *ApplicationPushData) SwitchRoutes(venAppName string, venAppExists bool, appName string, routes []map[string]string, legacyPush bool) error {
if legacyPush {
var legacyPush v2.Push = v2.NewV2LegacyPush(adp.Connection, adp.TraceLogging)
return legacyPush.SwitchRoutesOnly(venAppName, venAppExists, appName, routes)
}
return response.Links.CloudControllerV2.Meta.Version, response.Links.CloudControllerV3.Meta.Version, nil
var push v3.Push = v3.NewV3Push(adp.Connection, adp.TraceLogging)
return push.SwitchRoutesOnly(venAppName, appName, routes)
}
Loading

0 comments on commit 1c85ade

Please sign in to comment.