Skip to content

Commit

Permalink
Merge pull request #25 from buildtool/deployment_timeout
Browse files Browse the repository at this point in the history
feature: configurable deployment wait time
  • Loading branch information
peter-svensson authored Dec 19, 2019
2 parents ae58962 + 312eb28 commit e665574
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 31 deletions.
10 changes: 8 additions & 2 deletions cmd/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ func main() {
}

func doDeploy() int {
var context, namespace string
var context, namespace, timeout string
const (
contextUsage = "override the context for default environment deployment target"
namespaceUsage = "override the namespace for default environment deployment target"
timeoutUsage = "override the default deployment timeout (2 minutes). 0 means forever, all other values should contain a corresponding time unit (e.g. 1s, 2m, 3h)"
)
set := flag.NewFlagSet("deploy", flag.ExitOnError)
set.Usage = func() {
Expand All @@ -45,6 +46,8 @@ func doDeploy() int {
set.StringVar(&context, "c", "", contextUsage+" (shorthand)")
set.StringVar(&namespace, "namespace", "", namespaceUsage)
set.StringVar(&namespace, "n", "", namespaceUsage+" (shorthand)")
set.StringVar(&timeout, "timeout", "", timeoutUsage)
set.StringVar(&timeout, "t", "", timeoutUsage+" (shorthand)")
_ = set.Parse(os.Args[1:])
if set.NArg() < 1 {
set.Usage()
Expand All @@ -67,6 +70,9 @@ func doDeploy() int {
if namespace != "" {
env.Namespace = namespace
}
if timeout == "" {
timeout = "2m"
}
currentCI := cfg.CurrentCI()
if !ci.IsValid(currentCI) {
_, _ = fmt.Println(tml.Sprintf("Commit and/or branch information is <red>missing</red>. Perhaps your not in a Git repository or forgot to set environment variables?"))
Expand All @@ -76,7 +82,7 @@ func doDeploy() int {
tstamp := time.Now().Format(time.RFC3339)
client := kubectl.New(env, os.Stdout, os.Stderr)
defer client.Cleanup()
if err := deploy.Deploy(dir, currentCI.Commit(), currentCI.BuildName(), tstamp, environment, client, os.Stdout, os.Stderr); err != nil {
if err := deploy.Deploy(dir, currentCI.Commit(), currentCI.BuildName(), tstamp, environment, client, os.Stdout, os.Stderr, timeout); err != nil {
fmt.Println(err.Error())
return -4
}
Expand Down
27 changes: 27 additions & 0 deletions cmd/deploy/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,30 @@ environments:
os.Args = []string{"deploy", "-c", "other", "-n", "dev", "dummy"}
main()
}

func TestDeploy_Timeout(t *testing.T) {
exitFunc = func(code int) {
assert.Equal(t, -4, code)
}

defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")()
defer pkg.SetEnv("CI_PROJECT_NAME", "dummy")()
defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")()
oldPwd, _ := os.Getwd()
name, _ := ioutil.TempDir(os.TempDir(), "build-tools")
defer func() { _ = os.RemoveAll(name) }()
yaml := `
environments:
dummy:
context: missing
namespace: none
`
_ = ioutil.WriteFile(filepath.Join(name, ".buildtools.yaml"), []byte(yaml), 0777)

err := os.Chdir(name)
assert.NoError(t, err)
defer func() { _ = os.Chdir(oldPwd) }()

os.Args = []string{"deploy", "-t", "20s", "dummy"}
main()
}
4 changes: 2 additions & 2 deletions pkg/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
"strings"
)

func Deploy(dir, commit, buildName, timestamp, targetEnvironment string, client kubectl.Kubectl, out, eout io.Writer) error {
func Deploy(dir, commit, buildName, timestamp, targetEnvironment string, client kubectl.Kubectl, out, eout io.Writer, timeout string) error {
deploymentFiles := filepath.Join(dir, "k8s")
if err := processDir(deploymentFiles, commit, timestamp, targetEnvironment, client, out, eout); err != nil {
return err
}

if client.DeploymentExists(buildName) {
if !client.RolloutStatus(buildName) {
if !client.RolloutStatus(buildName, timeout) {
_, _ = fmt.Fprint(out, "Rollout failed. Fetching events.")
_, _ = fmt.Fprint(out, client.DeploymentEvents(buildName))
_, _ = fmt.Fprint(out, client.PodEvents(buildName))
Expand Down
28 changes: 14 additions & 14 deletions pkg/deploy/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestDeploy_MissingDeploymentFilesDir(t *testing.T) {

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(".", "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(".", "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, "open k8s: no such file or directory")
assert.Equal(t, "", out.String())
Expand All @@ -38,7 +38,7 @@ func TestDeploy_NoFiles(t *testing.T) {

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 0, len(client.Inputs))
Expand All @@ -65,7 +65,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 1, len(client.Inputs))
Expand All @@ -85,7 +85,7 @@ func TestDeploy_UnreadableFile(t *testing.T) {

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, fmt.Sprintf("read %s/k8s/deploy.yaml: is a directory", name))
assert.Equal(t, "", out.String())
Expand All @@ -107,7 +107,7 @@ func TestDeploy_FileBrokenSymlink(t *testing.T) {

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, fmt.Sprintf("open %s/k8s/deploy.yaml: no such file or directory", name))
assert.Equal(t, "", out.String())
Expand All @@ -133,7 +133,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "dummy", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "dummy", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 1, len(client.Inputs))
Expand All @@ -157,7 +157,7 @@ func TestDeploy_EnvSpecificFiles(t *testing.T) {

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 1, len(client.Inputs))
Expand All @@ -180,7 +180,7 @@ echo "Prod-script with suffix"`

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 0, len(client.Inputs))
Expand All @@ -202,7 +202,7 @@ echo "Script without suffix should not execute"`

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 0, len(client.Inputs))
Expand All @@ -225,7 +225,7 @@ echo "Prod-script with suffix"`

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "prod", client, out, eout, "2m")

assert.EqualError(t, err, fmt.Sprintf("fork/exec %s: permission denied", scriptName))
}
Expand All @@ -249,7 +249,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, "apply failed")
assert.Equal(t, "", out.String())
Expand Down Expand Up @@ -277,7 +277,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "2019-05-13T17:22:36Z01:00", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "2019-05-13T17:22:36Z01:00", "test", client, out, eout, "2m")

assert.NoError(t, err)
assert.Equal(t, 1, len(client.Inputs))
Expand Down Expand Up @@ -314,7 +314,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, "failed to rollout")
assert.Equal(t, 1, len(client.Inputs))
Expand Down Expand Up @@ -344,7 +344,7 @@ metadata:

out := &bytes.Buffer{}
eout := &bytes.Buffer{}
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout)
err := Deploy(name, "abc123", "image", "20190513-17:22:36", "test", client, out, eout, "2m")

assert.EqualError(t, err, "failed to rollout")
assert.Equal(t, 1, len(client.Inputs))
Expand Down
6 changes: 3 additions & 3 deletions pkg/kubectl/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Kubectl interface {
Apply(input string) error
Cleanup()
DeploymentExists(name string) bool
RolloutStatus(name string) bool
RolloutStatus(name, timeout string) bool
DeploymentEvents(name string) string
PodEvents(name string) string
}
Expand Down Expand Up @@ -114,9 +114,9 @@ func (k kubectl) DeploymentExists(name string) bool {
return c.Execute() == nil
}

func (k kubectl) RolloutStatus(name string) bool {
func (k kubectl) RolloutStatus(name, timeout string) bool {
args := k.defaultArgs()
args = append(args, "rollout", "status", "deployment", "--timeout=2m", name)
args = append(args, "rollout", "status", "deployment", fmt.Sprintf("--timeout=%s", timeout), name)
_, _ = fmt.Fprintf(k.out, "kubectl %s\n", strings.Join(args, " "))
c := newKubectlCmd(os.Stdin, os.Stdout, os.Stderr)
c.SetArgs(args)
Expand Down
10 changes: 5 additions & 5 deletions pkg/kubectl/kubectl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func TestKubectl_RolloutStatusSuccess(t *testing.T) {

k := New(&config.Environment{Context: "missing", Namespace: "default"}, out, eout)

result := k.RolloutStatus("image")
result := k.RolloutStatus("image", "2m")
assert.True(t, result)
assert.Equal(t, 1, len(calls))
assert.Equal(t, []string{"rollout", "status", "deployment", "image", "--context", "missing", "--namespace", "default", "--timeout", "2m0s"}, calls[0])
Expand All @@ -165,7 +165,7 @@ func TestKubectl_RolloutStatusFailure(t *testing.T) {

k := New(&config.Environment{Context: "missing", Namespace: "default"}, out, eout)

result := k.RolloutStatus("image")
result := k.RolloutStatus("image", "2m")
assert.False(t, result)
assert.Equal(t, 1, len(calls))
assert.Equal(t, []string{"rollout", "status", "deployment", "image", "--context", "missing", "--namespace", "default", "--timeout", "2m0s"}, calls[0])
Expand All @@ -186,11 +186,11 @@ func TestKubectl_RolloutStatusFatal(t *testing.T) {

k := New(&config.Environment{Context: "missing", Namespace: "default"}, out, eout)

result := k.RolloutStatus("image")
result := k.RolloutStatus("image", "3m")
assert.False(t, result)
assert.Equal(t, 1, len(calls))
assert.Equal(t, []string{"rollout", "status", "deployment", "image", "--context", "missing", "--namespace", "default", "--timeout", "2m0s"}, calls[0])
assert.Equal(t, "kubectl --context missing --namespace default rollout status deployment --timeout=2m image\n", out.String())
assert.Equal(t, []string{"rollout", "status", "deployment", "image", "--context", "missing", "--namespace", "default", "--timeout", "3m0s"}, calls[0])
assert.Equal(t, "kubectl --context missing --namespace default rollout status deployment --timeout=3m image\n", out.String())
assert.Equal(t, "failed to get kubeconfig from environment\n", eout.String())
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (m *MockKubectl) DeploymentExists(name string) bool {
return m.Deployment
}

func (m *MockKubectl) RolloutStatus(name string) bool {
func (m *MockKubectl) RolloutStatus(name, timeout string) bool {
return m.Status
}

Expand Down
9 changes: 5 additions & 4 deletions www/content/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ $ push -f docker/Dockerfile.build

Deploys the built application to a Kubernetes cluster. Normal usage `deploy <environment>`, but additional flags can be used to override:

| Flag | Description |
| :--------------------------------- | :------------------------------------------------------------ |
| `-c/--context` | Use a different context than the one found in configuration |
| `-n/--namespace` | Use a different namespace than the one found in configuration |
| Flag | Description |
| :--------------------------------- | :-------------------------------------------------------------------------------|
| `-c/--context` | Use a different context than the one found in configuration |
| `-n/--namespace` | Use a different namespace than the one found in configuration |
| `-t/--timeout` | Override the default deployment waiting time for completion (default 2 minutes). 0 means forever, all other values should contain a corresponding time unit (e.g. 1s, 2m, 3h) |

```sh
$ deploy -n testing_namespace local
Expand Down

0 comments on commit e665574

Please sign in to comment.