Skip to content

Commit

Permalink
Add a cleanup step
Browse files Browse the repository at this point in the history
Fix GoogleContainerTools#61

Signed-off-by: David Gageot <[email protected]>
  • Loading branch information
dgageot committed Apr 20, 2018
1 parent 55b5cfb commit 85a7ae2
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 11 deletions.
5 changes: 4 additions & 1 deletion cmd/skaffold/app/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package cmd

import (
"context"
"io"

yaml "gopkg.in/yaml.v2"
Expand Down Expand Up @@ -86,6 +87,8 @@ func SetUpLogs(out io.Writer, level string) error {
}

func runSkaffold(out io.Writer, dev bool, filename string) error {
ctx := context.Background()

buf, err := util.ReadConfiguration(filename)
if err != nil {
return errors.Wrap(err, "read skaffold config")
Expand Down Expand Up @@ -121,7 +124,7 @@ func runSkaffold(out io.Writer, dev bool, filename string) error {
return errors.Wrap(err, "getting skaffold config")
}

if err := r.Run(); err != nil {
if err := r.Run(ctx); err != nil {
return errors.Wrap(err, "running skaffold steps")
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/skaffold/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type Deployer interface {
// Deploy should ensure that the build results are deployed to the Kubernetes
// cluster.
Deploy(context.Context, io.Writer, *build.BuildResult) (*Result, error)

// Cleanup deletes what was deployed by calling Deploy.
Cleanup(context.Context, io.Writer) error
}

func JoinTagsToBuildResult(b []build.Build, params map[string]string) (map[string]build.Build, error) {
Expand Down
21 changes: 21 additions & 0 deletions pkg/skaffold/deploy/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, b *build.Build
return nil, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
for _, r := range h.HelmDeploy.Releases {
if err := h.deleteRelease(out, r); err != nil {
return errors.Wrapf(err, "deploying %s", r.Name)
}
}
return nil
}

func (h *HelmDeployer) args(moreArgs ...string) []string {
return append([]string{"--kube-context", h.kubeContext}, moreArgs...)
}
Expand Down Expand Up @@ -118,3 +128,14 @@ func (h *HelmDeployer) deployRelease(out io.Writer, r v1alpha2.HelmRelease, b *b
out.Write(stdout)
return nil
}

func (h *HelmDeployer) deleteRelease(out io.Writer, r v1alpha2.HelmRelease) error {
getCmd := exec.Command("helm", h.args("delete", r.Name, "--purge")...)
stdout, stderr, err := util.RunCommand(getCmd, nil)
if err != nil {
logrus.Debugf("running helm delete %s: %v stdout: %s stderr: %s", r.Name, err, string(stdout), string(stderr))
}

out.Write(stdout)
return nil
}
19 changes: 19 additions & 0 deletions pkg/skaffold/deploy/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, b *build.Bu
return &Result{}, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
if len(k.KubectlDeploy.Manifests) == 0 {
return k.kubectl(nil, out, "delete", "deployment", "skaffold")
}

manifests, err := k.readManifests()
if err != nil {
return errors.Wrap(err, "reading manifests")
}

err = k.kubectl(manifests.reader(), out, "delete", "-f", "-")
if err != nil {
return errors.Wrap(err, "deleting manifests")
}

return nil
}

// readOrGenerateManifests reads the manifests to deploy/delete. If no manifest exists, try to
// generate it with the information we have.
func (k *KubectlDeployer) readOrGenerateManifests(b *build.BuildResult) (manifestList, error) {
Expand Down
52 changes: 52 additions & 0 deletions pkg/skaffold/deploy/kubectl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
"github.com/GoogleContainerTools/skaffold/testutil"
"github.com/pkg/errors"
"github.com/spf13/afero"
)

Expand Down Expand Up @@ -163,6 +164,57 @@ func TestKubectlDeploy(t *testing.T) {
}
}

func TestKubectlCleanup(t *testing.T) {
var tests = []struct {
description string
cfg *v1alpha2.DeployConfig
command util.Command
shouldErr bool
}{
{
description: "cleanup success",
cfg: &v1alpha2.DeployConfig{
DeployType: v1alpha2.DeployType{
KubectlDeploy: &v1alpha2.KubectlDeploy{
Manifests: []string{"test/deployment.yaml"},
},
},
},
command: testutil.NewFakeRunCommand("kubectl --context kubecontext delete -f -", "", "", nil),
},
{
description: "cleanup error",
cfg: &v1alpha2.DeployConfig{
DeployType: v1alpha2.DeployType{
KubectlDeploy: &v1alpha2.KubectlDeploy{
Manifests: []string{"test/deployment.yaml"},
},
},
},
command: testutil.NewFakeRunCommand("kubectl --context kubecontext delete -f -", "", "", errors.New("BUG")),
shouldErr: true,
},
}

util.Fs = afero.NewMemMapFs()
defer util.ResetFs()
util.Fs.MkdirAll("test", 0750)
afero.WriteFile(util.Fs, "test/deployment.yaml", []byte(deploymentYAML), 0644)

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
if test.command != nil {
util.DefaultExecCommand = test.command
defer util.ResetDefaultExecCommand()
}

k := NewKubectlDeployer(test.cfg, testKubeContext)
err := k.Cleanup(context.Background(), &bytes.Buffer{})

testutil.CheckError(t, test.shouldErr, err)
})
}
}
func TestReplaceImages(t *testing.T) {
var tests = []struct {
description string
Expand Down
15 changes: 11 additions & 4 deletions pkg/skaffold/kubernetes/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (a *LogAggregator) Start(ctx context.Context, client corev1.CoreV1Interface
}

if a.podSelector.Select(pod) {
go a.streamLogs(client, pod)
go a.streamLogs(ctx, client, pod)
}
}
}
Expand All @@ -92,7 +92,7 @@ func (a *LogAggregator) Start(ctx context.Context, client corev1.CoreV1Interface
}

// nolint: interfacer
func (a *LogAggregator) streamLogs(client corev1.CoreV1Interface, pod *v1.Pod) error {
func (a *LogAggregator) streamLogs(ctx context.Context, client corev1.CoreV1Interface, pod *v1.Pod) error {
pods := client.Pods(pod.Namespace)
if err := WaitForPodReady(pods, pod.Name); err != nil {
return errors.Wrap(err, "waiting for pod ready")
Expand Down Expand Up @@ -138,7 +138,7 @@ func (a *LogAggregator) streamLogs(client corev1.CoreV1Interface, pod *v1.Pod) e
rc.Close()
}()

if err := a.streamRequest(prefix, rc); err != nil {
if err := a.streamRequest(ctx, prefix, rc); err != nil {
logrus.Errorf("streaming request %s", err)
}
}()
Expand All @@ -154,9 +154,16 @@ func prefix(pod *v1.Pod, container v1.ContainerStatus) string {
return fmt.Sprintf("[%s]", container.Name)
}

func (a *LogAggregator) streamRequest(header string, rc io.Reader) error {
func (a *LogAggregator) streamRequest(ctx context.Context, header string, rc io.Reader) error {
r := bufio.NewReader(rc)
for {
select {
case <-ctx.Done():
logrus.Infof("%s interrupted", header)
return nil
default:
}

// Read up to newline
line, err := r.ReadBytes('\n')
if err == io.EOF {
Expand Down
42 changes: 37 additions & 5 deletions pkg/skaffold/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package runner
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
Expand Down Expand Up @@ -129,18 +132,18 @@ func newTaggerForConfig(t v1alpha2.TagPolicy) (tag.Tagger, error) {
}

// Run runs the skaffold build and deploy pipeline.
func (r *SkaffoldRunner) Run() error {
ctx := context.Background()

func (r *SkaffoldRunner) Run(ctx context.Context) error {
if r.opts.DevMode {
return r.dev(ctx, r.config.Build.Artifacts)
return cleanUpOnCtrlC(ctx, r.dev, r.cleanup)
}

_, _, err := r.buildAndDeploy(ctx, r.config.Build.Artifacts, nil)
return err
}

func (r *SkaffoldRunner) dev(ctx context.Context, artifacts []*v1alpha2.Artifact) error {
func (r *SkaffoldRunner) dev(ctx context.Context) error {
artifacts := r.config.Build.Artifacts

var err error
r.depMap, err = build.NewDependencyMap(artifacts)
if err != nil {
Expand Down Expand Up @@ -245,6 +248,35 @@ func (r *SkaffoldRunner) deploy(ctx context.Context, bRes *build.BuildResult) (*
return dRes, nil
}

func cleanUpOnCtrlC(ctx context.Context, runDevMode func(context.Context) error, cleanup func(context.Context)) error {
ctx, cancel := context.WithCancel(ctx)

signals := make(chan os.Signal, 2)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

go func() {
<-signals
cancel()
}()

errRun := runDevMode(ctx)
cleanup(ctx)
return errRun
}

func (r *SkaffoldRunner) cleanup(ctx context.Context) {
start := time.Now()
fmt.Fprintln(r.opts.Output, "Cleaning up...")

err := r.Deployer.Cleanup(ctx, r.opts.Output)
if err != nil {
logrus.Warnf("cleanup: %s", err)
return
}

fmt.Fprintln(r.opts.Output, "Cleanup complete in", time.Since(start))
}

func mergeWithPreviousBuilds(builds, previous []build.Build) []build.Build {
updatedBuilds := map[string]bool{}
for _, build := range builds {
Expand Down
11 changes: 10 additions & 1 deletion pkg/skaffold/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func (t *TestDeployer) Deploy(context.Context, io.Writer, *build.BuildResult) (*
return t.res, t.err
}

func (t *TestDeployer) Cleanup(ctx context.Context, out io.Writer) error {
return nil
}

type TestDeployAll struct {
deployed *build.BuildResult
}
Expand All @@ -79,6 +83,10 @@ func (t *TestDeployAll) Deploy(ctx context.Context, w io.Writer, bRes *build.Bui
return &deploy.Result{}, nil
}

func (t *TestDeployAll) Cleanup(ctx context.Context, out io.Writer) error {
return nil
}

type TestTagger struct {
out string
err error
Expand Down Expand Up @@ -347,7 +355,8 @@ func TestRun(t *testing.T) {

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := test.runner.Run()
err := test.runner.Run(context.Background())

testutil.CheckError(t, test.shouldErr, err)
})
}
Expand Down

0 comments on commit 85a7ae2

Please sign in to comment.