containerd/cmd/dist/edit.go
Stephen J Day 941b0e47b0
cmd/dist: close and remove tempfile after edit
Signed-off-by: Stephen J Day <stephen.day@docker.com>
2017-05-09 15:17:03 -07:00

138 lines
2.6 KiB
Go

package main
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
contentapi "github.com/containerd/containerd/api/services/content"
contentservice "github.com/containerd/containerd/services/content"
digest "github.com/opencontainers/go-digest"
"github.com/urfave/cli"
)
var editCommand = cli.Command{
Name: "edit",
Usage: "edit a blob and return a new digest.",
ArgsUsage: "[flags] <digest>",
Description: `Edit a blob and return a new digest.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "validate",
Usage: "validate the result against a format (json, mediatype, etc.)",
},
},
Action: func(context *cli.Context) error {
var (
ctx = background
validate = context.String("validate")
object = context.Args().First()
)
if validate != "" {
return errors.New("validating the edit result not supported")
}
// TODO(stevvooe): Support looking up objects by a reference through
// the image metadata storage.
dgst, err := digest.Parse(object)
if err != nil {
return err
}
conn, err := connectGRPC(context)
if err != nil {
return err
}
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
rc, err := provider.Reader(ctx, dgst)
if err != nil {
return err
}
defer rc.Close()
nrc, err := edit(rc)
if err != nil {
return err
}
defer nrc.Close()
wr, err := ingester.Writer(ctx, "edit-"+object, 0, "") // TODO(stevvooe): Choose a better key?
if err != nil {
return err
}
if _, err := io.Copy(wr, nrc); err != nil {
return err
}
if err := wr.Commit(0, wr.Digest()); err != nil {
return err
}
fmt.Println(wr.Digest())
return nil
},
}
func edit(rd io.Reader) (io.ReadCloser, error) {
tmp, err := ioutil.TempFile("", "edit-")
if err != nil {
return nil, err
}
if _, err := io.Copy(tmp, rd); err != nil {
tmp.Close()
return nil, err
}
cmd := exec.Command("sh", "-c", "$EDITOR "+tmp.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
if err := cmd.Run(); err != nil {
tmp.Close()
return nil, err
}
if _, err := tmp.Seek(0, io.SeekStart); err != nil {
tmp.Close()
return nil, err
}
return onCloser{ReadCloser: tmp, onClose: func() error {
return os.RemoveAll(tmp.Name())
}}, nil
}
type onCloser struct {
io.ReadCloser
onClose func() error
}
func (oc onCloser) Close() error {
var err error
if err1 := oc.ReadCloser.Close(); err1 != nil {
err = err1
}
if oc.onClose != nil {
err1 := oc.onClose()
if err == nil {
err = err1
}
}
return err
}