Browse Source

Merged upstream changes in fs branch

shin- 12 years ago
parent
commit
2441edf1a3
12 changed files with 138 additions and 502 deletions
  1. 10 1
      container.go
  2. 1 2
      docker.go
  3. 1 1
      dockerd/dockerd.go
  4. 1 1
      fake/fake.go
  5. 106 106
      fs/changes.go
  6. 0 1
      fs/mount_linux.go
  7. 6 8
      fs/store.go
  8. 1 1
      fs/store_test.go
  9. 0 362
      image/image.go
  10. 3 6
      rcli/http.go
  11. 5 7
      rcli/tcp.go
  12. 4 6
      rcli/types.go

+ 10 - 1
container.go

@@ -2,6 +2,7 @@ package docker
 
 import (
 	"./fs"
+	"bytes"
 	"encoding/json"
 	"errors"
 	"github.com/kr/pty"
@@ -118,17 +119,25 @@ func createContainer(id string, root string, command string, args []string, imag
 	return container, nil
 }
 
-func loadContainer(containerPath string, netManager *NetworkManager) (*Container, error) {
+func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
 	data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
 	if err != nil {
 		return nil, err
 	}
+	mountpoint, err := store.FetchMountpoint(
+		path.Join(containerPath, "rootfs"),
+		path.Join(containerPath, "rw"),
+	)
+	if err != nil {
+		return nil, err
+	}
 	container := &Container{
 		stdout:          newWriteBroadcaster(),
 		stderr:          newWriteBroadcaster(),
 		lxcConfigPath:   path.Join(containerPath, "config.lxc"),
 		networkManager:  netManager,
 		NetworkSettings: &NetworkSettings{},
+		Mountpoint:      mountpoint,
 	}
 	// Load container settings
 	if err := json.Unmarshal(data, container); err != nil {

+ 1 - 2
docker.go

@@ -94,12 +94,11 @@ func (docker *Docker) restore() error {
 		return err
 	}
 	for _, v := range dir {
-		container, err := loadContainer(path.Join(docker.repository, v.Name()), docker.networkManager)
+		container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager)
 		if err != nil {
 			log.Printf("Failed to load container %v: %v", v.Name(), err)
 			continue
 		}
-		container.Mountpoint.Store = docker.Store
 		docker.containers.PushBack(container)
 	}
 	return nil

+ 1 - 1
dockerd/dockerd.go

@@ -1,9 +1,9 @@
 package main
 
 import (
-	"flag"
 	".."
 	"../server"
+	"flag"
 	"log"
 )
 

+ 1 - 1
fake/fake.go

@@ -13,7 +13,7 @@ func FakeTar() (io.Reader, error) {
 	content := []byte("Hello world!\n")
 	buf := new(bytes.Buffer)
 	tw := tar.NewWriter(buf)
-	for _, name := range []string{"hello", "etc/postgres/postgres.conf", "etc/passwd", "var/log/postgres/postgres.conf"} {
+	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
 		hdr := new(tar.Header)
 		hdr.Size = int64(len(content))
 		hdr.Name = name

+ 106 - 106
fs/changes.go

@@ -1,129 +1,129 @@
 package fs
 
 import (
-    "fmt"
-    "path/filepath"
-    "os"
-    "strings"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
 )
 
 type ChangeType int
 
 const (
-    ChangeModify = iota
-    ChangeAdd
-    ChangeDelete
+	ChangeModify = iota
+	ChangeAdd
+	ChangeDelete
 )
 
 type Change struct {
-    Path string
-    Kind ChangeType
+	Path string
+	Kind ChangeType
 }
 
 func (change *Change) String() string {
-    var kind string
-    switch change.Kind {
-    case ChangeModify:
-        kind = "C"
-    case ChangeAdd:
-        kind = "A"
-    case ChangeDelete:
-        kind = "D"
-    }
-    return fmt.Sprintf("%s %s", kind, change.Path)
+	var kind string
+	switch change.Kind {
+	case ChangeModify:
+		kind = "C"
+	case ChangeAdd:
+		kind = "A"
+	case ChangeDelete:
+		kind = "D"
+	}
+	return fmt.Sprintf("%s %s", kind, change.Path)
 }
 
 func (store *Store) Changes(mp *Mountpoint) ([]Change, error) {
-    var changes []Change
-    image, err := store.Get(mp.Image)
-    if err != nil {
-        return nil, err
-    }
-    layers, err := image.layers()
-    if err != nil {
-        return nil, err
-    }
-
-    err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error {
-        if err != nil {
-            return err
-        }
-
-        // Rebase path
-        path, err = filepath.Rel(mp.Rw, path)
-        if err != nil {
-            return err
-        }
-        path = filepath.Join("/", path)
-
-        // Skip root
-        if path == "/" {
-            return nil
-        }
-
-        // Skip AUFS metadata
-        if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
-            return err
-        }
-
-        change := Change{
-            Path: path,
-        }
-
-        // Find out what kind of modification happened
-        file := filepath.Base(path)
-        // If there is a whiteout, then the file was removed
-        if strings.HasPrefix(file, ".wh.") {
-            originalFile := strings.TrimLeft(file, ".wh.")
-            change.Path = filepath.Join(filepath.Dir(path), originalFile)
-            change.Kind = ChangeDelete
-        } else {
-            // Otherwise, the file was added
-            change.Kind = ChangeAdd
-
-            // ...Unless it already existed in a top layer, in which case, it's a modification
-            for _, layer := range layers {
-                stat, err := os.Stat(filepath.Join(layer, path))
-                if err != nil && !os.IsNotExist(err) {
-                    return err
-                }
-                if err == nil {
-                    // The file existed in the top layer, so that's a modification
-
-                    // However, if it's a directory, maybe it wasn't actually modified.
-                    // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
-                    if stat.IsDir() && f.IsDir() {
-                        if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
-                            // Both directories are the same, don't record the change
-                            return nil
-                        }
-                    }
-                    change.Kind = ChangeModify
-                    break
-                }
-            }
-        }
-
-        // Record change
-        changes = append(changes, change)
-        return nil
-    })
-    if err != nil {
-        return nil, err
-    }
-    return changes, nil
+	var changes []Change
+	image, err := store.Get(mp.Image)
+	if err != nil {
+		return nil, err
+	}
+	layers, err := image.layers()
+	if err != nil {
+		return nil, err
+	}
+
+	err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		// Rebase path
+		path, err = filepath.Rel(mp.Rw, path)
+		if err != nil {
+			return err
+		}
+		path = filepath.Join("/", path)
+
+		// Skip root
+		if path == "/" {
+			return nil
+		}
+
+		// Skip AUFS metadata
+		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
+			return err
+		}
+
+		change := Change{
+			Path: path,
+		}
+
+		// Find out what kind of modification happened
+		file := filepath.Base(path)
+		// If there is a whiteout, then the file was removed
+		if strings.HasPrefix(file, ".wh.") {
+			originalFile := strings.TrimLeft(file, ".wh.")
+			change.Path = filepath.Join(filepath.Dir(path), originalFile)
+			change.Kind = ChangeDelete
+		} else {
+			// Otherwise, the file was added
+			change.Kind = ChangeAdd
+
+			// ...Unless it already existed in a top layer, in which case, it's a modification
+			for _, layer := range layers {
+				stat, err := os.Stat(filepath.Join(layer, path))
+				if err != nil && !os.IsNotExist(err) {
+					return err
+				}
+				if err == nil {
+					// The file existed in the top layer, so that's a modification
+
+					// However, if it's a directory, maybe it wasn't actually modified.
+					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
+					if stat.IsDir() && f.IsDir() {
+						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
+							// Both directories are the same, don't record the change
+							return nil
+						}
+					}
+					change.Kind = ChangeModify
+					break
+				}
+			}
+		}
+
+		// Record change
+		changes = append(changes, change)
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	return changes, nil
 }
 
 // Reset removes all changes to the filesystem, reverting it to its initial state.
 func (mp *Mountpoint) Reset() error {
-    if err := os.RemoveAll(mp.Rw); err != nil {
-        return err
-    }
-    // We removed the RW directory itself along with its content: let's re-create an empty one.
-    if err := mp.createFolders(); err != nil {
-        return err
-    }
-    return nil
+	if err := os.RemoveAll(mp.Rw); err != nil {
+		return err
+	}
+	// We removed the RW directory itself along with its content: let's re-create an empty one.
+	if err := mp.createFolders(); err != nil {
+		return err
+	}
+	return nil
 }
 
 // Open opens the named file for reading.
@@ -141,4 +141,4 @@ func (mp *Mountpoint) Reset() error {
 //         return nil, err
 //     }
 //     return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname))
-// }
+// }

+ 0 - 1
fs/mount_linux.go

@@ -2,7 +2,6 @@ package fs
 
 import "syscall"
 
-
 func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
 	return syscall.Mount(source, target, fstype, flags, data)
 }

+ 6 - 8
fs/store.go

@@ -4,9 +4,9 @@ import (
 	"database/sql"
 	"errors"
 	"fmt"
-	"github.com/coopernurse/gorp"
 	"github.com/dotcloud/docker/future"
 	_ "github.com/mattn/go-sqlite3"
+	"github.com/shykes/gorp" //Forked to implement CreateTablesOpts
 	"io"
 	"os"
 	"path"
@@ -29,8 +29,6 @@ func New(root string) (*Store, error) {
 
 	if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {
 		return nil, err
-	} else if os.IsExist(err) {
-		isNewStore = false
 	}
 	db, err := sql.Open("sqlite3", path.Join(root, "db"))
 	if err != nil {
@@ -42,7 +40,7 @@ func New(root string) (*Store, error) {
 	orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root")
 	orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName")
 	if isNewStore {
-		if err := orm.CreateTables(); err != nil {
+		if err := orm.CreateTablesOpts(true); err != nil {
 			return nil, err
 		}
 	}
@@ -227,7 +225,7 @@ func (image *Image) Mountpoints() ([]*Mountpoint, error) {
 
 func (image *Image) Mount(root, rw string) (*Mountpoint, error) {
 	var mountpoint *Mountpoint
-	if mp, err := image.fetchMountpoint(root, rw); err != nil {
+	if mp, err := image.store.FetchMountpoint(root, rw); err != nil {
 		return nil, err
 	} else if mp == nil {
 		mountpoint, err = image.Mountpoint(root, rw)
@@ -345,8 +343,8 @@ func (mp *Mountpoint) Deregister() error {
 	return err
 }
 
-func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
-	res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=? and Root=? and Rw=?", image.Id, root, rw)
+func (store *Store) FetchMountpoint(root, rw string) (*Mountpoint, error) {
+	res, err := store.orm.Select(Mountpoint{}, "select * from mountpoints where Root=? and Rw=?", root, rw)
 	if err != nil {
 		return nil, err
 	} else if len(res) < 1 || res[0] == nil {
@@ -354,7 +352,7 @@ func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
 	}
 
 	mp := res[0].(*Mountpoint)
-	mp.Store = image.store
+	mp.Store = store
 	return mp, nil
 }
 

+ 1 - 1
fs/store_test.go

@@ -1,9 +1,9 @@
 package fs
 
 import (
+	"../fake"
 	"errors"
 	"fmt"
-	"../fake"
 	"io/ioutil"
 	"os"
 	"testing"

+ 0 - 362
image/image.go

@@ -1,362 +0,0 @@
-package image
-
-import (
-	"encoding/json"
-	"errors"
-	"github.com/dotcloud/docker/future"
-	"io"
-	"io/ioutil"
-	"os"
-	"path"
-	"path/filepath"
-	"regexp"
-	"sort"
-	"strings"
-	"time"
-)
-
-type Store struct {
-	*Index
-	Root   string
-	Layers *LayerStore
-}
-
-func New(root string) (*Store, error) {
-	abspath, err := filepath.Abs(root)
-	if err != nil {
-		return nil, err
-	}
-	if err := os.MkdirAll(abspath, 0700); err != nil && !os.IsExist(err) {
-		return nil, err
-	}
-	layers, err := NewLayerStore(path.Join(root, "layers"))
-	if err != nil {
-		return nil, err
-	}
-	if err := layers.Init(); err != nil {
-		return nil, err
-	}
-	return &Store{
-		Root:   abspath,
-		Index:  NewIndex(path.Join(root, "index.json")),
-		Layers: layers,
-	}, nil
-}
-
-// Import creates a new image from the contents of `archive` and registers it in the store as `name`.
-// If `parent` is not nil, it will registered as the parent of the new image.
-func (store *Store) Import(name string, archive io.Reader, parent *Image) (*Image, error) {
-	layer, err := store.Layers.AddLayer(archive)
-	if err != nil {
-		return nil, err
-	}
-	layers := []string{layer}
-	if parent != nil {
-		layers = append(layers, parent.Layers...)
-	}
-	var parentId string
-	if parent != nil {
-		parentId = parent.Id
-	}
-	return store.Create(name, parentId, layers...)
-}
-
-func (store *Store) Create(name string, source string, layers ...string) (*Image, error) {
-	image, err := NewImage(name, layers, source)
-	if err != nil {
-		return nil, err
-	}
-	if err := store.Index.Add(name, image); err != nil {
-		return nil, err
-	}
-	return image, nil
-}
-
-// Index
-
-type Index struct {
-	Path   string
-	ByName map[string]*History
-	ById   map[string]*Image
-}
-
-func NewIndex(path string) *Index {
-	return &Index{
-		Path:   path,
-		ByName: make(map[string]*History),
-		ById:   make(map[string]*Image),
-	}
-}
-
-func (index *Index) Exists(id string) bool {
-	_, exists := index.ById[id]
-	return exists
-}
-
-func (index *Index) Find(idOrName string) *Image {
-	// Load
-	if err := index.load(); err != nil {
-		return nil
-	}
-	// Lookup by ID
-	if image, exists := index.ById[idOrName]; exists {
-		return image
-	}
-	// Lookup by name
-	if history, exists := index.ByName[idOrName]; exists && history.Len() > 0 {
-		return (*history)[0]
-	}
-	return nil
-}
-
-func (index *Index) Add(name string, image *Image) error {
-	// Load
-	if err := index.load(); err != nil {
-		return err
-	}
-	if _, exists := index.ByName[name]; !exists {
-		index.ByName[name] = new(History)
-	} else {
-		// If this image is already the latest version, don't add it.
-		if (*index.ByName[name])[0].Id == image.Id {
-			return nil
-		}
-	}
-	index.ByName[name].Add(image)
-	index.ById[image.Id] = image
-	// Save
-	if err := index.save(); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (index *Index) Copy(srcNameOrId, dstName string) (*Image, error) {
-	if srcNameOrId == "" || dstName == "" {
-		return nil, errors.New("Illegal image name")
-	}
-	// Load
-	if err := index.load(); err != nil {
-		return nil, err
-	}
-	src := index.Find(srcNameOrId)
-	if src == nil {
-		return nil, errors.New("No such image: " + srcNameOrId)
-	}
-	dst, err := NewImage(dstName, src.Layers, src.Id)
-	if err != nil {
-		return nil, err
-	}
-	if err := index.Add(dstName, dst); err != nil {
-		return nil, err
-	}
-	// Save
-	if err := index.save(); err != nil {
-		return nil, err
-	}
-	return dst, nil
-}
-
-func (index *Index) Rename(oldName, newName string) error {
-	// Load
-	if err := index.load(); err != nil {
-		return err
-	}
-	if _, exists := index.ByName[oldName]; !exists {
-		return errors.New("Can't rename " + oldName + ": no such image.")
-	}
-	if _, exists := index.ByName[newName]; exists {
-		return errors.New("Can't rename to " + newName + ": name is already in use.")
-	}
-	index.ByName[newName] = index.ByName[oldName]
-	delete(index.ByName, oldName)
-	// Change the ID of all images, since they include the name
-	for _, image := range *index.ByName[newName] {
-		if id, err := generateImageId(newName, image.Layers); err != nil {
-			return err
-		} else {
-			oldId := image.Id
-			image.Id = id
-			index.ById[id] = image
-			delete(index.ById, oldId)
-		}
-	}
-	// Save
-	if err := index.save(); err != nil {
-		return err
-	}
-	return nil
-}
-
-// Delete deletes all images with the name `name`
-func (index *Index) Delete(name string) error {
-	// Load
-	if err := index.load(); err != nil {
-		return err
-	}
-	if _, exists := index.ByName[name]; !exists {
-		return errors.New("No such image: " + name)
-	}
-	// Remove from index lookup
-	for _, image := range *index.ByName[name] {
-		delete(index.ById, image.Id)
-	}
-	// Remove from name lookup
-	delete(index.ByName, name)
-	// Save
-	if err := index.save(); err != nil {
-		return err
-	}
-	return nil
-}
-
-// DeleteMatch deletes all images whose name matches `pattern`
-func (index *Index) DeleteMatch(pattern string) error {
-	// Load
-	if err := index.load(); err != nil {
-		return err
-	}
-	for name, history := range index.ByName {
-		if match, err := regexp.MatchString(pattern, name); err != nil {
-			return err
-		} else if match {
-			// Remove from index lookup
-			for _, image := range *history {
-				delete(index.ById, image.Id)
-			}
-			// Remove from name lookup
-			delete(index.ByName, name)
-		}
-	}
-	// Save
-	if err := index.save(); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (index *Index) Names() []string {
-	if err := index.load(); err != nil {
-		return []string{}
-	}
-	var names []string
-	for name := range index.ByName {
-		names = append(names, name)
-	}
-	sort.Strings(names)
-	return names
-}
-
-func (index *Index) load() error {
-	jsonData, err := ioutil.ReadFile(index.Path)
-	if err != nil {
-		if os.IsNotExist(err) {
-			return nil
-		}
-		return err
-	}
-	path := index.Path
-	if err := json.Unmarshal(jsonData, index); err != nil {
-		return err
-	}
-	index.Path = path
-	return nil
-}
-
-func (index *Index) save() error {
-	jsonData, err := json.Marshal(index)
-	if err != nil {
-		return err
-	}
-	if err := ioutil.WriteFile(index.Path, jsonData, 0600); err != nil {
-		return err
-	}
-	return nil
-}
-
-// History wraps an array of images so they can be sorted by date (most recent first)
-
-type History []*Image
-
-func (history *History) Len() int {
-	return len(*history)
-}
-
-func (history *History) Less(i, j int) bool {
-	images := *history
-	return images[j].Created.Before(images[i].Created)
-}
-
-func (history *History) Swap(i, j int) {
-	images := *history
-	tmp := images[i]
-	images[i] = images[j]
-	images[j] = tmp
-}
-
-func (history *History) Add(image *Image) {
-	*history = append(*history, image)
-	sort.Sort(history)
-}
-
-func (history *History) Del(id string) {
-	for idx, image := range *history {
-		if image.Id == id {
-			*history = append((*history)[:idx], (*history)[idx+1:]...)
-		}
-	}
-}
-
-type Image struct {
-	Id      string   // Globally unique identifier
-	Layers  []string // Absolute paths
-	Created time.Time
-	Parent  string
-}
-
-func (image *Image) IdParts() (string, string) {
-	if len(image.Id) < 8 {
-		return "", image.Id
-	}
-	hash := image.Id[len(image.Id)-8 : len(image.Id)]
-	name := image.Id[:len(image.Id)-9]
-	return name, hash
-}
-
-func (image *Image) IdIsFinal() bool {
-	return len(image.Layers) == 1
-}
-
-func generateImageId(name string, layers []string) (string, error) {
-	if len(layers) == 0 {
-		return "", errors.New("No layers provided.")
-	}
-	var hash string
-	if len(layers) == 1 {
-		hash = path.Base(layers[0])
-	} else {
-		var ids string
-		for _, layer := range layers {
-			ids += path.Base(layer)
-		}
-		if h, err := future.ComputeId(strings.NewReader(ids)); err != nil {
-			return "", err
-		} else {
-			hash = h
-		}
-	}
-	return name + ":" + hash, nil
-}
-
-func NewImage(name string, layers []string, parent string) (*Image, error) {
-	id, err := generateImageId(name, layers)
-	if err != nil {
-		return nil, err
-	}
-	return &Image{
-		Id:      id,
-		Layers:  layers,
-		Created: time.Now(),
-		Parent:  parent,
-	}, nil
-}

+ 3 - 6
rcli/http.go

@@ -1,13 +1,12 @@
 package rcli
 
 import (
+	"fmt"
 	"net/http"
 	"net/url"
 	"path"
-	"fmt"
 )
 
-
 // Use this key to encode an RPC call into an URL,
 // eg. domain.tld/path/to/method?q=get_user&q=gordon
 const ARG_URL_KEY = "q"
@@ -16,18 +15,16 @@ func URLToCall(u *url.URL) (method string, args []string) {
 	return path.Base(u.Path), u.Query()[ARG_URL_KEY]
 }
 
-
 func ListenAndServeHTTP(addr string, service Service) error {
 	return http.ListenAndServe(addr, http.HandlerFunc(
-		func (w http.ResponseWriter, r *http.Request) {
+		func(w http.ResponseWriter, r *http.Request) {
 			cmd, args := URLToCall(r.URL)
 			if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil {
-				fmt.Fprintf(w, "Error: " + err.Error() + "\n")
+				fmt.Fprintf(w, "Error: "+err.Error()+"\n")
 			}
 		}))
 }
 
-
 type AutoFlush struct {
 	http.ResponseWriter
 }

+ 5 - 7
rcli/tcp.go

@@ -1,13 +1,13 @@
 package rcli
 
 import (
+	"bufio"
+	"encoding/json"
+	"fmt"
 	"io"
 	"io/ioutil"
-	"net"
 	"log"
-	"fmt"
-	"encoding/json"
-	"bufio"
+	"net"
 )
 
 // Connect to a remote endpoint using protocol `proto` and address `addr`,
@@ -44,7 +44,7 @@ func ListenAndServe(proto, addr string, service Service) error {
 			go func() {
 				if err := Serve(conn, service); err != nil {
 					log.Printf("Error: " + err.Error() + "\n")
-					fmt.Fprintf(conn, "Error: " + err.Error() + "\n")
+					fmt.Fprintf(conn, "Error: "+err.Error()+"\n")
 				}
 				conn.Close()
 			}()
@@ -53,7 +53,6 @@ func ListenAndServe(proto, addr string, service Service) error {
 	return nil
 }
 
-
 // Parse an rcli call on a new connection, and pass it to `service` if it
 // is valid.
 func Serve(conn io.ReadWriter, service Service) error {
@@ -68,4 +67,3 @@ func Serve(conn io.ReadWriter, service Service) error {
 	}
 	return nil
 }
-

+ 4 - 6
rcli/types.go

@@ -8,13 +8,13 @@ package rcli
 // are the usual suspects.
 
 import (
+	"errors"
+	"flag"
 	"fmt"
 	"io"
-	"reflect"
-	"flag"
 	"log"
+	"reflect"
 	"strings"
-	"errors"
 )
 
 type Service interface {
@@ -25,7 +25,6 @@ type Service interface {
 type Cmd func(io.ReadCloser, io.Writer, ...string) error
 type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
 
-
 func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	if len(args) == 0 {
 		args = []string{"help"}
@@ -63,7 +62,7 @@ func getMethod(service Service, name string) Cmd {
 			return nil
 		}
 	}
-	methodName := "Cmd"+strings.ToUpper(name[:1])+strings.ToLower(name[1:])
+	methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
 	method, exists := reflect.TypeOf(service).MethodByName(methodName)
 	if !exists {
 		return nil
@@ -91,4 +90,3 @@ func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet
 	}
 	return flags
 }
-