Skip to content

Commit 59fd89b

Browse files
committed
Remove names boilerplate.
Add more documentation. Separate TCP vs Unix serving methods. Signed-off-by: David Calavera <david.calavera@gmail.com>
1 parent 381469a commit 59fd89b

File tree

3 files changed

+90
-43
lines changed

3 files changed

+90
-43
lines changed

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,27 @@ Go handler to create external volume extensions for Docker.
66

77
This library is designed to be integrated in your program.
88

9-
1. Implement the `VolumeDriver` interface.
10-
2. Initialize a `VolumeHander` with your implementation.
11-
2. Call the method `ListenAndServe` from the `VolumeHandler`.
9+
1. Implement the `dkv.Driver` interface.
10+
2. Initialize a `dkv.Hander` with your implementation.
11+
3. Call either `ServeTCP` or `ServeUnix` from the `dkv.Handler`.
12+
13+
### Example using TCP sockets:
14+
15+
```go
16+
d := MyVolumeDriver{}
17+
h := dkv.NewHandler(d)
18+
h.ServeTCP("test_volume", ":8080")
19+
```
20+
21+
### Example using Unix sockets:
1222

1323
```go
1424
d := MyVolumeDriver{}
15-
h := volumeapi.NewVolumeHandler(d)
16-
h.ListenAndServe("tcp", ":8080", "")
25+
h := dkv.NewHandler(d)
26+
h.ServeUnix("root", "/usr/share/docker/plugins/test_volume.sock")
1727
```
1828

19-
See a full example in https://github.com/calavera/docker-volume-keywhiz-fs
29+
See a full example in https://github.com/calavera/docker-volume-glusterfs
2030

2131
## License
2232

api.go

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1-
package volumeapi
1+
package dkv
22

33
import (
44
"encoding/json"
55
"fmt"
6+
"io/ioutil"
67
"net"
78
"net/http"
9+
"os"
10+
"path/filepath"
811
)
912

1013
const (
11-
DefaultDockerRootDirectory = "/var/lib/docker/volumes"
14+
// DefaultDockerRootDirectory is the default directory where volumes will be created.
15+
DefaultDockerRootDirectory = "/var/lib/docker/volumes"
16+
1217
defaultContentTypeV1 = "appplication/vnd.docker.plugins.v1+json"
1318
defaultImplementationManifest = `{"Implements": ["VolumeDriver"]}`
19+
pluginSpecDir = "/usr/share/docker/plugins"
1420

1521
activatePath = "/Plugin.Activate"
1622
createPath = "/VolumeDriver.Create"
@@ -20,64 +26,69 @@ const (
2026
unmountPath = "/VolumeDriver.Unmount"
2127
)
2228

23-
type VolumeRequest struct {
29+
// Request is the structure that docker's requests are deserialized to.
30+
type Request struct {
2431
Name string
2532
}
2633

27-
type VolumeResponse struct {
34+
// Response is the strucutre that the plugin's responses are serialized to.
35+
type Response struct {
2836
Mountpoint string
2937
Err string
3038
}
3139

32-
type VolumeDriver interface {
33-
Create(VolumeRequest) VolumeResponse
34-
Remove(VolumeRequest) VolumeResponse
35-
Path(VolumeRequest) VolumeResponse
36-
Mount(VolumeRequest) VolumeResponse
37-
Unmount(VolumeRequest) VolumeResponse
40+
// Driver represent the interface a driver must fulfill.
41+
type Driver interface {
42+
Create(Request) Response
43+
Remove(Request) Response
44+
Path(Request) Response
45+
Mount(Request) Response
46+
Unmount(Request) Response
3847
}
3948

40-
type VolumeHandler struct {
41-
handler VolumeDriver
42-
mux *http.ServeMux
49+
// Handler forwards requests and responses between the docker daemon and the plugin.
50+
type Handler struct {
51+
driver Driver
52+
mux *http.ServeMux
4353
}
4454

45-
type actionHandler func(VolumeRequest) VolumeResponse
55+
type actionHandler func(Request) Response
4656

47-
func NewVolumeHandler(handler VolumeDriver) *VolumeHandler {
48-
h := &VolumeHandler{handler, http.NewServeMux()}
57+
// NewHandler initializes the request handler with a driver implementation.
58+
func NewHandler(driver Driver) *Handler {
59+
h := &Handler{driver, http.NewServeMux()}
4960
h.initMux()
5061
return h
5162
}
5263

53-
func (h *VolumeHandler) initMux() {
64+
func (h *Handler) initMux() {
5465
h.mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) {
5566
w.Header().Set("Content-Type", defaultContentTypeV1)
5667
fmt.Fprintln(w, defaultImplementationManifest)
5768
})
5869

59-
h.handle(createPath, func(req VolumeRequest) VolumeResponse {
60-
return h.handler.Create(req)
70+
h.handle(createPath, func(req Request) Response {
71+
return h.driver.Create(req)
6172
})
6273

63-
h.handle(remotePath, func(req VolumeRequest) VolumeResponse {
64-
return h.handler.Remove(req)
74+
h.handle(remotePath, func(req Request) Response {
75+
return h.driver.Remove(req)
6576
})
6677

67-
h.handle(hostVirtualPath, func(req VolumeRequest) VolumeResponse {
68-
return h.handler.Path(req)
78+
h.handle(hostVirtualPath, func(req Request) Response {
79+
return h.driver.Path(req)
6980
})
7081

71-
h.handle(mountPath, func(req VolumeRequest) VolumeResponse {
72-
return h.handler.Mount(req)
82+
h.handle(mountPath, func(req Request) Response {
83+
return h.driver.Mount(req)
7384
})
7485

75-
h.handle(unmountPath, func(req VolumeRequest) VolumeResponse {
76-
return h.handler.Unmount(req)
86+
h.handle(unmountPath, func(req Request) Response {
87+
return h.driver.Unmount(req)
7788
})
7889
}
7990

80-
func (h *VolumeHandler) handle(name string, actionCall actionHandler) {
91+
func (h *Handler) handle(name string, actionCall actionHandler) {
8192
h.mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
8293
req, err := decodeRequest(w, r)
8394
if err != nil {
@@ -90,7 +101,19 @@ func (h *VolumeHandler) handle(name string, actionCall actionHandler) {
90101
})
91102
}
92103

93-
func (h *VolumeHandler) ListenAndServe(proto, addr, group string) error {
104+
// ServeTCP makes the handler to listen for request in a given TCP address.
105+
// It also writes the spec file on the right directory for docker to read.
106+
func (h *Handler) ServeTCP(pluginName, addr string) error {
107+
return h.listenAndServe("tcp", addr, pluginName)
108+
}
109+
110+
// ServeUnix makes the handler to listen for requests in a unix socket.
111+
// It also creates the socket file on the right directory for docker to read.
112+
func (h *Handler) ServeUnix(systemGroup, addr string) error {
113+
return h.listenAndServe("unix", addr, systemGroup)
114+
}
115+
116+
func (h *Handler) listenAndServe(proto, addr, group string) error {
94117
server := http.Server{
95118
Addr: addr,
96119
Handler: h.mux,
@@ -102,7 +125,10 @@ func (h *VolumeHandler) ListenAndServe(proto, addr, group string) error {
102125
var err error
103126
switch proto {
104127
case "tcp":
105-
l, err = newTcpSocket(addr, nil, start)
128+
l, err = newTCPSocket(addr, nil, start)
129+
if err == nil {
130+
err = writeSpec(group, l.Addr().String())
131+
}
106132
case "unix":
107133
l, err = newUnixSocket(addr, group, start)
108134
}
@@ -114,17 +140,27 @@ func (h *VolumeHandler) ListenAndServe(proto, addr, group string) error {
114140
return server.Serve(l)
115141
}
116142

117-
func decodeRequest(w http.ResponseWriter, r *http.Request) (req VolumeRequest, err error) {
143+
func decodeRequest(w http.ResponseWriter, r *http.Request) (req Request, err error) {
118144
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
119145
http.Error(w, err.Error(), http.StatusBadRequest)
120146
}
121147
return
122148
}
123149

124-
func encodeResponse(w http.ResponseWriter, res VolumeResponse) {
150+
func encodeResponse(w http.ResponseWriter, res Response) {
125151
w.Header().Set("Content-Type", defaultContentTypeV1)
126152
if res.Err != "" {
127153
w.WriteHeader(http.StatusInternalServerError)
128154
}
129155
json.NewEncoder(w).Encode(res)
130156
}
157+
158+
func writeSpec(name, addr string) error {
159+
if err := os.MkdirAll(pluginSpecDir, 0755); err != nil {
160+
return err
161+
}
162+
163+
spec := filepath.Join(pluginSpecDir, name+".spec")
164+
url := "tcp://" + addr
165+
return ioutil.WriteFile(spec, []byte(url), 0644)
166+
}

connection.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package volumeapi
1+
package dkv
22

33
import (
44
"crypto/tls"
@@ -21,7 +21,8 @@ import (
2121
// https://github.com/docker/docker/blob/master/api/server/unix_socket.go
2222
// https://github.com/docker/docker/blob/master/api/server/tcp_socket.go
2323

24-
type TlsConfig struct {
24+
// TLSConfig is the structure that represents the TLS configuration.
25+
type TLSConfig struct {
2526
CA string
2627
Certificate string
2728
Key string
@@ -49,20 +50,20 @@ func newUnixSocket(path, group string, activate <-chan struct{}) (net.Listener,
4950
return l, nil
5051
}
5152

52-
func newTcpSocket(addr string, config *TlsConfig, activate <-chan struct{}) (net.Listener, error) {
53+
func newTCPSocket(addr string, config *TLSConfig, activate <-chan struct{}) (net.Listener, error) {
5354
l, err := listenbuffer.NewListenBuffer("tcp", addr, activate)
5455
if err != nil {
5556
return nil, err
5657
}
5758
if config != nil {
58-
if l, err = setupTls(l, config); err != nil {
59+
if l, err = setupTLS(l, config); err != nil {
5960
return nil, err
6061
}
6162
}
6263
return l, nil
6364
}
6465

65-
func setupTls(l net.Listener, config *TlsConfig) (net.Listener, error) {
66+
func setupTLS(l net.Listener, config *TLSConfig) (net.Listener, error) {
6667
tlsCert, err := tls.LoadX509KeyPair(config.Certificate, config.Key)
6768
if err != nil {
6869
if os.IsNotExist(err) {

0 commit comments

Comments
 (0)