Repository: coreos/fleet
Pull request #1430 (2016-02-11 16:24:10)

[WIP] test: added test scripts for the functional tests
subj

jonboulle: I don't understand the use case for this. can you help me understand what it provides over just a bash script that runs this? Since we still need etcd, systemd etc running on the host.
kayrus: It is still WIP. And this script will be executed on coreos.
tixxdz: You may also check if etcd is up.
jonboulle: What is the point of doing this in docker at all? Look how much stuff you necessarily need to bring in from the host.. 
kayrus: @jonboulle this will allow you to run tests directly on coreos. which already has etcd.
jonboulle: Why do you need docker for that? The functional tests run fine on coreos
without it

kayrus <notifications@github.com> schrieb am Fr., 12. Feb. 2016 11:50:

> @jonboulle <https://github.com/jonboulle> this will allow you to run
> tests directly on coreos. which already has etcd.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/coreos/fleet/pull/1430#issuecomment-183271009>.
>

kayrus: @jonboulle there is not go binary inside coreos.
antrik: To elaborate a bit: according to `functional/README.md`, the official way to run tests is inside a CoreOS VM, typically through Vagrant. However, as CoreOS doesn't provide a Go environment itself, the README suggest mapping it from outside the VM using Vagrant shared folders. But that's neither easy nor a self-contained reproducible environment. In our understanding, the canonical way to get a Go environment inside CoreOS is by running a golang container.
jonboulle: @antrik thanks for the context, very helpful. I discussed out of band with @mirkoboehm and we'll circulate notes shortly
kayrus: @jonboulle tests passed. should be squash and merge?
kayrus: @jonboulle done, please review once again.
jonboulle: For the sake of moving forward:
i) squash commit history into something sensible
ii) land this
iii) please follow up the comments I've just added

Patch:
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index a1dd1743..94904b71 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -31,18 +31,18 @@
 		},
 		{
 			"ImportPath": "github.com/coreos/go-systemd/activation",
-			"Comment": "v3-48-g1f2251651d19",
-			"Rev": "1f2251651d199b698a0be8c8bf2283178a2a97ed"
+			"Comment": "v2-35-gcf3cdf7",
+			"Rev": "cf3cdf77462baaad163ad2d5d1984b9c1b493701"
 		},
 		{
 			"ImportPath": "github.com/coreos/go-systemd/dbus",
-			"Comment": "v3-48-g1f2251651d19",
-			"Rev": "1f2251651d199b698a0be8c8bf2283178a2a97ed"
+			"Comment": "v2-35-gcf3cdf7",
+			"Rev": "cf3cdf77462baaad163ad2d5d1984b9c1b493701"
 		},
 		{
 			"ImportPath": "github.com/coreos/go-systemd/unit",
-			"Comment": "v3-48-g1f2251651d19",
-			"Rev": "1f2251651d199b698a0be8c8bf2283178a2a97ed"
+			"Comment": "v2-35-gcf3cdf7",
+			"Rev": "cf3cdf77462baaad163ad2d5d1984b9c1b493701"
 		},
 		{
 			"ImportPath": "github.com/docker/libcontainer/netlink",
@@ -51,8 +51,8 @@
 		},
 		{
 			"ImportPath": "github.com/godbus/dbus",
-			"Comment": "v2-18-g9bb9dbf",
-			"Rev": "9bb9dbf0e53309fa81d26e7230cbb6cff9373cad"
+			"Comment": "v2-6-ga5942de",
+			"Rev": "a5942dec6340eb0d57f43f2003c190ce06e43dea"
 		},
 		{
 			"ImportPath": "github.com/jonboulle/clockwork",
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go
index c8e85fcd..74b4fc10 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 // Package activation implements primitives for systemd socket activation.
 package activation
@@ -28,8 +30,10 @@ const (
 
 func Files(unsetEnv bool) []*os.File {
 	if unsetEnv {
-		defer os.Unsetenv("LISTEN_PID")
-		defer os.Unsetenv("LISTEN_FDS")
+		// there is no way to unset env in golang os package for now
+		// https://code.google.com/p/go/issues/detail?id=6423
+		defer os.Setenv("LISTEN_PID", "")
+		defer os.Setenv("LISTEN_FDS", "")
 	}
 
 	pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
@@ -42,7 +46,7 @@ func Files(unsetEnv bool) []*os.File {
 		return nil
 	}
 
-	files := make([]*os.File, 0, nfds)
+	var files []*os.File
 	for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
 		syscall.CloseOnExec(fd)
 		files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go
index a30cb893..fe5200a1 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2014 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package activation
 
@@ -26,11 +28,14 @@ import (
 // corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
 func Listeners(unsetEnv bool) ([]net.Listener, error) {
 	files := Files(unsetEnv)
-	listeners := make([]net.Listener, len(files))
+	listeners := make([]net.Listener, 0)
 
-	for i, f := range files {
-		if pc, err := net.FileListener(f); err == nil {
-			listeners[i] = pc
+	for i := 0; i < len(files); i++ {
+		if pc, err := net.FileListener(files[i]); err == nil {
+			listeners = append(listeners, pc)
+			continue
+		} else {
+			listeners = append(listeners, nil)
 		}
 	}
 	return listeners, nil
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go
index 48b2ca02..72f81fba 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2014 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package activation
 
@@ -26,11 +28,13 @@ import (
 // corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
 func PacketConns(unsetEnv bool) ([]net.PacketConn, error) {
 	files := Files(unsetEnv)
-	conns := make([]net.PacketConn, len(files))
-
-	for i, f := range files {
-		if pc, err := net.FilePacketConn(f); err == nil {
-			conns[i] = pc
+	conns := make([]net.PacketConn, 0)
+	for i := 0; i < len(files); i++ {
+		if pc, err := net.FilePacketConn(files[i]); err == nil {
+			conns = append(conns, pc)
+			continue
+		} else {
+			conns = append(conns, nil)
 		}
 	}
 	return conns, nil
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go
index a9d434e8..eb6ebd36 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 // Integration with the systemd D-Bus API.  See http://www.freedesktop.org/wiki/Software/systemd/dbus/
 package dbus
@@ -64,11 +66,11 @@ func PathBusEscape(path string) string {
 type Conn struct {
 	// sysconn/sysobj are only used to call dbus methods
 	sysconn *dbus.Conn
-	sysobj  dbus.BusObject
+	sysobj  *dbus.Object
 
 	// sigconn/sigobj are only used to receive dbus signals
 	sigconn *dbus.Conn
-	sigobj  dbus.BusObject
+	sigobj  *dbus.Object
 
 	jobListener struct {
 		jobs map[dbus.ObjectPath]chan<- string
@@ -84,49 +86,24 @@ type Conn struct {
 }
 
 // New establishes a connection to the system bus and authenticates.
-// Callers should call Close() when done with the connection.
 func New() (*Conn, error) {
-	return newConnection(func() (*dbus.Conn, error) {
-		return dbusAuthHelloConnection(dbus.SystemBusPrivate)
-	})
+	return newConnection(dbus.SystemBusPrivate)
 }
 
 // NewUserConnection establishes a connection to the session bus and
 // authenticates. This can be used to connect to systemd user instances.
-// Callers should call Close() when done with the connection.
 func NewUserConnection() (*Conn, error) {
-	return newConnection(func() (*dbus.Conn, error) {
-		return dbusAuthHelloConnection(dbus.SessionBusPrivate)
-	})
-}
-
-// NewSystemdConnection establishes a private, direct connection to systemd.
-// This can be used for communicating with systemd without a dbus daemon.
-// Callers should call Close() when done with the connection.
-func NewSystemdConnection() (*Conn, error) {
-	return newConnection(func() (*dbus.Conn, error) {
-		// We skip Hello when talking directly to systemd.
-		return dbusAuthConnection(func() (*dbus.Conn, error) {
-			return dbus.Dial("unix:path=/run/systemd/private")
-		})
-	})
-}
-
-// Close closes an established connection
-func (c *Conn) Close() {
-	c.sysconn.Close()
-	c.sigconn.Close()
+	return newConnection(dbus.SessionBusPrivate)
 }
 
 func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
-	sysconn, err := createBus()
+	sysconn, err := dbusConnection(createBus)
 	if err != nil {
 		return nil, err
 	}
 
-	sigconn, err := createBus()
+	sigconn, err := dbusConnection(createBus)
 	if err != nil {
-		sysconn.Close()
 		return nil, err
 	}
 
@@ -148,7 +125,7 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
 	return c, nil
 }
 
-func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
+func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
 	conn, err := createBus()
 	if err != nil {
 		return nil, err
@@ -165,16 +142,8 @@ func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error
 		return nil, err
 	}
 
-	return conn, nil
-}
-
-func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
-	conn, err := dbusAuthConnection(createBus)
+	err = conn.Hello()
 	if err != nil {
-		return nil, err
-	}
-
-	if err = conn.Hello(); err != nil {
 		conn.Close()
 		return nil, err
 	}
@@ -182,6 +151,6 @@ func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn,
 	return conn, nil
 }
 
-func systemdObject(conn *dbus.Conn) dbus.BusObject {
+func systemdObject(conn *dbus.Conn) *dbus.Object {
 	return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
 }
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go
index e1f9bf8d..4a3c0c1b 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package dbus
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go
index d683495e..e81ba1d9 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package dbus
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go
index f92e6fbe..45ad1fb3 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package dbus
 
 type set struct {
@@ -31,17 +17,17 @@ func (s *set) Contains(value string) (exists bool) {
 	return
 }
 
-func (s *set) Length() int {
+func (s *set) Length() (int) {
 	return len(s.data)
 }
 
 func (s *set) Values() (values []string) {
-	for val, _ := range s.data {
+	 for val, _ := range s.data {
 		values = append(values, val)
-	}
-	return
+	 }
+	 return
 }
 
-func newSet() *set {
-	return &set{make(map[string]bool)}
+func newSet() (*set) {
+	return &set{make(map[string] bool)}
 }
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go
index 905e5380..d692ece9 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package dbus
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go
index 5b408d58..296e6dda 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package dbus
 
 import (
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize.go
index 8a88162f..2b1522e2 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
@@ -24,23 +10,6 @@ import (
 	"unicode"
 )
 
-const (
-	// SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
-	// On typical systemd platforms (i.e. modern Linux), this will most
-	// commonly be 2048, so let's use that as a sanity check.
-	// Technically, we should probably pull this at runtime:
-	//    SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
-	// but this would introduce an (unfortunate) dependency on cgo
-	SYSTEMD_LINE_MAX = 2048
-
-	// characters that systemd considers indicate a newline
-	SYSTEMD_NEWLINE = "\r\n"
-)
-
-var (
-	ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
-)
-
 // Deserialize parses a systemd unit file into a list of UnitOption objects.
 func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
 	lexer, optchan, errchan := newLexer(f)
@@ -71,34 +40,17 @@ type lexer struct {
 
 func (l *lexer) lex() {
 	var err error
-	defer func() {
-		close(l.optchan)
-		close(l.errchan)
-	}()
 	next := l.lexNextSection
 	for next != nil {
-		if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
-			// systemd truncates lines longer than LINE_MAX
-			// https://bugs.freedesktop.org/show_bug.cgi?id=85308
-			// Rather than allowing this to pass silently, let's
-			// explicitly gate people from encountering this
-			line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
-			if err != nil {
-				l.errchan <- err
-				return
-			}
-			if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
-				l.errchan <- ErrLineTooLong
-				return
-			}
-		}
-
 		next, err = next()
 		if err != nil {
 			l.errchan <- err
-			return
+			break
 		}
 	}
+
+	close(l.optchan)
+	close(l.errchan)
 }
 
 type lexStep func() (lexStep, error)
@@ -114,7 +66,7 @@ func (l *lexer) lexSectionName() (lexStep, error) {
 
 func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
 	return func() (lexStep, error) {
-		garbage, _, err := l.toEOL()
+		garbage, err := l.toEOL()
 		if err != nil {
 			return nil, err
 		}
@@ -131,7 +83,7 @@ func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
 func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
 	return func() (lexStep, error) {
 		for {
-			line, _, err := l.toEOL()
+			line, err := l.toEOL()
 			if err != nil {
 				return nil, err
 			}
@@ -211,64 +163,49 @@ func (l *lexer) lexOptionNameFunc(section string) lexStep {
 		}
 
 		name := strings.TrimSpace(partial.String())
-		return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
+		return l.lexOptionValueFunc(section, name), nil
 	}
 }
 
-func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
+func (l *lexer) lexOptionValueFunc(section, name string) lexStep {
 	return func() (lexStep, error) {
+		var partial bytes.Buffer
+
 		for {
-			line, eof, err := l.toEOL()
+			line, err := l.toEOL()
 			if err != nil {
 				return nil, err
 			}
 
-			if len(bytes.TrimSpace(line)) == 0 {
-				break
-			}
-
-			partial.Write(line)
-
 			// lack of continuation means this value has been exhausted
 			idx := bytes.LastIndex(line, []byte{'\\'})
 			if idx == -1 || idx != (len(line)-1) {
+				partial.Write(line)
 				break
 			}
 
-			if !eof {
-				partial.WriteRune('\n')
-			}
-
-			return l.lexOptionValueFunc(section, name, partial), nil
+			partial.Write(line[0:idx])
+			partial.WriteRune(' ')
 		}
 
-		val := partial.String()
-		if strings.HasSuffix(val, "\n") {
-			// A newline was added to the end, so the file didn't end with a backslash.
-			// => Keep the newline
-			val = strings.TrimSpace(val) + "\n"
-		} else {
-			val = strings.TrimSpace(val)
-		}
+		val := strings.TrimSpace(partial.String())
 		l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
 
 		return l.lexNextSectionOrOptionFunc(section), nil
 	}
 }
 
-// toEOL reads until the end-of-line or end-of-file.
-// Returns (data, EOFfound, error)
-func (l *lexer) toEOL() ([]byte, bool, error) {
+func (l *lexer) toEOL() ([]byte, error) {
 	line, err := l.buf.ReadBytes('\n')
 	// ignore EOF here since it's roughly equivalent to EOL
 	if err != nil && err != io.EOF {
-		return nil, false, err
+		return nil, err
 	}
 
 	line = bytes.TrimSuffix(line, []byte{'\r'})
 	line = bytes.TrimSuffix(line, []byte{'\n'})
 
-	return line, err == io.EOF, nil
+	return line, nil
 }
 
 func isComment(r rune) bool {
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape.go
deleted file mode 100644
index 63b11726..00000000
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Implements systemd-escape [--unescape] [--path]
-
-package unit
-
-import (
-	"fmt"
-	"strconv"
-	"strings"
-)
-
-const (
-	allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
-)
-
-// If isPath is true:
-//   We remove redundant '/'s, the leading '/', and trailing '/'.
-//   If the result is empty, a '/' is inserted.
-//
-// We always:
-//  Replace the following characters with `\x%x`:
-//   Leading `.`
-//   `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]`
-//  Replace '/' with '-'.
-func escape(unescaped string, isPath bool) string {
-	e := []byte{}
-	inSlashes := false
-	start := true
-	for i := 0; i < len(unescaped); i++ {
-		c := unescaped[i]
-		if isPath {
-			if c == '/' {
-				inSlashes = true
-				continue
-			} else if inSlashes {
-				inSlashes = false
-				if !start {
-					e = append(e, '-')
-				}
-			}
-		}
-
-		if c == '/' {
-			e = append(e, '-')
-		} else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
-			e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
-		} else {
-			e = append(e, c)
-		}
-		start = false
-	}
-	if isPath && len(e) == 0 {
-		e = append(e, '-')
-	}
-	return string(e)
-}
-
-// If isPath is true:
-//   We always return a string beginning with '/'.
-//
-// We always:
-//  Replace '-' with '/'.
-//  Replace `\x%x` with the value represented in hex.
-func unescape(escaped string, isPath bool) string {
-	u := []byte{}
-	for i := 0; i < len(escaped); i++ {
-		c := escaped[i]
-		if c == '-' {
-			c = '/'
-		} else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
-			n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
-			if err == nil {
-				c = byte(n)
-				i += 3
-			}
-		}
-		u = append(u, c)
-	}
-	if isPath && (len(u) == 0 || u[0] != '/') {
-		u = append([]byte("/"), u...)
-	}
-	return string(u)
-}
-
-// UnitNameEscape escapes a string as `systemd-escape` would
-func UnitNameEscape(unescaped string) string {
-	return escape(unescaped, false)
-}
-
-// UnitNameUnescape unescapes a string as `systemd-escape --unescape` would
-func UnitNameUnescape(escaped string) string {
-	return unescape(escaped, false)
-}
-
-// UnitNamePathEscape escapes a string as `systemd-escape --path` would
-func UnitNamePathEscape(unescaped string) string {
-	return escape(unescaped, true)
-}
-
-// UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would
-func UnitNamePathUnescape(escaped string) string {
-	return unescape(escaped, true)
-}
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option.go
index e5d21e19..2b70fca3 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
@@ -24,10 +10,6 @@ type UnitOption struct {
 	Value   string
 }
 
-func NewUnitOption(section, name, value string) *UnitOption {
-	return &UnitOption{Section: section, Name: name, Value: value}
-}
-
 func (uo *UnitOption) String() string {
 	return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value)
 }
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize.go
index e07799ca..867ce600 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
@@ -19,9 +5,7 @@ import (
 	"io"
 )
 
-// Serialize encodes all of the given UnitOption objects into a
-// unit file. When serialized the options are sorted in their
-// supplied order but grouped by section.
+// Serialize encodes all of the given UnitOption objects into a unit file
 func Serialize(opts []*UnitOption) io.Reader {
 	var buf bytes.Buffer
 
@@ -29,30 +13,22 @@ func Serialize(opts []*UnitOption) io.Reader {
 		return &buf
 	}
 
-	// Index of sections -> ordered options
-	idx := map[string][]*UnitOption{}
-	// Separately preserve order in which sections were seen
-	sections := []string{}
-	for _, opt := range opts {
-		sec := opt.Section
-		if _, ok := idx[sec]; !ok {
-			sections = append(sections, sec)
-		}
-		idx[sec] = append(idx[sec], opt)
-	}
+	curSection := opts[0].Section
 
-	for i, sect := range sections {
-		writeSectionHeader(&buf, sect)
-		writeNewline(&buf)
+	writeSectionHeader(&buf, curSection)
+	writeNewline(&buf)
+
+	for _, opt := range opts {
+		if opt.Section != curSection {
+			curSection = opt.Section
 
-		opts := idx[sect]
-		for _, opt := range opts {
-			writeOption(&buf, opt)
 			writeNewline(&buf)
-		}
-		if i < len(sections)-1 {
+			writeSectionHeader(&buf, curSection)
 			writeNewline(&buf)
 		}
+
+		writeOption(&buf, opt)
+		writeNewline(&buf)
 	}
 
 	return &buf
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/godbus/dbus/CONTRIBUTING.md
deleted file mode 100644
index c88f9b2b..00000000
--- a/Godeps/_workspace/src/github.com/godbus/dbus/CONTRIBUTING.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# How to Contribute
-
-## Getting Started
-
-- Fork the repository on GitHub
-- Read the [README](README.markdown) for build and test instructions
-- Play with the project, submit bugs, submit patches!
-
-## Contribution Flow
-
-This is a rough outline of what a contributor's workflow looks like:
-
-- Create a topic branch from where you want to base your work (usually master).
-- Make commits of logical units.
-- Make sure your commit messages are in the proper format (see below).
-- Push your changes to a topic branch in your fork of the repository.
-- Make sure the tests pass, and add any new tests as appropriate.
-- Submit a pull request to the original repository.
-
-Thanks for your contributions!
-
-### Format of the Commit Message
-
-We follow a rough convention for commit messages that is designed to answer two
-questions: what changed and why. The subject line should feature the what and
-the body of the commit should describe the why.
-
-```
-scripts: add the test-cluster command
-
-this uses tmux to setup a test cluster that you can easily kill and
-start for debugging.
-
-Fixes #38
-```
-
-The format can be described more formally as follows:
-
-```
-<subsystem>: <what changed>
-<BLANK LINE>
-<why this change was made>
-<BLANK LINE>
-<footer>
-```
-
-The first line is the subject and should be no longer than 70 characters, the
-second line is always blank, and other lines should be wrapped at 80 characters.
-This allows the message to be easier to read on GitHub as well as in various
-git tools.
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/MAINTAINERS b/Godeps/_workspace/src/github.com/godbus/dbus/MAINTAINERS
deleted file mode 100644
index e8968ece..00000000
--- a/Godeps/_workspace/src/github.com/godbus/dbus/MAINTAINERS
+++ /dev/null
@@ -1,2 +0,0 @@
-Brandon Philips <brandon@ifup.org> (@philips)
-Brian Waldon <brian@waldon.cc> (@bcwaldon)
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/README.markdown b/Godeps/_workspace/src/github.com/godbus/dbus/README.markdown
index 0a6e7e5b..3ab21166 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/README.markdown
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/README.markdown
@@ -27,9 +27,6 @@ The complete package documentation and some simple examples are available at
 [_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
 gives a short overview over the basic usage. 
 
-#### Projects using godbus
-- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
-
 Please note that the API is considered unstable for now and may change without
 further notice.
 
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/call.go b/Godeps/_workspace/src/github.com/godbus/dbus/call.go
index ba6e73f6..1d2fbc7e 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/call.go
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/call.go
@@ -2,6 +2,7 @@ package dbus
 
 import (
 	"errors"
+	"strings"
 )
 
 // Call represents a pending or completed method call.
@@ -34,3 +35,113 @@ func (c *Call) Store(retvalues ...interface{}) error {
 
 	return Store(c.Body, retvalues...)
 }
+
+// Object represents a remote object on which methods can be invoked.
+type Object struct {
+	conn *Conn
+	dest string
+	path ObjectPath
+}
+
+// Call calls a method with (*Object).Go and waits for its reply.
+func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
+	return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
+}
+
+// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
+// object. The property name must be given in interface.member notation.
+func (o *Object) GetProperty(p string) (Variant, error) {
+	idx := strings.LastIndex(p, ".")
+	if idx == -1 || idx+1 == len(p) {
+		return Variant{}, errors.New("dbus: invalid property " + p)
+	}
+
+	iface := p[:idx]
+	prop := p[idx+1:]
+
+	result := Variant{}
+	err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
+
+	if err != nil {
+		return Variant{}, err
+	}
+
+	return result, nil
+}
+
+// Go calls a method with the given arguments asynchronously. It returns a
+// Call structure representing this method call. The passed channel will
+// return the same value once the call is done. If ch is nil, a new channel
+// will be allocated. Otherwise, ch has to be buffered or Go will panic.
+//
+// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
+// is returned of which only the Err member is valid.
+//
+// If the method parameter contains a dot ('.'), the part before the last dot
+// specifies the interface on which the method is called.
+func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
+	iface := ""
+	i := strings.LastIndex(method, ".")
+	if i != -1 {
+		iface = method[:i]
+	}
+	method = method[i+1:]
+	msg := new(Message)
+	msg.Type = TypeMethodCall
+	msg.serial = o.conn.getSerial()
+	msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
+	msg.Headers = make(map[HeaderField]Variant)
+	msg.Headers[FieldPath] = MakeVariant(o.path)
+	msg.Headers[FieldDestination] = MakeVariant(o.dest)
+	msg.Headers[FieldMember] = MakeVariant(method)
+	if iface != "" {
+		msg.Headers[FieldInterface] = MakeVariant(iface)
+	}
+	msg.Body = args
+	if len(args) > 0 {
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
+	}
+	if msg.Flags&FlagNoReplyExpected == 0 {
+		if ch == nil {
+			ch = make(chan *Call, 10)
+		} else if cap(ch) == 0 {
+			panic("dbus: unbuffered channel passed to (*Object).Go")
+		}
+		call := &Call{
+			Destination: o.dest,
+			Path:        o.path,
+			Method:      method,
+			Args:        args,
+			Done:        ch,
+		}
+		o.conn.callsLck.Lock()
+		o.conn.calls[msg.serial] = call
+		o.conn.callsLck.Unlock()
+		o.conn.outLck.RLock()
+		if o.conn.closed {
+			call.Err = ErrClosed
+			call.Done <- call
+		} else {
+			o.conn.out <- msg
+		}
+		o.conn.outLck.RUnlock()
+		return call
+	}
+	o.conn.outLck.RLock()
+	defer o.conn.outLck.RUnlock()
+	if o.conn.closed {
+		return &Call{Err: ErrClosed}
+	}
+	o.conn.out <- msg
+	return &Call{Err: nil}
+}
+
+// Destination returns the destination that calls on o are sent to.
+func (o *Object) Destination() string {
+	return o.dest
+}
+
+// Path returns the path that calls on o are sent to.
+func (o *Object) Path() ObjectPath {
+	return o.path
+}
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/conn.go b/Godeps/_workspace/src/github.com/godbus/dbus/conn.go
index d68d997e..18027a09 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/conn.go
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/conn.go
@@ -32,7 +32,7 @@ var ErrClosed = errors.New("dbus: connection closed by user")
 type Conn struct {
 	transport
 
-	busObj BusObject
+	busObj *Object
 	unixFD bool
 	uuid   string
 
@@ -46,7 +46,7 @@ type Conn struct {
 	calls    map[uint32]*Call
 	callsLck sync.RWMutex
 
-	handlers    map[ObjectPath]map[string]exportWithMapping
+	handlers    map[ObjectPath]map[string]interface{}
 	handlersLck sync.RWMutex
 
 	out    chan *Message
@@ -157,7 +157,7 @@ func newConn(tr transport) (*Conn, error) {
 	conn.transport = tr
 	conn.calls = make(map[uint32]*Call)
 	conn.out = make(chan *Message, 10)
-	conn.handlers = make(map[ObjectPath]map[string]exportWithMapping)
+	conn.handlers = make(map[ObjectPath]map[string]interface{})
 	conn.nextSerial = 1
 	conn.serialUsed = map[uint32]bool{0: true}
 	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
@@ -166,7 +166,7 @@ func newConn(tr transport) (*Conn, error) {
 
 // BusObject returns the object owned by the bus daemon which handles
 // administrative requests.
-func (conn *Conn) BusObject() BusObject {
+func (conn *Conn) BusObject() *Object {
 	return conn.busObj
 }
 
@@ -303,33 +303,18 @@ func (conn *Conn) inWorker() {
 				// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
 				// sender is optional for signals.
 				sender, _ := msg.Headers[FieldSender].value.(string)
-				if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
-					if member == "NameLost" {
-						// If we lost the name on the bus, remove it from our
-						// tracking list.
-						name, ok := msg.Body[0].(string)
-						if !ok {
-							panic("Unable to read the lost name")
+				if iface == "org.freedesktop.DBus" && member == "NameLost" &&
+					sender == "org.freedesktop.DBus" {
+
+					name, _ := msg.Body[0].(string)
+					conn.namesLck.Lock()
+					for i, v := range conn.names {
+						if v == name {
+							copy(conn.names[i:], conn.names[i+1:])
+							conn.names = conn.names[:len(conn.names)-1]
 						}
-						conn.namesLck.Lock()
-						for i, v := range conn.names {
-							if v == name {
-								conn.names = append(conn.names[:i],
-									conn.names[i+1:]...)
-							}
-						}
-						conn.namesLck.Unlock()
-					} else if member == "NameAcquired" {
-						// If we acquired the name on the bus, add it to our
-						// tracking list.
-						name, ok := msg.Body[0].(string)
-						if !ok {
-							panic("Unable to read the acquired name")
-						}
-						conn.namesLck.Lock()
-						conn.names = append(conn.names, name)
-						conn.namesLck.Unlock()
 					}
+					conn.namesLck.Unlock()
 				}
 				signal := &Signal{
 					Sender: sender,
@@ -375,7 +360,7 @@ func (conn *Conn) Names() []string {
 }
 
 // Object returns the object identified by the given destination name and path.
-func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
+func (conn *Conn) Object(dest string, path ObjectPath) *Object {
 	return &Object{conn, dest, path}
 }
 
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/export.go b/Godeps/_workspace/src/github.com/godbus/dbus/export.go
index c6440a74..f7c0a5c0 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/export.go
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/export.go
@@ -2,9 +2,9 @@ package dbus
 
 import (
 	"errors"
-	"fmt"
 	"reflect"
 	"strings"
+	"unicode"
 )
 
 var (
@@ -22,52 +22,16 @@ var (
 	}
 )
 
-// exportWithMapping represents an exported struct along with a method name
-// mapping to allow for exporting lower-case methods, etc.
-type exportWithMapping struct {
-	export interface{}
-
-	// Method name mapping; key -> struct method, value -> dbus method.
-	mapping map[string]string
-
-	// Whether or not this export is for the entire subtree
-	includeSubtree bool
-}
-
 // Sender is a type which can be used in exported methods to receive the message
 // sender.
 type Sender string
 
-func exportedMethod(export exportWithMapping, name string) reflect.Value {
-	if export.export == nil {
+func exportedMethod(v interface{}, name string) reflect.Value {
+	if v == nil {
 		return reflect.Value{}
 	}
-
-	// If a mapping was included in the export, check the map to see if we
-	// should be looking for a different method in the export.
-	if export.mapping != nil {
-		for key, value := range export.mapping {
-			if value == name {
-				name = key
-				break
-			}
-
-			// Catch the case where a method is aliased but the client is calling
-			// the original, e.g. the "Foo" method was exported mapped to
-			// "foo," and dbus client called the original "Foo."
-			if key == name {
-				return reflect.Value{}
-			}
-		}
-	}
-
-	value := reflect.ValueOf(export.export)
-	m := value.MethodByName(name)
-
-	// Catch the case of attempting to call an unexported method
-	method, ok := value.Type().MethodByName(name)
-
-	if !m.IsValid() || !ok || method.PkgPath != "" {
+	m := reflect.ValueOf(v).MethodByName(name)
+	if !m.IsValid() {
 		return reflect.Value{}
 	}
 	t := m.Type()
@@ -79,42 +43,6 @@ func exportedMethod(export exportWithMapping, name string) reflect.Value {
 	return m
 }
 
-// searchHandlers will look through all registered handlers looking for one
-// to handle the given path. If a verbatim one isn't found, it will check for
-// a subtree registration for the path as well.
-func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
-	conn.handlersLck.RLock()
-	defer conn.handlersLck.RUnlock()
-
-	handlers, ok := conn.handlers[path]
-	if ok {
-		return handlers, ok
-	}
-
-	// If handlers weren't found for this exact path, look for a matching subtree
-	// registration
-	handlers = make(map[string]exportWithMapping)
-	path = path[:strings.LastIndex(string(path), "/")]
-	for len(path) > 0 {
-		var subtreeHandlers map[string]exportWithMapping
-		subtreeHandlers, ok = conn.handlers[path]
-		if ok {
-			for iface, handler := range subtreeHandlers {
-				// Only include this handler if it registered for the subtree
-				if handler.includeSubtree {
-					handlers[iface] = handler
-				}
-			}
-
-			break
-		}
-
-		path = path[:strings.LastIndex(string(path), "/")]
-	}
-
-	return handlers, ok
-}
-
 // handleCall handles the given method call (i.e. looks if it's one of the
 // pre-implemented ones and searches for a corresponding handler if not).
 func (conn *Conn) handleCall(msg *Message) {
@@ -134,35 +62,40 @@ func (conn *Conn) handleCall(msg *Message) {
 		}
 		return
 	}
-	if len(name) == 0 {
+	if len(name) == 0 || unicode.IsLower([]rune(name)[0]) {
 		conn.sendError(errmsgUnknownMethod, sender, serial)
 	}
-
-	// Find the exported handler (if any) for this path
-	handlers, ok := conn.searchHandlers(path)
-	if !ok {
-		conn.sendError(errmsgNoObject, sender, serial)
-		return
-	}
-
 	var m reflect.Value
 	if hasIface {
-		iface := handlers[ifaceName]
+		conn.handlersLck.RLock()
+		obj, ok := conn.handlers[path]
+		if !ok {
+			conn.sendError(errmsgNoObject, sender, serial)
+			conn.handlersLck.RUnlock()
+			return
+		}
+		iface := obj[ifaceName]
+		conn.handlersLck.RUnlock()
 		m = exportedMethod(iface, name)
 	} else {
-		for _, v := range handlers {
+		conn.handlersLck.RLock()
+		if _, ok := conn.handlers[path]; !ok {
+			conn.sendError(errmsgNoObject, sender, serial)
+			conn.handlersLck.RUnlock()
+			return
+		}
+		for _, v := range conn.handlers[path] {
 			m = exportedMethod(v, name)
 			if m.IsValid() {
 				break
 			}
 		}
+		conn.handlersLck.RUnlock()
 	}
-
 	if !m.IsValid() {
 		conn.sendError(errmsgUnknownMethod, sender, serial)
 		return
 	}
-
 	t := m.Type()
 	vs := msg.Body
 	pointers := make([]interface{}, t.NumIn())
@@ -173,36 +106,27 @@ func (conn *Conn) handleCall(msg *Message) {
 		pointers[i] = val.Interface()
 		if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
 			val.Elem().SetString(sender)
-		} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
-			val.Elem().Set(reflect.ValueOf(*msg))
 		} else {
 			decode = append(decode, pointers[i])
 		}
 	}
-
 	if len(decode) != len(vs) {
 		conn.sendError(errmsgInvalidArg, sender, serial)
 		return
 	}
-
 	if err := Store(vs, decode...); err != nil {
 		conn.sendError(errmsgInvalidArg, sender, serial)
 		return
 	}
-
-	// Extract parameters
 	params := make([]reflect.Value, len(pointers))
 	for i := 0; i < len(pointers); i++ {
 		params[i] = reflect.ValueOf(pointers[i]).Elem()
 	}
-
-	// Call method
 	ret := m.Call(params)
 	if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
 		conn.sendError(*em, sender, serial)
 		return
 	}
-
 	if msg.Flags&FlagNoReplyExpected == 0 {
 		reply := new(Message)
 		reply.Type = TypeMethodReply
@@ -279,10 +203,6 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
 // contribute to the dbus signature of the method (i.e. the method is exposed
 // as if the parameters of type Sender were not there).
 //
-// Similarly, any parameters with the type Message are set to the raw message
-// received on the bus. Again, parameters of this type do not contribute to the
-// dbus signature of the method.
-//
 // Every method call is executed in a new goroutine, so the method may be called
 // in multiple goroutines at once.
 //
@@ -294,51 +214,11 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
 //
 // Export returns an error if path is not a valid path name.
 func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
-	return conn.ExportWithMap(v, nil, path, iface)
-}
-
-// ExportWithMap works exactly like Export but provides the ability to remap
-// method names (e.g. export a lower-case method).
-//
-// The keys in the map are the real method names (exported on the struct), and
-// the values are the method names to be exported on DBus.
-func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
-	return conn.exportWithMap(v, mapping, path, iface, false)
-}
-
-// ExportSubtree works exactly like Export but registers the given value for
-// an entire subtree rather under the root path provided.
-//
-// In order to make this useful, one parameter in each of the value's exported
-// methods should be a Message, in which case it will contain the raw message
-// (allowing one to get access to the path that caused the method to be called).
-//
-// Note that more specific export paths take precedence over less specific. For
-// example, a method call using the ObjectPath /foo/bar/baz will call a method
-// exported on /foo/bar before a method exported on /foo.
-func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
-	return conn.ExportSubtreeWithMap(v, nil, path, iface)
-}
-
-// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
-// ability to remap method names (e.g. export a lower-case method).
-//
-// The keys in the map are the real method names (exported on the struct), and
-// the values are the method names to be exported on DBus.
-func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
-	return conn.exportWithMap(v, mapping, path, iface, true)
-}
-
-// exportWithMap is the worker function for all exports/registrations.
-func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
 	if !path.IsValid() {
-		return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
+		return errors.New("dbus: invalid path name")
 	}
-
 	conn.handlersLck.Lock()
 	defer conn.handlersLck.Unlock()
-
-	// Remove a previous export if the interface is nil
 	if v == nil {
 		if _, ok := conn.handlers[path]; ok {
 			delete(conn.handlers[path], iface)
@@ -346,39 +226,50 @@ func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path O
 				delete(conn.handlers, path)
 			}
 		}
-
 		return nil
 	}
-
-	// If this is the first handler for this path, make a new map to hold all
-	// handlers for this path.
 	if _, ok := conn.handlers[path]; !ok {
-		conn.handlers[path] = make(map[string]exportWithMapping)
+		conn.handlers[path] = make(map[string]interface{})
 	}
-
-	// Finally, save this handler
-	conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree}
-
+	conn.handlers[path][iface] = v
 	return nil
 }
 
-// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
+// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this
+// method to release a name (see below).
 func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
 	var r uint32
 	err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
 	if err != nil {
 		return 0, err
 	}
+	if r == uint32(ReleaseNameReplyReleased) {
+		conn.namesLck.Lock()
+		for i, v := range conn.names {
+			if v == name {
+				copy(conn.names[i:], conn.names[i+1:])
+				conn.names = conn.names[:len(conn.names)-1]
+			}
+		}
+		conn.namesLck.Unlock()
+	}
 	return ReleaseNameReply(r), nil
 }
 
-// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
+// RequestName calls org.freedesktop.DBus.RequestName. You should use only this
+// method to request a name because package dbus needs to keep track of all
+// names that the connection has.
 func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
 	var r uint32
 	err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
 	if err != nil {
 		return 0, err
 	}
+	if r == uint32(RequestNameReplyPrimaryOwner) {
+		conn.namesLck.Lock()
+		conn.names = append(conn.names, name)
+		conn.namesLck.Unlock()
+	}
 	return RequestNameReply(r), nil
 }
 
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/introspect/call.go b/Godeps/_workspace/src/github.com/godbus/dbus/introspect/call.go
index a379a526..48df0791 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/introspect/call.go
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/introspect/call.go
@@ -8,7 +8,7 @@ import (
 
 // Call calls org.freedesktop.Introspectable.Introspect on a remote object
 // and returns the introspection data.
-func Call(o dbus.BusObject) (*Node, error) {
+func Call(o *dbus.Object) (*Node, error) {
 	var xmldata string
 	var node Node
 
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/introspect/introspectable.go b/Godeps/_workspace/src/github.com/godbus/dbus/introspect/introspectable.go
index 21d3152d..3765c37c 100644
--- a/Godeps/_workspace/src/github.com/godbus/dbus/introspect/introspectable.go
+++ b/Godeps/_workspace/src/github.com/godbus/dbus/introspect/introspectable.go
@@ -59,8 +59,7 @@ func Methods(v interface{}) []Method {
 		m.Name = t.Method(i).Name
 		m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
 		for j := 1; j < mt.NumIn(); j++ {
-			if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() &&
-				mt.In(j) != reflect.TypeOf((*dbus.Message)(nil)).Elem() {
+			if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() {
 				arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
 				m.Args = append(m.Args, arg)
 			}
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/object.go b/Godeps/_workspace/src/github.com/godbus/dbus/object.go
deleted file mode 100644
index 7ef45da4..00000000
--- a/Godeps/_workspace/src/github.com/godbus/dbus/object.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package dbus
-
-import (
-	"errors"
-	"strings"
-)
-
-// BusObject is the interface of a remote object on which methods can be
-// invoked.
-type BusObject interface {
-	Call(method string, flags Flags, args ...interface{}) *Call
-	Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
-	GetProperty(p string) (Variant, error)
-	Destination() string
-	Path() ObjectPath
-}
-
-// Object represents a remote object on which methods can be invoked.
-type Object struct {
-	conn *Conn
-	dest string
-	path ObjectPath
-}
-
-// Call calls a method with (*Object).Go and waits for its reply.
-func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
-	return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
-}
-
-// Go calls a method with the given arguments asynchronously. It returns a
-// Call structure representing this method call. The passed channel will
-// return the same value once the call is done. If ch is nil, a new channel
-// will be allocated. Otherwise, ch has to be buffered or Go will panic.
-//
-// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
-// is returned of which only the Err member is valid.
-//
-// If the method parameter contains a dot ('.'), the part before the last dot
-// specifies the interface on which the method is called.
-func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
-	iface := ""
-	i := strings.LastIndex(method, ".")
-	if i != -1 {
-		iface = method[:i]
-	}
-	method = method[i+1:]
-	msg := new(Message)
-	msg.Type = TypeMethodCall
-	msg.serial = o.conn.getSerial()
-	msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
-	msg.Headers = make(map[HeaderField]Variant)
-	msg.Headers[FieldPath] = MakeVariant(o.path)
-	msg.Headers[FieldDestination] = MakeVariant(o.dest)
-	msg.Headers[FieldMember] = MakeVariant(method)
-	if iface != "" {
-		msg.Headers[FieldInterface] = MakeVariant(iface)
-	}
-	msg.Body = args
-	if len(args) > 0 {
-		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
-	}
-	if msg.Flags&FlagNoReplyExpected == 0 {
-		if ch == nil {
-			ch = make(chan *Call, 10)
-		} else if cap(ch) == 0 {
-			panic("dbus: unbuffered channel passed to (*Object).Go")
-		}
-		call := &Call{
-			Destination: o.dest,
-			Path:        o.path,
-			Method:      method,
-			Args:        args,
-			Done:        ch,
-		}
-		o.conn.callsLck.Lock()
-		o.conn.calls[msg.serial] = call
-		o.conn.callsLck.Unlock()
-		o.conn.outLck.RLock()
-		if o.conn.closed {
-			call.Err = ErrClosed
-			call.Done <- call
-		} else {
-			o.conn.out <- msg
-		}
-		o.conn.outLck.RUnlock()
-		return call
-	}
-	o.conn.outLck.RLock()
-	defer o.conn.outLck.RUnlock()
-	if o.conn.closed {
-		return &Call{Err: ErrClosed}
-	}
-	o.conn.out <- msg
-	return &Call{Err: nil}
-}
-
-// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
-// object. The property name must be given in interface.member notation.
-func (o *Object) GetProperty(p string) (Variant, error) {
-	idx := strings.LastIndex(p, ".")
-	if idx == -1 || idx+1 == len(p) {
-		return Variant{}, errors.New("dbus: invalid property " + p)
-	}
-
-	iface := p[:idx]
-	prop := p[idx+1:]
-
-	result := Variant{}
-	err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
-
-	if err != nil {
-		return Variant{}, err
-	}
-
-	return result, nil
-}
-
-// Destination returns the destination that calls on o are sent to.
-func (o *Object) Destination() string {
-	return o.dest
-}
-
-// Path returns the path that calls on o are sent to.
-func (o *Object) Path() ObjectPath {
-	return o.path
-}
diff --git a/api/units.go b/api/units.go
index 67b4a953..2765495d 100644
--- a/api/units.go
+++ b/api/units.go
@@ -28,8 +28,6 @@ import (
 	"github.com/coreos/fleet/pkg"
 	"github.com/coreos/fleet/schema"
 	"github.com/coreos/fleet/unit"
-
-	gsunit "github.com/coreos/fleet/Godeps/_workspace/src/github.com/coreos/go-systemd/unit"
 )
 
 func wireUpUnitsResource(mux *http.ServeMux, prefix string, tokenLimit int, cAPI client.API) {
@@ -192,13 +190,6 @@ func ValidateName(name string) error {
 // with a set of options, only the first is returned.
 func ValidateOptions(opts []*schema.UnitOption) error {
 	uf := schema.MapSchemaUnitOptionsToUnitFile(opts)
-	// Sanity check using go-systemd's deserializer, which will do things
-	// like check for excessive line lengths
-	_, err := gsunit.Deserialize(gsunit.Serialize(uf.Options))
-	if err != nil {
-		return err
-	}
-
 	j := &job.Job{
 		Unit: *uf,
 	}
diff --git a/build b/build
index 7ac5497d..3c32a330 100755
--- a/build
+++ b/build
@@ -1,41 +1,24 @@
 #!/bin/bash -e
 
-# The -X format changed from go1.4 -> go1.5
-function go_linker_dashX {
-	local version=$(go version)
-	local regex="go([0-9]+).([0-9]+)."
-	if [[ $version =~ $regex ]]; then
-		if [ ${BASH_REMATCH[1]} -eq "1" -a ${BASH_REMATCH[2]} -le "4" ]; then
-			echo "$1 \"$2\""
-		else
-			echo "$1=$2"
-		fi
-	else
-		echo "could not determine Go version"
-		exit 1
-	fi
-}
+CDIR=$(cd `dirname $0` && pwd)
+cd $CDIR
 
 ORG_PATH="github.com/coreos"
 REPO_PATH="${ORG_PATH}/fleet"
 VERSION=$(git describe --dirty)
-GLDFLAGS="-X $(go_linker_dashX github.com/coreos/fleet/version.Version ${VERSION})"
+
+source build-env
 
 if [ ! -h gopath/src/${REPO_PATH} ]; then
-	mkdir -p gopath/src/${ORG_PATH}
-	ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
+  mkdir -p gopath/src/${ORG_PATH}
+  ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
 fi
 
-export GOBIN=${PWD}/bin
-export GOPATH=${PWD}/gopath
-
-eval $(go env)
-
 if [ ${GOOS} = "linux" ]; then
-	echo "Building fleetd..."
-	CGO_ENABLED=0 go build -o bin/fleetd -a -installsuffix netgo -ldflags "${GLDFLAGS}" ${REPO_PATH}/fleetd
+  echo "Building fleetd..."
+  CGO_ENABLED=0 go build -o bin/fleetd -a -installsuffix netgo -ldflags "${GLDFLAGS}" ${REPO_PATH}/fleetd
 else
-	echo "Not on Linux - skipping fleetd build"
+  echo "Not on Linux - skipping fleetd build"
 fi
 
 echo "Building fleetctl..."
diff --git a/build-docker b/build-docker
index f120c2f7..cfc92638 100755
--- a/build-docker
+++ b/build-docker
@@ -1,3 +1,5 @@
 #!/bin/bash -e
 
-docker run --rm -v $PWD:/opt/fleet -u $(id -u):$(id -g) google/golang:1.4 /bin/bash -c "cd /opt/fleet && ./build"
+CDIR=$(cd `dirname $0` && pwd)
+
+docker run --rm -v $CDIR:/opt/fleet -u $(id -u):$(id -g) google/golang:1.4 /bin/bash -c "cd /opt/fleet && ./build"
diff --git a/build-env b/build-env
new file mode 100755
index 00000000..9fa387f3
--- /dev/null
+++ b/build-env
@@ -0,0 +1,23 @@
+# The -X format changed from go1.4 -> go1.5
+function go_linker_dashX {
+  local version=$(go version)
+  local regex="go([0-9]+).([0-9]+)."
+  if [[ $version =~ $regex ]]; then
+    if [ ${BASH_REMATCH[1]} -eq "1" -a ${BASH_REMATCH[2]} -le "4" ]; then
+      echo "$1 \"$2\""
+    else
+      echo "$1=$2"
+    fi
+  else
+    echo "could not determine Go version"
+    exit 1
+  fi
+}
+
+export GOBIN=${PWD}/bin
+export GOPATH=${PWD}/gopath
+export GLDFLAGS="-X $(go_linker_dashX github.com/coreos/fleet/version.Version ${VERSION})"
+eval $(go env)
+export PATH="${GOROOT}/bin:${PATH}"
+export FLEETD_BIN="$(pwd)/bin/fleetd"
+export FLEETCTL_BIN="$(pwd)/bin/fleetctl"
diff --git a/functional/.gitignore b/functional/.gitignore
new file mode 100644
index 00000000..a977916f
--- /dev/null
+++ b/functional/.gitignore
@@ -0,0 +1,1 @@
+.vagrant/
diff --git a/functional/README.md b/functional/README.md
index 815400d0..b36aeb72 100644
--- a/functional/README.md
+++ b/functional/README.md
@@ -2,52 +2,101 @@
 
 This functional test suite deploys a fleet cluster using nspawn containers, and asserts fleet is functioning properly.
 
-It shares an instance of etcd deployed on the host machine with each of the nspawn containers.
+It shares an instance of etcd deployed on the host machine with each of the nspawn containers which use `172.18.0.1/16` network, so please make sure this network does not intersect with others.
 
-It's recommended to run this in a virtual machine environment on CoreOS (e.g. using coreos-vagrant). The only dependency for the tests not provided on the CoreOS image is `go`.
+It's recommended to run this in a virtual machine environment on CoreOS (e.g. using [Vagrant][test-in-vagrant]).
 
-The caller must do three things before running the tests:
+Since the tests utilize [`systemd-nspawn`][systemd-nspawn], this needs to be invoked as sudo/root.
 
-1. Ensure an ssh-agent is running and the functional-testing identity is loaded. The `SSH_AUTH_SOCK` environment variable must be set.
+If the tests are aborted partway through, it's currently possible for them to leave residual state as a result of the `systemd-nspawn` operations. This can be cleaned up using the `clean.sh` script.
 
-```
-$ ssh-agent
-$ ssh-add fleet/functional/fixtures/id_rsa
-$ echo $SSH_AUTH_SOCK
-/tmp/ssh-kwmtTOsL7978/agent.7978
-```
-2. Ensure the `FLEETD_BIN` and `FLEETCTL_BIN` environment variables point to the respective fleetd and fleetctl binaries that should be used to drive the actual tests.
+### Run tests in Vagrant
+
+The recommended way to run the tests is to use the provided Vagrantfile, which will set up a single CoreOS instance with a one-member etcd cluster (configuration is applied using `user-data` [Cloud-Config][cloud-config] file located in this directory).
+To do so, simply run the following commands on a system with Vagrant installed (see [Vagrant configuration][configure-vagrant] section of this doc)
 
+```sh
+$ git clone https://github.com/coreos/fleet
+$ cd fleet/functional
+$ ./run-in-vagrant
 ```
-$ export FLEETD_BIN=/path/to/fleetd
-$ export FLEETCTL_BIN=/path/to/fleetctl
+
+Vagrant's provision step includes go binaries download using `functional/provision/install_go.sh` script.
+
+### Run tests inside other CoreOS platforms (QEMU/BareMetal/libvirt/etc)
+
+It's also possible to run the tests on CoreOS on other platforms. The following commands should be run *inside* the CoreOS instance.
+
+```sh
+$ git clone https://github.com/coreos/fleet
 ```
 
-3. Make sure etcd is running on the host system.
+If you didn't configure etcd2 daemon yet, just run this script:
 
+```sh
+$ sudo fleet/functional/start_etcd
 ```
-$ systemctl start etcd
+
+It will configure and start a one-member etcd cluster.
+
+Then run the functional tests (script will download and unpack golang into home directory):
+
+```sh
+$ sudo fleet/functional/test
 ```
 
-Then the tests can be run with:
+When `fleet/functional/test` can not find go binaries, it will download them automatically using `functional/provision/install_go.sh` script.
 
+## Configure host environment to run Vagrant
+
+### Debian/Ubuntu
+
+#### Install Vagrant
+
+```sh
+sudo apt-get install -y git nfs-kernel-server
+wget https://releases.hashicorp.com/vagrant/1.8.1/vagrant_1.8.1_x86_64.deb
+sudo dpkg -i vagrant_1.8.1_x86_64.deb
 ```
-# go test github.com/coreos/fleet/functional
+
+#### Install VirtualBox
+
+```sh
+echo "deb http://download.virtualbox.org/virtualbox/debian $(lsb_release -sc) contrib" | sudo tee /etc/apt/sources.list.d/virtualbox.list
+wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -
+sudo apt-get update
+sudo apt-get install -y build-essential dkms
+sudo apt-get install -y VirtualBox-5.0
+#Previous VirtualBox (if you have problems with nested virtualization, more info here: https://www.virtualbox.org/ticket/14965)
+#sudo apt-get install -y VirtualBox-4.3
 ```
 
-Since the tests utilize `systemd-nspawn`, this needs to be invoked as sudo/root.
+### CentOS/Fedora
 
-An example test session using coreos-vagrant follows. This assumes that go is available in `/home/core/go` and the fleet repository in `/home/core/fleet` on the target machine (the easiest way to achieve this is to use shared folders).
+**NOTE**: NFS and Vagrant doesn't work out of the box on CentOS 6.x, so it is recommended to use CentOS 7.x
+
+#### Install Vagrant
+
+```sh
+sudo yum install -y git nfs-utils
+sudo service nfs start
+sudo yum install -y https://releases.hashicorp.com/vagrant/1.8.1/vagrant_1.8.1_x86_64.rpm
 ```
-vagrant ssh core-01 -- -A
-export GOROOT="$(pwd)/go"
-export PATH="${GOROOT}/bin:$PATH"
-cd fleet
-ssh-add functional/fixtures/id_rsa
-export GOPATH="$(pwd)/gopath"
-export FLEETD_BIN="$(pwd)/bin/fleetd"
-export FLEETCTL_BIN="$(pwd)/bin/fleetctl"
-sudo -E env PATH=$PATH go test github.com/coreos/fleet/functional -v
+
+#### Install VirtualBox
+
+```sh
+source /etc/os-release
+for id in $ID_LIKE $ID; do break; done
+OS_ID=${id:-rhel}
+curl http://download.virtualbox.org/virtualbox/rpm/$OS_ID/virtualbox.repo | sudo tee /etc/yum.repos.d/virtualbox.repo
+sudo yum install -y make automake gcc gcc-c++ kernel-devel-`uname -r` dkms
+sudo yum install -y VirtualBox-5.0
+#Previous VirtualBox (if you have problems with nested virtualization, more info here: https://www.virtualbox.org/ticket/14965)
+#sudo yum install -y VirtualBox-4.3
 ```
 
-If the tests are aborted partway through, it's currently possible for them to leave residual state as a result of the systemd-nspawn operations. This can be cleaned up using the `clean.sh` script.
+[test-in-vagrant]: #run-tests-in-vagrant
+[configure-vagrant]: #configure-host-environment-to-run-vagrant
+[systemd-nspawn]: https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
+[cloud-config]: https://github.com/coreos/coreos-cloudinit/blob/master/Documentation/cloud-config.md
diff --git a/functional/Vagrantfile b/functional/Vagrantfile
new file mode 100644
index 00000000..b2b71a66
--- /dev/null
+++ b/functional/Vagrantfile
@@ -0,0 +1,146 @@
+# -*- mode: ruby -*-
+# # vi: set ft=ruby :
+# Vagrantfile based on official CoreOS Vagranfile https://github.com/coreos/coreos-vagrant with one extra provision string
+
+require 'fileutils'
+
+Vagrant.require_version ">= 1.6.0"
+
+CLOUD_CONFIG_PATH = File.join(File.dirname(__FILE__), "user-data")
+CONFIG = File.join(File.dirname(__FILE__), "config.rb")
+
+# Defaults for config options defined in CONFIG
+$num_instances = 1
+$instance_name_prefix = "core"
+$update_channel = "alpha"
+$image_version = "current"
+$enable_serial_logging = false
+$share_home = false
+$vm_gui = false
+$vm_memory = 1024
+$vm_cpus = 1
+$shared_folders = {}
+$forwarded_ports = {}
+
+# Attempt to apply the deprecated environment variable NUM_INSTANCES to
+# $num_instances while allowing config.rb to override it
+if ENV["NUM_INSTANCES"].to_i > 0 && ENV["NUM_INSTANCES"]
+  $num_instances = ENV["NUM_INSTANCES"].to_i
+end
+
+if File.exist?(CONFIG)
+  require CONFIG
+end
+
+# Use old vb_xxx config variables when set
+def vm_gui
+  $vb_gui.nil? ? $vm_gui : $vb_gui
+end
+
+def vm_memory
+  $vb_memory.nil? ? $vm_memory : $vb_memory
+end
+
+def vm_cpus
+  $vb_cpus.nil? ? $vm_cpus : $vb_cpus
+end
+
+Vagrant.configure("2") do |config|
+  # always use Vagrants insecure key
+  config.ssh.insert_key = false
+
+  config.vm.box = "coreos-%s" % $update_channel
+  if $image_version != "current"
+      config.vm.box_version = $image_version
+  end
+  config.vm.box_url = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json" % [$update_channel, $image_version]
+
+  ["vmware_fusion", "vmware_workstation"].each do |vmware|
+    config.vm.provider vmware do |v, override|
+      override.vm.box_url = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json" % [$update_channel, $image_version]
+    end
+  end
+
+  config.vm.provider :virtualbox do |v|
+    # On VirtualBox, we don't have guest additions or a functional vboxsf
+    # in CoreOS, so tell Vagrant that so it can be smarter.
+    v.check_guest_additions = false
+    v.functional_vboxsf     = false
+  end
+
+  # plugin conflict
+  if Vagrant.has_plugin?("vagrant-vbguest") then
+    config.vbguest.auto_update = false
+  end
+
+  (1..$num_instances).each do |i|
+    config.vm.define vm_name = "%s-%02d" % [$instance_name_prefix, i] do |config|
+      config.vm.hostname = vm_name
+
+      if $enable_serial_logging
+        logdir = File.join(File.dirname(__FILE__), "log")
+        FileUtils.mkdir_p(logdir)
+
+        serialFile = File.join(logdir, "%s-serial.txt" % vm_name)
+        FileUtils.touch(serialFile)
+
+        ["vmware_fusion", "vmware_workstation"].each do |vmware|
+          config.vm.provider vmware do |v, override|
+            v.vmx["serial0.present"] = "TRUE"
+            v.vmx["serial0.fileType"] = "file"
+            v.vmx["serial0.fileName"] = serialFile
+            v.vmx["serial0.tryNoRxLoss"] = "FALSE"
+          end
+        end
+
+        config.vm.provider :virtualbox do |vb, override|
+          vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
+          vb.customize ["modifyvm", :id, "--uartmode1", serialFile]
+        end
+      end
+
+      if $expose_docker_tcp
+        config.vm.network "forwarded_port", guest: 2375, host: ($expose_docker_tcp + i - 1), auto_correct: true
+      end
+
+      $forwarded_ports.each do |guest, host|
+        config.vm.network "forwarded_port", guest: guest, host: host, auto_correct: true
+      end
+
+      ["vmware_fusion", "vmware_workstation"].each do |vmware|
+        config.vm.provider vmware do |v|
+          v.gui = vm_gui
+          v.vmx['memsize'] = vm_memory
+          v.vmx['numvcpus'] = vm_cpus
+        end
+      end
+
+      config.vm.provider :virtualbox do |vb|
+        vb.gui = vm_gui
+        vb.memory = vm_memory
+        vb.cpus = vm_cpus
+      end
+
+      ip = "172.17.8.#{i+100}"
+      config.vm.network :private_network, ip: ip
+
+      # Uncomment below to enable NFS for sharing the host machine into the coreos-vagrant VM.
+      #config.vm.synced_folder ".", "/home/core/share", id: "core", :nfs => true, :mount_options => ['nolock,vers=3,udp']
+      $shared_folders.each_with_index do |(host_folder, guest_folder), index|
+        config.vm.synced_folder host_folder.to_s, guest_folder.to_s, id: "core-share%02d" % index, nfs: true, mount_options: ['nolock,vers=3,udp']
+      end
+
+      if $share_home
+        config.vm.synced_folder ENV['HOME'], ENV['HOME'], id: "home", :nfs => true, :mount_options => ['nolock,vers=3,udp']
+      end
+
+      if File.exist?(CLOUD_CONFIG_PATH)
+        config.vm.provision :file, :source => "#{CLOUD_CONFIG_PATH}", :destination => "/tmp/vagrantfile-user-data"
+        config.vm.provision :shell, :inline => "mv /tmp/vagrantfile-user-data /var/lib/coreos-vagrant/", :privileged => true
+      end
+
+      config.vm.provision :shell, :path => "provision/install_go.sh", :privileged => false
+
+    end
+  end
+end
diff --git a/functional/clean.sh b/functional/clean.sh
index 0b3f25e0..f86b7552 100755
--- a/functional/clean.sh
+++ b/functional/clean.sh
@@ -9,3 +9,5 @@ sudo rm -fr /run/systemd/system/*smoke* /tmp/smoke
 sudo systemctl daemon-reload
 ip link show fleet0 &>/dev/null && sudo ip link del fleet0
 etcdctl rm --recursive /fleet_functional
+
+rm -f log
diff --git a/functional/config.rb b/functional/config.rb
new file mode 100644
index 00000000..27ae745d
--- /dev/null
+++ b/functional/config.rb
@@ -0,0 +1,58 @@
+# Size of the CoreOS cluster created by Vagrant
+$num_instances=1
+
+# coreos-vagrant is configured through a series of configuration
+# options (global ruby variables) which are detailed below. To modify
+# these options, first copy this file to "config.rb". Then simply
+# uncomment the necessary lines, leaving the $, and replace everything
+# after the equals sign..
+
+# Change basename of the VM
+# The default value is "core", which results in VMs named starting with
+# "core-01" through to "core-${num_instances}".
+#$instance_name_prefix="core"
+
+# Change the version of CoreOS to be installed
+# To deploy a specific version, simply set $image_version accordingly.
+# For example, to deploy version 709.0.0, set $image_version="709.0.0".
+# The default value is "current", which points to the current version
+# of the selected channel
+#$image_version = "current"
+
+# Official CoreOS channel from which updates should be downloaded
+$update_channel='stable'
+
+# Log the serial consoles of CoreOS VMs to log/
+# Enable by setting value to true, disable with false
+# WARNING: Serial logging is known to result in extremely high CPU usage with
+# VirtualBox, so should only be used in debugging situations
+#$enable_serial_logging=false
+
+# Enable port forwarding of Docker TCP socket
+# Set to the TCP port you want exposed on the *host* machine, default is 2375
+# If 2375 is used, Vagrant will auto-increment (e.g. in the case of $num_instances > 1)
+# You can then use the docker tool locally by setting the following env var:
+#   export DOCKER_HOST='tcp://127.0.0.1:2375'
+#$expose_docker_tcp=2375
+
+# Enable NFS sharing of your home directory ($HOME) to CoreOS
+# It will be mounted at the same path in the VM as on the host.
+# Example: /Users/foobar -> /Users/foobar
+#$share_home=false
+
+# Customize VMs
+#$vm_gui = false
+$vm_memory = 512
+$vm_cpus = 1
+
+# Share additional folders to the CoreOS VMs
+# For example,
+# $shared_folders = {'/path/on/host' => '/path/on/guest', '/home/foo/app' => '/app'}
+# or, to map host folders to guest folders of the same name,
+# $shared_folders = Hash[*['/home/foo/app1', '/home/foo/app2'].map{|d| [d, d]}.flatten]
+#$shared_folders = {}
+
+$shared_folders = {'../' => '/home/core/fleet'}
+
+# Enable port forwarding from guest(s) to host machine, syntax is: { 80 => 8080 }, auto correction is enabled by default.
+#$forwarded_ports = {}
diff --git a/functional/platform/nspawn.go b/functional/platform/nspawn.go
index d642fd2d..0b461d41 100644
--- a/functional/platform/nspawn.go
+++ b/functional/platform/nspawn.go
@@ -211,10 +211,10 @@ func (nc *nspawnCluster) prepCluster() (err error) {
 		return
 	}
 
-	if !strings.Contains(stdout, "172.17.0.1/16") {
-		_, _, err = run("ip addr add 172.17.0.1/16 dev fleet0")
+	if !strings.Contains(stdout, "172.18.0.1/16") {
+		_, _, err = run("ip addr add 172.18.0.1/16 dev fleet0")
 		if err != nil {
-			log.Printf("Failed adding 172.17.0.1/16 to fleet0: %v", err)
+			log.Printf("Failed adding 172.18.0.1/16 to fleet0: %v", err)
 			return
 		}
 	}
@@ -251,7 +251,7 @@ func (nc *nspawnCluster) buildConfigDrive(dir, ip string) error {
 	}
 	defer userFile.Close()
 
-	etcd := "http://172.17.0.1:4001"
+	etcd := "http://172.18.0.1:4001"
 	return util.BuildCloudConfig(userFile, ip, etcd, nc.keyspace())
 }
 
@@ -290,7 +290,7 @@ func (nc *nspawnCluster) createMember(id string) (m Member, err error) {
 	nm := nspawnMember{
 		uuid: newMachineID(),
 		id:   id,
-		ip:   fmt.Sprintf("172.17.1.%s", id),
+		ip:   fmt.Sprintf("172.18.1.%s", id),
 	}
 	nc.members[nm.ID()] = nm
 
@@ -303,13 +303,15 @@ func (nc *nspawnCluster) createMember(id string) (m Member, err error) {
 		// minimum requirements for running systemd/coreos in a container
 		fmt.Sprintf("mkdir -p %s/usr", fsdir),
 		fmt.Sprintf("cp /etc/os-release %s/etc", fsdir),
+		fmt.Sprintf("echo 'core:x:500:500:CoreOS Admin:/home/core:/bin/bash' > %s/etc/passwd", fsdir),
+		fmt.Sprintf("echo 'core:x:500:' > %s/etc/group", fsdir),
 		fmt.Sprintf("ln -s /proc/self/mounts %s/etc/mtab", fsdir),
 		fmt.Sprintf("ln -s usr/lib64 %s/lib64", fsdir),
 		fmt.Sprintf("ln -s lib64 %s/lib", fsdir),
 		fmt.Sprintf("ln -s usr/bin %s/bin", fsdir),
 		fmt.Sprintf("ln -s usr/sbin %s/sbin", fsdir),
 		fmt.Sprintf("mkdir -p %s/home/core/.ssh", fsdir),
-		fmt.Sprintf("chown -R core:core %s/home/core", fsdir),
+		fmt.Sprintf("chown -R 500:500 %s/home/core", fsdir),
 
 		// We don't need this, and it's slow, so mask it
 		fmt.Sprintf("ln -s /dev/null %s/etc/systemd/system/systemd-udev-hwdb-update.service", fsdir),
@@ -346,7 +348,7 @@ UseDNS no
 	[Service]
 	Type=oneshot
 	RemainAfterExit=yes
-	ExecStart=/usr/bin/ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" -b 768`
+	ExecStart=/usr/bin/ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" -b 1024`
 	if err = ioutil.WriteFile(path.Join(fsdir, "/etc/systemd/system/sshd-keygen.service"), []byte(sshd_keygen), 0644); err != nil {
 		log.Printf("Failed writing sshd-keygen.service: %v", err)
 		return
@@ -395,6 +397,7 @@ UseDNS no
 			return
 		default:
 		}
+		log.Printf("Dialing machine: %s", addr)
 		c, err := net.DialTimeout("tcp", addr, 100*time.Millisecond)
 		if err == nil {
 			c.Close()
diff --git a/functional/provision/install_go.sh b/functional/provision/install_go.sh
new file mode 100755
index 00000000..0e58cb52
--- /dev/null
+++ b/functional/provision/install_go.sh
@@ -0,0 +1,16 @@
+#!/bin/bash -e
+
+USER_ID=${SUDO_UID:-$(id -u)}
+HOME=$(getent passwd "${USER_ID}" | cut -d: -f6)
+
+export GOROOT=${HOME}/go
+export PATH=${HOME}/go/bin:${PATH}
+
+gover=1.5.3
+gotar=go${gover}.linux-amd64.tar.gz
+if [ ! -f ${HOME}/${gotar} ]; then
+  # Remove unfinished archive when you press Ctrl+C
+  trap "rm -f ${HOME}/${gotar}" INT TERM
+  wget --no-verbose https://storage.googleapis.com/golang/${gotar} -P ${HOME}
+fi
+tar -xf ${HOME}/${gotar} -C ${HOME}
diff --git a/functional/run-in-vagrant b/functional/run-in-vagrant
new file mode 100755
index 00000000..d528c6d2
--- /dev/null
+++ b/functional/run-in-vagrant
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+CDIR=$(cd `dirname $0` && pwd)
+cd $CDIR
+
+vagrant up
+vagrant ssh core-01 -c "sudo ~/fleet/functional/test"
diff --git a/functional/start_etcd b/functional/start_etcd
new file mode 100755
index 00000000..84b85362
--- /dev/null
+++ b/functional/start_etcd
@@ -0,0 +1,25 @@
+#!/bin/bash -e
+
+CDIR=$(cd `dirname $0` && pwd)
+USER_ID=${SUDO_UID:-$(id -u)}
+HOME=$(getent passwd "${USER_ID}" | cut -d: -f6)
+
+if [[ -z "${SUDO_UID}" && "${USER_ID}" != "0" ]]; then
+  echo "Script should be run using sudo"
+  exit 1
+fi
+
+if [ ! -f ${HOME}/setup-network-environment ]; then
+  # Remove unfinished file when you press Ctrl+C
+  trap "rm -f ${HOME}/setup-network-environment" INT TERM
+  wget --no-verbose https://github.com/kelseyhightower/setup-network-environment/releases/download/1.0.1/setup-network-environment -P ${HOME}
+fi
+
+if [ ! -x ${HOME}/setup-network-environment ]; then
+  chmod +x ${HOME}/setup-network-environment
+fi
+
+${HOME}/setup-network-environment
+source /etc/network-environment
+export COREOS_PRIVATE_IPV4=$DEFAULT_IPV4
+coreos-cloudinit --from-file=${CDIR}/user-data
diff --git a/functional/user-data b/functional/user-data
new file mode 100644
index 00000000..9aba6418
--- /dev/null
+++ b/functional/user-data
@@ -0,0 +1,12 @@
+#cloud-config
+
+---
+coreos:
+  etcd2:
+    advertise-client-urls: http://$private_ipv4:2379
+    listen-client-urls: http://0.0.0.0:2379,http://0.0.0.0:4001
+  units:
+  - name: etcd2.service
+    command: start
+  update:
+    reboot-strategy: off


Test patch:
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files_test.go
index 8e15f2a1..a1c6948f 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/files_test.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package activation
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners_test.go
index 72fb0ff6..9fb1c4bb 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/listeners_test.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2014 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package activation
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns_test.go
index 8449756c..5a5fb244 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/activation/packetconns_test.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2014 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package activation
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go
index 3ea131e2..4cfbe829 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package dbus
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go
index 75185539..805e42ea 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go
@@ -1,16 +1,18 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
 package dbus
 
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go
index 2f04096f..c4435f88 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package dbus
 
 import (
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
index 53f75dfb..14948e2e 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package dbus
 
 import (
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go
index e50fc6f9..996ef491 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package dbus
 
 import (
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize_test.go
index 84b7169f..bb98b829 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/deserialize_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
@@ -80,15 +66,14 @@ Environment= "FOO=BAR" "BAZ=QUX"
 			},
 		},
 
-		// line continuations unmodified
+		// line continuations respected
 		{
 			[]byte(`[Unit]
 Description= Unnecessarily wrapped \
     words here
 `),
 			[]*UnitOption{
-				&UnitOption{"Unit", "Description", `Unnecessarily wrapped \
-    words here`},
+				&UnitOption{"Unit", "Description", "Unnecessarily wrapped      words here"},
 			},
 		},
 
@@ -120,8 +105,8 @@ Description=Bar\
 Baz
 `),
 			[]*UnitOption{
-				&UnitOption{"Unit", "Description", "Bar\\\n# comment alpha"},
-				&UnitOption{"Unit", "Description", "Bar\\\n# comment bravo \\\nBaz"},
+				&UnitOption{"Unit", "Description", "Bar # comment alpha"},
+				&UnitOption{"Unit", "Description", "Bar # comment bravo  Baz"},
 			},
 		},
 
@@ -181,7 +166,7 @@ Description=Bar`),
 			[]byte(`[Unit]
 Description=Bar \`),
 			[]*UnitOption{
-				&UnitOption{"Unit", "Description", "Bar \\"},
+				&UnitOption{"Unit", "Description", "Bar"},
 			},
 		},
 
@@ -219,7 +204,7 @@ Description= words here `),
 Description= words here \
   `),
 			[]*UnitOption{
-				&UnitOption{"Unit", "Description", "words here \\\n"},
+				&UnitOption{"Unit", "Description", "words here"},
 			},
 		},
 
@@ -241,25 +226,6 @@ ExecStart=/bin/bash echo poof \  `),
 				&UnitOption{"Service", "ExecStart", `/bin/bash echo poof \`},
 			},
 		},
-		// a long unit file line that's just equal to the maximum permitted length
-		{
-			[]byte(`[Service]
-ExecStart=/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`),
-			[]*UnitOption{
-				&UnitOption{"Service", "ExecStart", `/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`},
-			},
-		},
-		// the same, but with a trailing newline
-		{
-			[]byte(`[Service]
-ExecStart=/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."
-Option=value
-`),
-			[]*UnitOption{
-				&UnitOption{"Service", "ExecStart", `/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`},
-				&UnitOption{"Service", "Option", "value"},
-			},
-		},
 	}
 
 	assert := func(expect, output []*UnitOption) error {
@@ -321,7 +287,7 @@ Description=Foo
 	for i, tt := range tests {
 		output, err := Deserialize(bytes.NewReader(tt))
 		if err == nil {
-			t.Errorf("case %d: unexpected nil error", i)
+			t.Errorf("case %d: unexpected non-nil error, received nil", i)
 			t.Log("Output:")
 			logUnitOptionSlice(t, output)
 		}
@@ -333,49 +299,3 @@ func logUnitOptionSlice(t *testing.T, opts []*UnitOption) {
 		t.Logf("%d: %v", idx, opt)
 	}
 }
-
-func TestDeserializeLineTooLong(t *testing.T) {
-	tests := [][]byte{
-		// section header that's far too long
-		[]byte(`[Seeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeervice]
-`),
-		// sane-looking unit file with a line just greater than the maximum allowed (currently, 2048)
-		[]byte(`[Service]
-ExecStart=/bin/bash -c "echo ..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."
-`),
-		// sane-looking unit file with option value way too long
-		[]byte(`
-# test unit file
-
-[Service]
-ExecStartPre=-/usr/bin/docker rm %p
-ExecStartPre=-/usr/bin/docker pull busybox
-ExecStart=/usr/bin/docker run --rm --name %p --net=host \
-  -e "test=1123t" \
-  -e "test=1123t" \
-  -e "fiz=1123t" \
-  -e "buz=1123t" \
-  -e "FOO=BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBABARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARRBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR"BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBABARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARRBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR" \
-  busybox sleep 10
-ExecStop=-/usr/bin/docker kill %p
-SyslogIdentifier=busybox
-Restart=always
-RestartSec=10s
-`),
-		// single arbitrary line that's way too long
-		[]byte(`arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters`),
-		// sane-looking unit file with option name way too long
-		[]byte(`[Service]
-ExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStart=/bin/true
-`),
-	}
-
-	for i, tt := range tests {
-		output, err := Deserialize(bytes.NewReader(tt))
-		if err != ErrLineTooLong {
-			t.Errorf("case %d: unexpected err: %v", i, err)
-			t.Log("Output:")
-			logUnitOptionSlice(t, output)
-		}
-	}
-}
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/end_to_end_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/end_to_end_test.go
deleted file mode 100644
index 7182327a..00000000
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/end_to_end_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package unit
-
-import (
-	"bytes"
-	"io/ioutil"
-	"testing"
-)
-
-func TestDeserializeAndReserialize(t *testing.T) {
-	tests := []struct {
-		in   string
-		wout string
-	}{
-		{
-			`[Service]
-ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"
-`,
-			`[Service]
-ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"
-`},
-		{
-			`[Unit]
-Description= Unnecessarily wrapped \
-    words here`,
-			`[Unit]
-Description=Unnecessarily wrapped \
-    words here
-`,
-		},
-		{
-			`[Unit]
-Description=Demo \
-
-Requires=docker.service
-`,
-			`[Unit]
-Description=Demo \
-
-Requires=docker.service
-`,
-		},
-		{
-			`; comment alpha
-# comment bravo
-[Unit]
-; comment charlie
-# comment delta
-#Description=Foo
-Description=Bar
-; comment echo
-# comment foxtrot
-`,
-			`[Unit]
-Description=Bar
-`},
-	}
-	for i, tt := range tests {
-		ds, err := Deserialize(bytes.NewBufferString(tt.in))
-		if err != nil {
-			t.Errorf("case %d: unexpected error parsing unit: %v", i, err)
-			continue
-		}
-		out, err := ioutil.ReadAll(Serialize(ds))
-		if err != nil {
-			t.Errorf("case %d: unexpected error serializing unit: %v", i, err)
-			continue
-		}
-		if g := string(out); g != tt.wout {
-			t.Errorf("case %d: incorrect output", i)
-			t.Logf("Expected:\n%#v", tt.wout)
-			t.Logf("Actual:\n%#v", g)
-		}
-	}
-}
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape_test.go
deleted file mode 100644
index 36b1a7d3..00000000
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/escape_test.go
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package unit
-
-import (
-	"testing"
-)
-
-func TestUnitNameEscape(t *testing.T) {
-	tests := []struct {
-		in     string
-		out    string
-		isPath bool
-	}{
-		// turn empty string path into escaped /
-		{
-			in:     "",
-			out:    "-",
-			isPath: true,
-		},
-		// turn redundant ////s into single escaped /
-		{
-			in:     "/////////",
-			out:    "-",
-			isPath: true,
-		},
-		// remove all redundant ////s
-		{
-			in:     "///foo////bar/////tail//////",
-			out:    "foo-bar-tail",
-			isPath: true,
-		},
-		// leave empty string empty
-		{
-			in:     "",
-			out:    "",
-			isPath: false,
-		},
-		// escape leading dot
-		{
-			in:     ".",
-			out:    `\x2e`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     "/.",
-			out:    `\x2e`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     "/////////.",
-			out:    `\x2e`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     "/////////.///////////////",
-			out:    `\x2e`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     ".....",
-			out:    `\x2e....`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     "/.foo/.bar",
-			out:    `\x2efoo-.bar`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     ".foo/.bar",
-			out:    `\x2efoo-.bar`,
-			isPath: true,
-		},
-		// escape leading dot
-		{
-			in:     ".foo/.bar",
-			out:    `\x2efoo-.bar`,
-			isPath: false,
-		},
-		// escape disallowed
-		{
-			in:     `///..\-!#??///`,
-			out:    `---..\x5c\x2d\x21\x23\x3f\x3f---`,
-			isPath: false,
-		},
-		// escape disallowed
-		{
-			in:     `///..\-!#??///`,
-			out:    `\x2e.\x5c\x2d\x21\x23\x3f\x3f`,
-			isPath: true,
-		},
-		// escape real-world example
-		{
-			in:     `user-cloudinit@/var/lib/coreos/vagrant/vagrantfile-user-data.service`,
-			out:    `user\x2dcloudinit\x40-var-lib-coreos-vagrant-vagrantfile\x2duser\x2ddata.service`,
-			isPath: false,
-		},
-	}
-
-	for i, tt := range tests {
-		var s string
-		if tt.isPath {
-			s = UnitNamePathEscape(tt.in)
-		} else {
-			s = UnitNameEscape(tt.in)
-		}
-		if s != tt.out {
-			t.Errorf("case %d: failed escaping %v isPath: %v - expected %v, got %v", i, tt.in, tt.isPath, tt.out, s)
-		}
-	}
-}
-
-func TestUnitNameUnescape(t *testing.T) {
-	tests := []struct {
-		in     string
-		out    string
-		isPath bool
-	}{
-		// turn empty string path into /
-		{
-			in:     "",
-			out:    "/",
-			isPath: true,
-		},
-		// leave empty string empty
-		{
-			in:     "",
-			out:    "",
-			isPath: false,
-		},
-		// turn ////s into
-		{
-			in:     "---------",
-			out:    "/////////",
-			isPath: true,
-		},
-		// unescape hex
-		{
-			in:     `---..\x5c\x2d\x21\x23\x3f\x3f---`,
-			out:    `///..\-!#??///`,
-			isPath: false,
-		},
-		// unescape hex
-		{
-			in:     `\x2e.\x5c\x2d\x21\x23\x3f\x3f`,
-			out:    `/..\-!#??`,
-			isPath: true,
-		},
-		// unescape hex, retain invalids
-		{
-			in:     `\x2e.\x5c\x2d\xaZ\x.o\x21\x23\x3f\x3f`,
-			out:    `/..\-\xaZ\x.o!#??`,
-			isPath: true,
-		},
-		// unescape hex, retain invalids, partial tail
-		{
-			in:     `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\x3`,
-			out:    `/..\\x-\xaZ\x.o!#??\x3`,
-			isPath: true,
-		},
-		// unescape hex, retain invalids, partial tail
-		{
-			in:     `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\x`,
-			out:    `/..\\x-\xaZ\x.o!#??\x`,
-			isPath: true,
-		},
-		// unescape hex, retain invalids, partial tail
-		{
-			in:     `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\`,
-			out:    `/..\\x-\xaZ\x.o!#??\`,
-			isPath: true,
-		},
-		// unescape real-world example
-		{
-			in:     `user\x2dcloudinit\x40-var-lib-coreos-vagrant-vagrantfile\x2duser\x2ddata.service`,
-			out:    `user-cloudinit@/var/lib/coreos/vagrant/vagrantfile-user-data.service`,
-			isPath: false,
-		},
-	}
-
-	for i, tt := range tests {
-		var s string
-		if tt.isPath {
-			s = UnitNamePathUnescape(tt.in)
-		} else {
-			s = UnitNameUnescape(tt.in)
-		}
-		if s != tt.out {
-			t.Errorf("case %d: failed unescaping %v isPath: %v - expected %v, got %v", i, tt.in, tt.isPath, tt.out, s)
-		}
-	}
-}
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option_test.go
index 0765f03a..ebdc062f 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/option_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
diff --git a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize_test.go b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize_test.go
index bd492b0d..e5ec0f13 100644
--- a/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize_test.go
+++ b/Godeps/_workspace/src/github.com/coreos/go-systemd/unit/serialize_test.go
@@ -1,17 +1,3 @@
-// Copyright 2015 CoreOS, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package unit
 
 import (
@@ -68,7 +54,7 @@ ExecStart=/usr/bin/sleep infinity
 `,
 		},
 
-		// options are grouped into sections
+		// no optimization for unsorted options
 		{
 			[]*UnitOption{
 				&UnitOption{"Unit", "Description", "Foo"},
@@ -77,34 +63,12 @@ ExecStart=/usr/bin/sleep infinity
 			},
 			`[Unit]
 Description=Foo
-BindsTo=bar.service
 
 [Service]
 ExecStart=/usr/bin/sleep infinity
-`,
-		},
 
-		// options are ordered within groups, and sections are ordered in the order in which they were first seen
-		{
-			[]*UnitOption{
-				&UnitOption{"Unit", "Description", "Foo"},
-				&UnitOption{"Service", "ExecStart", "/usr/bin/sleep infinity"},
-				&UnitOption{"Unit", "BindsTo", "bar.service"},
-				&UnitOption{"X-Foo", "Bar", "baz"},
-				&UnitOption{"Service", "ExecStop", "/usr/bin/sleep 1"},
-				&UnitOption{"Unit", "Documentation", "https://foo.com"},
-			},
-			`[Unit]
-Description=Foo
+[Unit]
 BindsTo=bar.service
-Documentation=https://foo.com
-
-[Service]
-ExecStart=/usr/bin/sleep infinity
-ExecStop=/usr/bin/sleep 1
-
-[X-Foo]
-Bar=baz
 `,
 		},
 
@@ -162,7 +126,7 @@ o
 
 		output := string(outBytes)
 		if tt.output != output {
-			t.Errorf("case %d: incorrect output", i)
+			t.Errorf("case %d: incorrect output")
 			t.Logf("Expected:\n%s", tt.output)
 			t.Logf("Actual:\n%s", output)
 		}
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/encoder_test.go b/Godeps/_workspace/src/github.com/godbus/dbus/encoder_test.go
deleted file mode 100644
index e2c938a1..00000000
--- a/Godeps/_workspace/src/github.com/godbus/dbus/encoder_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package dbus
-
-import (
-	"bytes"
-	"encoding/binary"
-	"reflect"
-	"testing"
-)
-
-func TestEncodeArrayOfMaps(t *testing.T) {
-	tests := []struct {
-		name string
-		vs   []interface{}
-	}{
-		{
-			"aligned at 8 at start of array",
-			[]interface{}{
-				"12345",
-				[]map[string]Variant{
-					{
-						"abcdefg": MakeVariant("foo"),
-						"cdef":    MakeVariant(uint32(2)),
-					},
-				},
-			},
-		},
-		{
-			"not aligned at 8 for start of array",
-			[]interface{}{
-				"1234567890",
-				[]map[string]Variant{
-					{
-						"abcdefg": MakeVariant("foo"),
-						"cdef":    MakeVariant(uint32(2)),
-					},
-				},
-			},
-		},
-	}
-	for _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} {
-		for _, tt := range tests {
-			buf := new(bytes.Buffer)
-			enc := newEncoder(buf, order)
-			enc.Encode(tt.vs...)
-
-			dec := newDecoder(buf, order)
-			v, err := dec.Decode(SignatureOf(tt.vs...))
-			if err != nil {
-				t.Errorf("%q: decode (%v) failed: %v", tt.name, order, err)
-				continue
-			}
-			if !reflect.DeepEqual(v, tt.vs) {
-				t.Errorf("%q: (%v) not equal: got '%v', want '%v'", tt.name, order, v, tt.vs)
-				continue
-			}
-		}
-	}
-}
diff --git a/Godeps/_workspace/src/github.com/godbus/dbus/export_test.go b/Godeps/_workspace/src/github.com/godbus/dbus/export_test.go
deleted file mode 100644
index 27891f75..00000000
--- a/Godeps/_workspace/src/github.com/godbus/dbus/export_test.go
+++ /dev/null
@@ -1,374 +0,0 @@
-package dbus
-
-import "testing"
-
-type lowerCaseExport struct{}
-
-func (export lowerCaseExport) foo() (string, *Error) {
-	return "bar", nil
-}
-
-type fooExport struct {
-	message Message
-}
-
-func (export *fooExport) Foo(message Message, param string) (string, *Error) {
-	export.message = message
-	return "foo", nil
-}
-
-type barExport struct{}
-
-func (export barExport) Foo(param string) (string, *Error) {
-	return "bar", nil
-}
-
-type badExport struct{}
-
-func (export badExport) Foo(param string) string {
-	return "bar"
-}
-
-// Test typical Export usage.
-func TestExport(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	connection.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-	subtreeObject := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response int64
-	err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Double: %s", err)
-	}
-
-	if response != 4 {
-		t.Errorf("Response was %d, expected 4", response)
-	}
-
-	// Verify that calling a subtree of a regular export does not result in a
-	// valid method call.
-	err = subtreeObject.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err == nil {
-		t.Error("Expected error due to no object being exported on that path")
-	}
-
-	// Now remove export
-	connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err == nil {
-		t.Error("Expected an error since the export was removed")
-	}
-}
-
-// Test that Exported handlers can obtain raw message.
-func TestExport_message(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	export := &fooExport{}
-	connection.Export(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-
-	var response string
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Foo: %s", err)
-	}
-
-	if response != "foo" {
-		t.Errorf(`Response was %s, expected "foo"`, response)
-	}
-
-	if export.message.serial == 0 {
-		t.Error("Expected a valid message to be given to handler")
-	}
-}
-
-// Test Export with an invalid path.
-func TestExport_invalidPath(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	err = connection.Export(nil, "foo", "bar")
-	if err == nil {
-		t.Error("Expected an error due to exporting with an invalid path")
-	}
-}
-
-// Test Export with an un-exported method. This should not panic, but rather
-// result in an invalid method call.
-func TestExport_unexportedMethod(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	connection.Export(lowerCaseExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-
-	var response string
-	call := object.Call("org.guelfey.DBus.Test.foo", 0)
-	err = call.Store(&response)
-	if err == nil {
-		t.Errorf("Expected an error due to calling unexported method")
-	}
-}
-
-// Test Export with a method lacking the correct return signature. This should
-// result in an invalid method call.
-func TestExport_badSignature(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	connection.Export(badExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-
-	var response string
-	call := object.Call("org.guelfey.DBus.Test.Foo", 0)
-	err = call.Store(&response)
-	if err == nil {
-		t.Errorf("Expected an error due to the method lacking the right signature")
-	}
-}
-
-// Test typical ExportWithMap usage.
-func TestExportWithMap(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	mapping := make(map[string]string)
-	mapping["Double"] = "double" // Export this method as lower-case
-
-	connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-
-	var response int64
-	err = object.Call("org.guelfey.DBus.Test.double", 0, int64(2)).Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling double: %s", err)
-	}
-
-	if response != 4 {
-		t.Errorf("Response was %d, expected 4", response)
-	}
-}
-
-// Test that ExportWithMap does not export both method alias and method.
-func TestExportWithMap_bypassAlias(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	mapping := make(map[string]string)
-	mapping["Double"] = "double" // Export this method as lower-case
-
-	connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test")
-
-	var response int64
-	// Call upper-case Double (i.e. the real method, not the alias)
-	err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err == nil {
-		t.Error("Expected an error due to calling actual method, not alias")
-	}
-}
-
-// Test typical ExportSubtree usage.
-func TestExportSubtree(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	export := &fooExport{}
-	connection.ExportSubtree(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-
-	// Call a subpath of the exported path
-	object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response string
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Foo: %s", err)
-	}
-
-	if response != "foo" {
-		t.Errorf(`Response was %s, expected "foo"`, response)
-	}
-
-	if export.message.serial == 0 {
-		t.Error("Expected the raw message, got an invalid one")
-	}
-
-	// Now remove export
-	connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err == nil {
-		t.Error("Expected an error since the export was removed")
-	}
-}
-
-// Test that using ExportSubtree with exported methods that don't contain a
-// Message still work, they just don't get the message.
-func TestExportSubtree_noMessage(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	connection.ExportSubtree(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-
-	// Call a subpath of the exported path
-	object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response int64
-	err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Double: %s", err)
-	}
-
-	if response != 4 {
-		t.Errorf("Response was %d, expected 4", response)
-	}
-
-	// Now remove export
-	connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
-	if err == nil {
-		t.Error("Expected an error since the export was removed")
-	}
-}
-
-// Test that a regular Export takes precedence over ExportSubtree.
-func TestExportSubtree_exportPrecedence(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	// Register for the entire subtree of /org/guelfey/DBus/Test
-	connection.ExportSubtree(&fooExport{},
-		"/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-
-	// Explicitly register for /org/guelfey/DBus/Test/Foo, a subpath of above
-	connection.Export(&barExport{}, "/org/guelfey/DBus/Test/Foo",
-		"org.guelfey.DBus.Test")
-
-	// Call the explicitly exported path
-	object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response string
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Foo: %s", err)
-	}
-
-	if response != "bar" {
-		t.Errorf(`Response was %s, expected "bar"`, response)
-	}
-
-	response = "" // Reset response so errors aren't confusing
-
-	// Now remove explicit export
-	connection.Export(nil, "/org/guelfey/DBus/Test/Foo", "org.guelfey.DBus.Test")
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Foo: %s", err)
-	}
-
-	// Now the subtree export should handle the call
-	if response != "foo" {
-		t.Errorf(`Response was %s, expected "foo"`, response)
-	}
-}
-
-// Test typical ExportSubtreeWithMap usage.
-func TestExportSubtreeWithMap(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	mapping := make(map[string]string)
-	mapping["Foo"] = "foo" // Export this method as lower-case
-
-	connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-
-	// Call a subpath of the exported path
-	object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response string
-	// Call the lower-case method
-	err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
-	if err != nil {
-		t.Errorf("Unexpected error calling Foo: %s", err)
-	}
-
-	if response != "foo" {
-		t.Errorf(`Response was %s, expected "foo"`, response)
-	}
-
-	// Now remove export
-	connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
-	if err == nil {
-		t.Error("Expected an error since the export was removed")
-	}
-}
-
-// Test that ExportSubtreeWithMap does not export both method alias and method.
-func TestExportSubtreeWithMap_bypassAlias(t *testing.T) {
-	connection, err := SessionBus()
-	if err != nil {
-		t.Fatalf("Unexpected error connecting to session bus: %s", err)
-	}
-
-	name := connection.Names()[0]
-
-	mapping := make(map[string]string)
-	mapping["Foo"] = "foo" // Export this method as lower-case
-
-	connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
-	object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
-
-	var response string
-	// Call upper-case Foo (i.e. the real method, not the alias)
-	err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
-	if err == nil {
-		t.Error("Expected an error due to calling actual method, not alias")
-	}
-}
diff --git a/functional/test b/functional/test
new file mode 100755
index 00000000..0ccb464e
--- /dev/null
+++ b/functional/test
@@ -0,0 +1,49 @@
+#!/bin/bash -e
+
+CDIR=$(cd `dirname $0` && pwd)
+USER_ID=${SUDO_UID:-$(id -u)}
+HOME=$(getent passwd "${USER_ID}" | cut -d: -f6)
+
+cd ${CDIR}/../
+export VERSION=$(git describe --dirty)
+export GOROOT=${HOME}/go
+export PATH=${HOME}/go/bin:${PATH}
+
+if [ ! -S "$SSH_AUTH_SOCK" ]; then
+  eval $(ssh-agent)
+fi
+
+# github doesn't support explicit file permission set, this is workaround
+chmod 0600 functional/fixtures/id_rsa
+ssh-add functional/fixtures/id_rsa
+sudo systemctl stop fleet || true
+
+if [[ ! $(go version 2>/dev/null) ]]; then
+  functional/provision/install_go.sh
+fi
+
+if [ ! -x "bin/fleetd" ] || \
+   [ ! -x "bin/fleetctl" ] || \
+   [ ! $(bin/fleetctl | grep "$VERSION") ]; then
+  ./build
+fi
+
+source build-env
+eval $(go env)
+go test github.com/coreos/fleet/functional -ldflags "${GLDFLAGS}" -v 2>&1 | tee functional/log
+
+total=$(grep -E '^--- (PASS|FAIL)' functional/log | wc -l)
+pass=$(grep '^--- PASS' functional/log | wc -l)
+fail=$(grep '^--- FAIL' functional/log | wc -l)
+
+echo ""
+grep -E '^--- (PASS|FAIL)' functional/log
+
+echo "==========================================================="
+echo "Functional test summary"
+echo "==========================================================="
+echo "# TOTAL: $total"
+echo "# PASS: $pass"
+echo "# FAIL: $fail"
+echo ""
+echo "See functional/log for the detailed output."
diff --git a/unit/unit_test.go b/unit/unit_test.go
index 256ff14a..9590f4e2 100644
--- a/unit/unit_test.go
+++ b/unit/unit_test.go
@@ -265,17 +265,10 @@ pung
 `
 	expected := map[string]map[string][]string{
 		"Service": {
-			"ExecStart": {`echo \
-  "pi\
-  ng"`},
-			"ExecStop": {`\
-echo "po\
-ng"`},
-			"ExecStopPre": {`echo\
-#pang`},
-			"ExecStopPost": {`echo\
-#peng\
-pung`},
+			"ExecStart":    {`echo    "pi   ng"`},
+			"ExecStop":     {`echo "po ng"`},
+			"ExecStopPre":  {`echo #pang`},
+			"ExecStopPost": {`echo #peng pung`},
 		},
 	}
 	unitFile, err := NewUnitFile(contents)