Permalink
Browse files

Introduce a dependency injection convention

The convention provides a standard way of constructing interface
values and wiring them together with their dependencies. Runtime code
then has a simple way of obtaining a default value of an interface.
Test code can inject mocks and stubs for dependencies as necessary.

The convention involves some "wiring" functions typically defined in
the file wiring.go.

The convention associates an interface with a pair of functions of the
form:

* Wire(<parms>, <depParms>) (<InterfaceType>, gerror.Gerror)
* WireWith(<parms>, <dependencies>) (<InterfaceType>, gerror.Gerror)

where <InterfaceType> indicates the interface type being constructed.
gerror.Gerror is returned, although the caller may treat that as an
error.

If there is one interface in a package, then the function names are
"Wire" and "WireWith". If there is more than one interface in a
package, the function names are decorated, for example "WireFS" and
"WireFSWith".

Wire(<params>, <depParms>) constructs a default value of the interface
with dependencies injected. <params> indicates any construction
parameters, usually not of interface type. <depParms> indicates any
additional parameters needed to construct the dependencies. Wire()
typically constructs any dependencies by passing <depParms> to the
Wire() functions of its dependencies and then calls WireWith() passing
<params> and the constructed dependencies.

WireWith(<parms>, <dependencies>) constructs a special value of the
interface with the specified dependencies. This is used by Wire() to
create the default value. It may also be used by test code to create a
value in which some or all of its dependencies are mocks or stubs.
WireWith should not normally be used outside its package or the test
package.

The Wire and WireWith functions are placed by convention in the file
wiring.go, although this convention is not enforced. The complexity of
wiring.go is minimised by having WireWith call a private function in
another file to construct the value. The private function is typically
of the form:

newXXX(<params>,<dependencies>) (<InterfaceType>, gerror.Gerror)

where XXX is usually the interface type name, but more generally
identifies the concrete type which implements the interface, for
example when the package provides more than one implementation of
the interface.
  • Loading branch information...
1 parent 41764ed commit a023fa493953e2010f40419dd2290f84e86d3960 @glyn glyn committed Aug 7, 2014
@@ -1,2 +1,23 @@
package config_builder
+import (
+ "github.com/docker/libcontainer"
+ "github.com/cf-guardian/guardian-backend/utils/gerror"
+)
+
+type ConfigBuilder interface {
+
+ // Builds a libcontainer configuration.
+ Build() *libcontainer.Config
+}
+
+type configBuilder struct {
+}
+
+func newConfigBuilder() (ConfigBuilder, gerror.Gerror) {
+ return &configBuilder{}, nil
+}
+
+func (cb *configBuilder) Build() *libcontainer.Config {
+ return &libcontainer.Config{}
+}
@@ -0,0 +1,11 @@
+package config_builder
+
+import "github.com/cf-guardian/guardian-backend/utils/gerror"
+
+func Wire() (ConfigBuilder, gerror.Gerror) {
+ return WireWith()
+}
+
+func WireWith() (ConfigBuilder, gerror.Gerror) {
+ return newConfigBuilder()
+}
@@ -13,7 +13,13 @@ import (
)
func StartServer(opts *options.Options) <-chan int {
- backend := guardian_backend.New(opts.DepotPath)
+ backend, err := guardian_backend.Wire(opts.DepotPath, opts.DepotPath)
+ if err != nil {
+ log.Printf("StartServer failed to wire GuardianBackend: ", err)
+ exitChan := make(chan int, 1)
+ exitChan<-1
+ return exitChan
+ }
return runServer(backend, opts)
}
@@ -3,20 +3,25 @@ package guardian_backend
import (
"github.com/cf-guardian/guardian-backend/container"
+ "github.com/cf-guardian/guardian-backend/rootfs"
"github.com/cf-guardian/guardian-backend/system_info"
"github.com/cloudfoundry-incubator/garden/warden"
"time"
+ "github.com/cf-guardian/guardian-backend/config_builder"
+ "github.com/cf-guardian/guardian-backend/utils/gerror"
+ "path/filepath"
)
type guardianBackend struct {
systemInfo system_info.Provider
+ rootfs rootfs.RootFS
}
-func New(depotPath string) warden.Backend {
- systemInfo := system_info.NewProvider(depotPath)
+func newGuardianBackend(depotPath string, rootfs rootfs.RootFS, configBuilder config_builder.ConfigBuilder) (warden.Backend, gerror.Gerror) {
+ systemInfo := system_info.NewProvider(filepath.Join(depotPath, "depot"))
return &guardianBackend{
systemInfo: systemInfo,
- }
+ }, nil
}
func (b *guardianBackend) Ping() error {
@@ -0,0 +1,26 @@
+package guardian_backend
+
+import (
+ "github.com/cloudfoundry-incubator/garden/warden"
+ "github.com/cf-guardian/guardian-backend/rootfs"
+ "github.com/cf-guardian/guardian-backend/config_builder"
+ "github.com/cf-guardian/guardian-backend/utils/gerror"
+)
+
+func Wire(depotPath string, rwBaseDir string) (warden.Backend, gerror.Gerror) {
+ rootfs, err := rootfs.Wire(rwBaseDir)
+ if err != nil {
+ return nil, err
+ }
+
+ configBuilder, err := config_builder.Wire()
+ if err != nil {
+ return nil, err
+ }
+
+ return WireWith(depotPath, rootfs, configBuilder)
+}
+
+func WireWith(depotPath string, rootfs rootfs.RootFS, configBuilder config_builder.ConfigBuilder) (warden.Backend, gerror.Gerror) {
+ return newGuardianBackend(depotPath, rootfs, configBuilder)
+}
View
@@ -105,7 +105,7 @@ var rootSubdirs []string = []string{`proc`, `dev`, `etc`, `home`, `sbin`, `var`,
directory as a base for the writable portion of generated root filesystems. The SyscallFS interface value
must not be nil to ensure that the caller has sufficient privileges to perform mounts, etc.
*/
-func NewRootFS(sc syscall.SyscallFS, f fileutils.Fileutils, rwBaseDir string) (RootFS, gerror.Gerror) {
+func newRootFS(sc syscall.SyscallFS, f fileutils.Fileutils, rwBaseDir string) (RootFS, gerror.Gerror) {
if sc == nil {
return nil, gerror.New(ErrNilSyscallFS, "nil SyscallFS")
}
View
@@ -35,7 +35,7 @@ func TestNilSyscallFS(t *testing.T) {
mockCtrl, mockFileUtils, _ := setupMocks(t)
defer mockCtrl.Finish()
- rfs, gerr := rootfs.NewRootFS(nil, mockFileUtils, "")
+ rfs, gerr := rootfs.WireWith("", nil, mockFileUtils)
if rfs != nil || !gerr.EqualTag(rootfs.ErrNilSyscallFS) {
t.Errorf("Incorrect return values (%s, %s)", rfs, gerr)
return
@@ -48,7 +48,7 @@ func TestNonExistentReadWriteBaseDir(t *testing.T) {
mockFileUtils.EXPECT().Filemode("/nosuch").Return(os.FileMode(0), gerror.New(fileutils.ErrFileNotFound, "an error"))
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, "/nosuch")
+ rfs, gerr := rootfs.WireWith("/nosuch", mockSyscallFS, mockFileUtils)
if rfs != nil || !gerr.EqualTag(rootfs.ErrRwBaseDirMissing) {
t.Errorf("Incorrect return values (%s, %s)", rfs, gerr)
return
@@ -65,7 +65,7 @@ func TestNonDirReadWriteBaseDir(t *testing.T) {
filePath := test_support.CreateFile(tempDir, "testFile")
mockFileUtils.EXPECT().Filemode(filePath).Return(os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, filePath)
+ rfs, gerr := rootfs.WireWith(filePath, mockSyscallFS, mockFileUtils)
if rfs != nil || !gerr.EqualTag(rootfs.ErrRwBaseDirIsFile) {
t.Errorf("Incorrect return values (%s, %s)", rfs, gerr)
return
@@ -82,7 +82,7 @@ func TestReadOnlyReadWriteBaseDir(t *testing.T) {
dirPath := test_support.CreateDirWithMode(tempDir, "test-rootfs", os.FileMode(0400))
mockFileUtils.EXPECT().Filemode(dirPath).Return(os.ModeDir|os.FileMode(0100), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, dirPath)
+ rfs, gerr := rootfs.WireWith(dirPath, mockSyscallFS, mockFileUtils)
if rfs != nil || !gerr.EqualTag(rootfs.ErrRwBaseDirNotRw) {
t.Errorf("Incorrect return values (%s, %s)", rfs, gerr)
return
@@ -97,7 +97,7 @@ func TestGenerate(t *testing.T) {
defer test_support.CleanupDirs(t, tempDir)
mockFileUtils.EXPECT().Filemode(tempDir).Return(os.ModeDir|os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, tempDir)
+ rfs, gerr := rootfs.WireWith(tempDir, mockSyscallFS, mockFileUtils)
if gerr != nil {
t.Errorf("%s", gerr)
return
@@ -142,7 +142,7 @@ func testGenerateBackoutAfterBindMountReadWriteError(i int, t *testing.T) {
defer test_support.CleanupDirs(t, tempDir)
mockFileUtils.EXPECT().Filemode(tempDir).Return(os.ModeDir|os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, tempDir)
+ rfs, gerr := rootfs.WireWith(tempDir, mockSyscallFS, mockFileUtils)
if gerr != nil {
t.Errorf("%s", gerr)
return
@@ -187,7 +187,7 @@ func TestRemove(t *testing.T) {
defer test_support.CleanupDirs(t, tempDir)
mockFileUtils.EXPECT().Filemode(tempDir).Return(os.ModeDir|os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, tempDir)
+ rfs, gerr := rootfs.WireWith(tempDir, mockSyscallFS, mockFileUtils)
if gerr != nil {
t.Errorf("%s", gerr)
return
@@ -223,7 +223,7 @@ func testRemoveUnmountSubdirFailure(i int, t *testing.T) {
defer test_support.CleanupDirs(t, tempDir)
mockFileUtils.EXPECT().Filemode(tempDir).Return(os.ModeDir|os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, tempDir)
+ rfs, gerr := rootfs.WireWith(tempDir, mockSyscallFS, mockFileUtils)
if gerr != nil {
t.Errorf("%s", gerr)
return
@@ -259,7 +259,7 @@ func TestRemoveUnmountRootFailure(t *testing.T) {
defer test_support.CleanupDirs(t, tempDir)
mockFileUtils.EXPECT().Filemode(tempDir).Return(os.ModeDir|os.FileMode(0700), nil)
- rfs, gerr := rootfs.NewRootFS(mockSyscallFS, mockFileUtils, tempDir)
+ rfs, gerr := rootfs.WireWith(tempDir, mockSyscallFS, mockFileUtils)
if gerr != nil {
t.Errorf("%s", gerr)
return
View
@@ -0,0 +1,26 @@
+package rootfs
+
+import (
+ "github.com/cf-guardian/guardian-backend/utils/fileutils"
+ "github.com/cf-guardian/guardian-backend/utils/syscall"
+ "github.com/cf-guardian/guardian-backend/utils/syscall/syscall_linux"
+ "github.com/cf-guardian/guardian-backend/utils/gerror"
+)
+
+func Wire(rwBaseDir string) (RootFS, gerror.Gerror) {
+ sc, err := syscall_linux.WireFS()
+ if err != nil {
+ return nil, err
+ }
+
+ f, err := fileutils.Wire()
+ if err != nil {
+ return nil, err
+ }
+
+ return WireWith(rwBaseDir, sc, f)
+}
+
+func WireWith(rwBaseDir string, sc syscall.SyscallFS, f fileutils.Fileutils) (RootFS, gerror.Gerror) {
+ return newRootFS(sc, f, rwBaseDir)
+}
@@ -76,8 +76,8 @@ type Fileutils interface {
type futils struct {
}
-func New() Fileutils {
- return &futils{}
+func newFileutils() (Fileutils, gerror.Gerror) {
+ return &futils{}, nil
}
func (f *futils) Copy(destPath string, srcPath string) gerror.Gerror {
Oops, something went wrong.

0 comments on commit a023fa4

Please sign in to comment.