Signed-off-by: Antonio Murdaca <me@runcom.ninja>
| ... | ... |
@@ -1,13 +1,14 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
"io" |
| 6 | 7 |
"net/url" |
| 7 | 8 |
"os" |
| 8 | 9 |
|
| 9 | 10 |
"github.com/Sirupsen/logrus" |
| 10 |
- "github.com/docker/docker/engine" |
|
| 11 |
+ "github.com/docker/docker/api/types" |
|
| 11 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 12 | 13 |
"github.com/docker/docker/pkg/promise" |
| 13 | 14 |
"github.com/docker/docker/pkg/signal" |
| ... | ... |
@@ -65,12 +66,12 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 65 | 65 |
return err |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
- env := engine.Env{}
|
|
| 69 |
- if err := env.Decode(stream); err != nil {
|
|
| 68 |
+ var c types.ContainerJSON |
|
| 69 |
+ if err := json.NewDecoder(stream).Decode(&c); err != nil {
|
|
| 70 | 70 |
return err |
| 71 | 71 |
} |
| 72 |
- config := env.GetSubEnv("Config")
|
|
| 73 |
- tty = config.GetBool("Tty")
|
|
| 72 |
+ |
|
| 73 |
+ tty = c.Config.Tty |
|
| 74 | 74 |
|
| 75 | 75 |
if !tty {
|
| 76 | 76 |
sigc := cli.forwardAllSignals(cmd.Arg(0)) |
| ... | ... |
@@ -82,7 +83,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
| 82 | 82 |
v := url.Values{}
|
| 83 | 83 |
v.Set("stream", "1")
|
| 84 | 84 |
|
| 85 |
- if *openStdin && config.GetBool("OpenStdin") {
|
|
| 85 |
+ if *openStdin && c.Config.OpenStdin {
|
|
| 86 | 86 |
v.Set("stdin", "1")
|
| 87 | 87 |
in = cli.in |
| 88 | 88 |
} |
| ... | ... |
@@ -22,7 +22,6 @@ import ( |
| 22 | 22 |
"github.com/docker/docker/api/types" |
| 23 | 23 |
"github.com/docker/docker/autogen/dockerversion" |
| 24 | 24 |
"github.com/docker/docker/cliconfig" |
| 25 |
- "github.com/docker/docker/engine" |
|
| 26 | 25 |
"github.com/docker/docker/pkg/jsonmessage" |
| 27 | 26 |
"github.com/docker/docker/pkg/signal" |
| 28 | 27 |
"github.com/docker/docker/pkg/stdcopy" |
| ... | ... |
@@ -42,18 +41,8 @@ func (cli *DockerCli) HTTPClient() *http.Client {
|
| 42 | 42 |
func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
|
| 43 | 43 |
params := bytes.NewBuffer(nil) |
| 44 | 44 |
if data != nil {
|
| 45 |
- if env, ok := data.(engine.Env); ok {
|
|
| 46 |
- if err := env.Encode(params); err != nil {
|
|
| 47 |
- return nil, err |
|
| 48 |
- } |
|
| 49 |
- } else {
|
|
| 50 |
- buf, err := json.Marshal(data) |
|
| 51 |
- if err != nil {
|
|
| 52 |
- return nil, err |
|
| 53 |
- } |
|
| 54 |
- if _, err := params.Write(buf); err != nil {
|
|
| 55 |
- return nil, err |
|
| 56 |
- } |
|
| 45 |
+ if err := json.NewEncoder(params).Encode(data); err != nil {
|
|
| 46 |
+ return nil, err |
|
| 57 | 47 |
} |
| 58 | 48 |
} |
| 59 | 49 |
return params, nil |
| ... | ... |
@@ -1,13 +1,14 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
"runtime" |
| 6 | 7 |
|
| 7 | 8 |
"github.com/Sirupsen/logrus" |
| 8 | 9 |
"github.com/docker/docker/api" |
| 10 |
+ "github.com/docker/docker/api/types" |
|
| 9 | 11 |
"github.com/docker/docker/autogen/dockerversion" |
| 10 |
- "github.com/docker/docker/engine" |
|
| 11 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 12 | 13 |
) |
| 13 | 14 |
|
| ... | ... |
@@ -32,28 +33,24 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
| 32 | 32 |
} |
| 33 | 33 |
fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH) |
| 34 | 34 |
|
| 35 |
- body, _, err := readBody(cli.call("GET", "/version", nil, nil))
|
|
| 35 |
+ stream, _, err := cli.call("GET", "/version", nil, nil)
|
|
| 36 | 36 |
if err != nil {
|
| 37 | 37 |
return err |
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 |
- out := engine.NewOutput() |
|
| 41 |
- remoteVersion, err := out.AddEnv() |
|
| 42 |
- if err != nil {
|
|
| 43 |
- logrus.Errorf("Error reading remote version: %s", err)
|
|
| 44 |
- return err |
|
| 45 |
- } |
|
| 46 |
- if _, err := out.Write(body); err != nil {
|
|
| 40 |
+ var v types.Version |
|
| 41 |
+ if err := json.NewDecoder(stream).Decode(&v); err != nil {
|
|
| 47 | 42 |
logrus.Errorf("Error reading remote version: %s", err)
|
| 48 | 43 |
return err |
| 49 | 44 |
} |
| 50 |
- out.Close() |
|
| 51 |
- fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
|
|
| 52 |
- if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" {
|
|
| 53 |
- fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion) |
|
| 45 |
+ |
|
| 46 |
+ fmt.Fprintf(cli.out, "Server version: %s\n", v.Version) |
|
| 47 |
+ if v.ApiVersion != "" {
|
|
| 48 |
+ fmt.Fprintf(cli.out, "Server API version: %s\n", v.ApiVersion) |
|
| 54 | 49 |
} |
| 55 |
- fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
|
|
| 56 |
- fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
|
|
| 57 |
- fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", remoteVersion.Get("Os"), remoteVersion.Get("Arch"))
|
|
| 50 |
+ fmt.Fprintf(cli.out, "Go version (server): %s\n", v.GoVersion) |
|
| 51 |
+ fmt.Fprintf(cli.out, "Git commit (server): %s\n", v.GitCommit) |
|
| 52 |
+ fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", v.Os, v.Arch) |
|
| 53 |
+ |
|
| 58 | 54 |
return nil |
| 59 | 55 |
} |
| ... | ... |
@@ -1,9 +1,6 @@ |
| 1 | 1 |
package server |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "runtime" |
|
| 5 |
- "time" |
|
| 6 |
- |
|
| 7 | 4 |
"encoding/base64" |
| 8 | 5 |
"encoding/json" |
| 9 | 6 |
"fmt" |
| ... | ... |
@@ -11,8 +8,10 @@ import ( |
| 11 | 11 |
"net" |
| 12 | 12 |
"net/http" |
| 13 | 13 |
"os" |
| 14 |
+ "runtime" |
|
| 14 | 15 |
"strconv" |
| 15 | 16 |
"strings" |
| 17 |
+ "time" |
|
| 16 | 18 |
|
| 17 | 19 |
"code.google.com/p/go.net/websocket" |
| 18 | 20 |
"github.com/gorilla/mux" |
| ... | ... |
@@ -25,7 +24,6 @@ import ( |
| 25 | 25 |
"github.com/docker/docker/cliconfig" |
| 26 | 26 |
"github.com/docker/docker/daemon" |
| 27 | 27 |
"github.com/docker/docker/daemon/networkdriver/bridge" |
| 28 |
- "github.com/docker/docker/engine" |
|
| 29 | 28 |
"github.com/docker/docker/graph" |
| 30 | 29 |
"github.com/docker/docker/pkg/jsonmessage" |
| 31 | 30 |
"github.com/docker/docker/pkg/parsers" |
| ... | ... |
@@ -53,26 +51,31 @@ type ServerConfig struct {
|
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 | 55 |
type Server struct {
|
| 56 |
- daemon *daemon.Daemon |
|
| 57 |
- cfg *ServerConfig |
|
| 58 |
- router *mux.Router |
|
| 59 |
- start chan struct{}
|
|
| 60 |
- |
|
| 61 |
- // TODO: delete engine |
|
| 62 |
- eng *engine.Engine |
|
| 56 |
+ daemon *daemon.Daemon |
|
| 57 |
+ cfg *ServerConfig |
|
| 58 |
+ router *mux.Router |
|
| 59 |
+ start chan struct{}
|
|
| 60 |
+ servers []serverCloser |
|
| 63 | 61 |
} |
| 64 | 62 |
|
| 65 |
-func New(cfg *ServerConfig, eng *engine.Engine) *Server {
|
|
| 63 |
+func New(cfg *ServerConfig) *Server {
|
|
| 66 | 64 |
srv := &Server{
|
| 67 | 65 |
cfg: cfg, |
| 68 | 66 |
start: make(chan struct{}),
|
| 69 |
- eng: eng, |
|
| 70 | 67 |
} |
| 71 |
- r := createRouter(srv, eng) |
|
| 68 |
+ r := createRouter(srv) |
|
| 72 | 69 |
srv.router = r |
| 73 | 70 |
return srv |
| 74 | 71 |
} |
| 75 | 72 |
|
| 73 |
+func (s *Server) Close() {
|
|
| 74 |
+ for _, srv := range s.servers {
|
|
| 75 |
+ if err := srv.Close(); err != nil {
|
|
| 76 |
+ logrus.Error(err) |
|
| 77 |
+ } |
|
| 78 |
+ } |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 76 | 81 |
func (s *Server) SetDaemon(d *daemon.Daemon) {
|
| 77 | 82 |
s.daemon = d |
| 78 | 83 |
} |
| ... | ... |
@@ -92,19 +95,15 @@ func (s *Server) ServeApi(protoAddrs []string) error {
|
| 92 | 92 |
if len(protoAddrParts) != 2 {
|
| 93 | 93 |
return fmt.Errorf("bad format, expected PROTO://ADDR")
|
| 94 | 94 |
} |
| 95 |
+ srv, err := s.newServer(protoAddrParts[0], protoAddrParts[1]) |
|
| 96 |
+ if err != nil {
|
|
| 97 |
+ return err |
|
| 98 |
+ } |
|
| 99 |
+ s.servers = append(s.servers, srv) |
|
| 100 |
+ |
|
| 95 | 101 |
go func(proto, addr string) {
|
| 96 | 102 |
logrus.Infof("Listening for HTTP on %s (%s)", proto, addr)
|
| 97 |
- srv, err := s.newServer(proto, addr) |
|
| 98 |
- if err != nil {
|
|
| 99 |
- chErrors <- err |
|
| 100 |
- return |
|
| 101 |
- } |
|
| 102 |
- s.eng.OnShutdown(func() {
|
|
| 103 |
- if err := srv.Close(); err != nil {
|
|
| 104 |
- logrus.Error(err) |
|
| 105 |
- } |
|
| 106 |
- }) |
|
| 107 |
- if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
|
| 103 |
+ if err := srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
|
| 108 | 104 |
err = nil |
| 109 | 105 |
} |
| 110 | 106 |
chErrors <- err |
| ... | ... |
@@ -133,7 +132,7 @@ func (s *HttpServer) Close() error {
|
| 133 | 133 |
return s.l.Close() |
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 |
-type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error |
|
| 136 |
+type HttpApiFunc func(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error |
|
| 137 | 137 |
|
| 138 | 138 |
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
| 139 | 139 |
conn, _, err := w.(http.Hijacker).Hijack() |
| ... | ... |
@@ -230,16 +229,7 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
|
| 230 | 230 |
return json.NewEncoder(w).Encode(v) |
| 231 | 231 |
} |
| 232 | 232 |
|
| 233 |
-func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
|
|
| 234 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 235 |
- if flush {
|
|
| 236 |
- out.Add(utils.NewWriteFlusher(w)) |
|
| 237 |
- } else {
|
|
| 238 |
- out.Add(w) |
|
| 239 |
- } |
|
| 240 |
-} |
|
| 241 |
- |
|
| 242 |
-func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 233 |
+func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 243 | 234 |
var config *cliconfig.AuthConfig |
| 244 | 235 |
err := json.NewDecoder(r.Body).Decode(&config) |
| 245 | 236 |
r.Body.Close() |
| ... | ... |
@@ -255,7 +245,7 @@ func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.Re |
| 255 | 255 |
}) |
| 256 | 256 |
} |
| 257 | 257 |
|
| 258 |
-func (s *Server) getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 258 |
+func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 259 | 259 |
w.Header().Set("Content-Type", "application/json")
|
| 260 | 260 |
|
| 261 | 261 |
v := &types.Version{
|
| ... | ... |
@@ -273,7 +263,7 @@ func (s *Server) getVersion(eng *engine.Engine, version version.Version, w http. |
| 273 | 273 |
return writeJSON(w, http.StatusOK, v) |
| 274 | 274 |
} |
| 275 | 275 |
|
| 276 |
-func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 276 |
+func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 277 | 277 |
if vars == nil {
|
| 278 | 278 |
return fmt.Errorf("Missing parameter")
|
| 279 | 279 |
} |
| ... | ... |
@@ -308,7 +298,7 @@ func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, |
| 308 | 308 |
return nil |
| 309 | 309 |
} |
| 310 | 310 |
|
| 311 |
-func (s *Server) postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 311 |
+func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 312 | 312 |
if vars == nil {
|
| 313 | 313 |
return fmt.Errorf("Missing parameter")
|
| 314 | 314 |
} |
| ... | ... |
@@ -332,7 +322,7 @@ func (s *Server) postContainersPause(eng *engine.Engine, version version.Version |
| 332 | 332 |
return nil |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
-func (s *Server) postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 335 |
+func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 336 | 336 |
if vars == nil {
|
| 337 | 337 |
return fmt.Errorf("Missing parameter")
|
| 338 | 338 |
} |
| ... | ... |
@@ -356,7 +346,7 @@ func (s *Server) postContainersUnpause(eng *engine.Engine, version version.Versi |
| 356 | 356 |
return nil |
| 357 | 357 |
} |
| 358 | 358 |
|
| 359 |
-func (s *Server) getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 359 |
+func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 360 | 360 |
if vars == nil {
|
| 361 | 361 |
return fmt.Errorf("Missing parameter")
|
| 362 | 362 |
} |
| ... | ... |
@@ -364,7 +354,7 @@ func (s *Server) getContainersExport(eng *engine.Engine, version version.Version |
| 364 | 364 |
return s.daemon.ContainerExport(vars["name"], w) |
| 365 | 365 |
} |
| 366 | 366 |
|
| 367 |
-func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 367 |
+func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 368 | 368 |
if err := parseForm(r); err != nil {
|
| 369 | 369 |
return err |
| 370 | 370 |
} |
| ... | ... |
@@ -405,7 +395,7 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht |
| 405 | 405 |
return writeJSON(w, http.StatusOK, legacyImages) |
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 |
-func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 408 |
+func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 409 | 409 |
w.Header().Set("Content-Type", "application/json")
|
| 410 | 410 |
|
| 411 | 411 |
info, err := s.daemon.SystemInfo() |
| ... | ... |
@@ -416,7 +406,7 @@ func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.Res |
| 416 | 416 |
return writeJSON(w, http.StatusOK, info) |
| 417 | 417 |
} |
| 418 | 418 |
|
| 419 |
-func (s *Server) getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 419 |
+func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 420 | 420 |
if err := parseForm(r); err != nil {
|
| 421 | 421 |
return err |
| 422 | 422 |
} |
| ... | ... |
@@ -520,7 +510,7 @@ func (s *Server) getEvents(eng *engine.Engine, version version.Version, w http.R |
| 520 | 520 |
} |
| 521 | 521 |
} |
| 522 | 522 |
|
| 523 |
-func (s *Server) getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 523 |
+func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 524 | 524 |
if vars == nil {
|
| 525 | 525 |
return fmt.Errorf("Missing parameter")
|
| 526 | 526 |
} |
| ... | ... |
@@ -534,7 +524,7 @@ func (s *Server) getImagesHistory(eng *engine.Engine, version version.Version, w |
| 534 | 534 |
return writeJSON(w, http.StatusOK, history) |
| 535 | 535 |
} |
| 536 | 536 |
|
| 537 |
-func (s *Server) getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 537 |
+func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 538 | 538 |
if vars == nil {
|
| 539 | 539 |
return fmt.Errorf("Missing parameter")
|
| 540 | 540 |
} |
| ... | ... |
@@ -553,7 +543,7 @@ func (s *Server) getContainersChanges(eng *engine.Engine, version version.Versio |
| 553 | 553 |
return writeJSON(w, http.StatusOK, changes) |
| 554 | 554 |
} |
| 555 | 555 |
|
| 556 |
-func (s *Server) getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 556 |
+func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 557 | 557 |
if version.LessThan("1.4") {
|
| 558 | 558 |
return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
| 559 | 559 |
} |
| ... | ... |
@@ -574,7 +564,7 @@ func (s *Server) getContainersTop(eng *engine.Engine, version version.Version, w |
| 574 | 574 |
return writeJSON(w, http.StatusOK, procList) |
| 575 | 575 |
} |
| 576 | 576 |
|
| 577 |
-func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 577 |
+func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 578 | 578 |
if err := parseForm(r); err != nil {
|
| 579 | 579 |
return err |
| 580 | 580 |
} |
| ... | ... |
@@ -603,7 +593,7 @@ func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version, |
| 603 | 603 |
return writeJSON(w, http.StatusOK, containers) |
| 604 | 604 |
} |
| 605 | 605 |
|
| 606 |
-func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 606 |
+func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 607 | 607 |
if err := parseForm(r); err != nil {
|
| 608 | 608 |
return err |
| 609 | 609 |
} |
| ... | ... |
@@ -614,7 +604,7 @@ func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, |
| 614 | 614 |
return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w)) |
| 615 | 615 |
} |
| 616 | 616 |
|
| 617 |
-func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 617 |
+func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 618 | 618 |
if err := parseForm(r); err != nil {
|
| 619 | 619 |
return err |
| 620 | 620 |
} |
| ... | ... |
@@ -644,7 +634,7 @@ func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, |
| 644 | 644 |
return nil |
| 645 | 645 |
} |
| 646 | 646 |
|
| 647 |
-func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 647 |
+func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 648 | 648 |
if err := parseForm(r); err != nil {
|
| 649 | 649 |
return err |
| 650 | 650 |
} |
| ... | ... |
@@ -662,7 +652,7 @@ func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w ht |
| 662 | 662 |
return nil |
| 663 | 663 |
} |
| 664 | 664 |
|
| 665 |
-func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 665 |
+func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 666 | 666 |
if err := parseForm(r); err != nil {
|
| 667 | 667 |
return err |
| 668 | 668 |
} |
| ... | ... |
@@ -708,7 +698,7 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http. |
| 708 | 708 |
} |
| 709 | 709 |
|
| 710 | 710 |
// Creates an image from Pull or from Import |
| 711 |
-func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 711 |
+func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 712 | 712 |
if err := parseForm(r); err != nil {
|
| 713 | 713 |
return err |
| 714 | 714 |
} |
| ... | ... |
@@ -788,7 +778,7 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w |
| 788 | 788 |
return nil |
| 789 | 789 |
} |
| 790 | 790 |
|
| 791 |
-func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 791 |
+func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 792 | 792 |
if err := parseForm(r); err != nil {
|
| 793 | 793 |
return err |
| 794 | 794 |
} |
| ... | ... |
@@ -818,7 +808,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w |
| 818 | 818 |
return json.NewEncoder(w).Encode(query.Results) |
| 819 | 819 |
} |
| 820 | 820 |
|
| 821 |
-func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 821 |
+func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 822 | 822 |
if vars == nil {
|
| 823 | 823 |
return fmt.Errorf("Missing parameter")
|
| 824 | 824 |
} |
| ... | ... |
@@ -875,7 +865,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h |
| 875 | 875 |
|
| 876 | 876 |
} |
| 877 | 877 |
|
| 878 |
-func (s *Server) getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 878 |
+func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 879 | 879 |
if vars == nil {
|
| 880 | 880 |
return fmt.Errorf("Missing parameter")
|
| 881 | 881 |
} |
| ... | ... |
@@ -907,11 +897,11 @@ func (s *Server) getImagesGet(eng *engine.Engine, version version.Version, w htt |
| 907 | 907 |
|
| 908 | 908 |
} |
| 909 | 909 |
|
| 910 |
-func (s *Server) postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 910 |
+func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 911 | 911 |
return s.daemon.Repositories().Load(r.Body, w) |
| 912 | 912 |
} |
| 913 | 913 |
|
| 914 |
-func (s *Server) postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 914 |
+func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 915 | 915 |
if err := parseForm(r); err != nil {
|
| 916 | 916 |
return nil |
| 917 | 917 |
} |
| ... | ... |
@@ -939,7 +929,7 @@ func (s *Server) postContainersCreate(eng *engine.Engine, version version.Versio |
| 939 | 939 |
}) |
| 940 | 940 |
} |
| 941 | 941 |
|
| 942 |
-func (s *Server) postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 942 |
+func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 943 | 943 |
if err := parseForm(r); err != nil {
|
| 944 | 944 |
return err |
| 945 | 945 |
} |
| ... | ... |
@@ -961,7 +951,7 @@ func (s *Server) postContainersRestart(eng *engine.Engine, version version.Versi |
| 961 | 961 |
return nil |
| 962 | 962 |
} |
| 963 | 963 |
|
| 964 |
-func (s *Server) postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 964 |
+func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 965 | 965 |
if err := parseForm(r); err != nil {
|
| 966 | 966 |
return err |
| 967 | 967 |
} |
| ... | ... |
@@ -978,7 +968,7 @@ func (s *Server) postContainerRename(eng *engine.Engine, version version.Version |
| 978 | 978 |
return nil |
| 979 | 979 |
} |
| 980 | 980 |
|
| 981 |
-func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 981 |
+func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 982 | 982 |
if err := parseForm(r); err != nil {
|
| 983 | 983 |
return err |
| 984 | 984 |
} |
| ... | ... |
@@ -1006,7 +996,7 @@ func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w |
| 1006 | 1006 |
return nil |
| 1007 | 1007 |
} |
| 1008 | 1008 |
|
| 1009 |
-func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1009 |
+func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1010 | 1010 |
if err := parseForm(r); err != nil {
|
| 1011 | 1011 |
return err |
| 1012 | 1012 |
} |
| ... | ... |
@@ -1026,7 +1016,7 @@ func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w htt |
| 1026 | 1026 |
return writeJSON(w, http.StatusOK, list) |
| 1027 | 1027 |
} |
| 1028 | 1028 |
|
| 1029 |
-func (s *Server) postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1029 |
+func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1030 | 1030 |
if vars == nil {
|
| 1031 | 1031 |
return fmt.Errorf("Missing parameter")
|
| 1032 | 1032 |
} |
| ... | ... |
@@ -1062,7 +1052,7 @@ func (s *Server) postContainersStart(eng *engine.Engine, version version.Version |
| 1062 | 1062 |
return nil |
| 1063 | 1063 |
} |
| 1064 | 1064 |
|
| 1065 |
-func (s *Server) postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1065 |
+func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1066 | 1066 |
if err := parseForm(r); err != nil {
|
| 1067 | 1067 |
return err |
| 1068 | 1068 |
} |
| ... | ... |
@@ -1087,7 +1077,7 @@ func (s *Server) postContainersStop(eng *engine.Engine, version version.Version, |
| 1087 | 1087 |
return nil |
| 1088 | 1088 |
} |
| 1089 | 1089 |
|
| 1090 |
-func (s *Server) postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1090 |
+func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1091 | 1091 |
if vars == nil {
|
| 1092 | 1092 |
return fmt.Errorf("Missing parameter")
|
| 1093 | 1093 |
} |
| ... | ... |
@@ -1105,7 +1095,7 @@ func (s *Server) postContainersWait(eng *engine.Engine, version version.Version, |
| 1105 | 1105 |
}) |
| 1106 | 1106 |
} |
| 1107 | 1107 |
|
| 1108 |
-func (s *Server) postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1108 |
+func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1109 | 1109 |
if err := parseForm(r); err != nil {
|
| 1110 | 1110 |
return err |
| 1111 | 1111 |
} |
| ... | ... |
@@ -1130,7 +1120,7 @@ func (s *Server) postContainersResize(eng *engine.Engine, version version.Versio |
| 1130 | 1130 |
return cont.Resize(height, width) |
| 1131 | 1131 |
} |
| 1132 | 1132 |
|
| 1133 |
-func (s *Server) postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1133 |
+func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1134 | 1134 |
if err := parseForm(r); err != nil {
|
| 1135 | 1135 |
return err |
| 1136 | 1136 |
} |
| ... | ... |
@@ -1185,7 +1175,7 @@ func (s *Server) postContainersAttach(eng *engine.Engine, version version.Versio |
| 1185 | 1185 |
return nil |
| 1186 | 1186 |
} |
| 1187 | 1187 |
|
| 1188 |
-func (s *Server) wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1188 |
+func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1189 | 1189 |
if err := parseForm(r); err != nil {
|
| 1190 | 1190 |
return err |
| 1191 | 1191 |
} |
| ... | ... |
@@ -1211,7 +1201,7 @@ func (s *Server) wsContainersAttach(eng *engine.Engine, version version.Version, |
| 1211 | 1211 |
return nil |
| 1212 | 1212 |
} |
| 1213 | 1213 |
|
| 1214 |
-func (s *Server) getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1214 |
+func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1215 | 1215 |
if vars == nil {
|
| 1216 | 1216 |
return fmt.Errorf("Missing parameter")
|
| 1217 | 1217 |
} |
| ... | ... |
@@ -1232,7 +1222,7 @@ func (s *Server) getContainersByName(eng *engine.Engine, version version.Version |
| 1232 | 1232 |
return writeJSON(w, http.StatusOK, containerJSON) |
| 1233 | 1233 |
} |
| 1234 | 1234 |
|
| 1235 |
-func (s *Server) getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1235 |
+func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1236 | 1236 |
if vars == nil {
|
| 1237 | 1237 |
return fmt.Errorf("Missing parameter 'id'")
|
| 1238 | 1238 |
} |
| ... | ... |
@@ -1245,7 +1235,7 @@ func (s *Server) getExecByID(eng *engine.Engine, version version.Version, w http |
| 1245 | 1245 |
return writeJSON(w, http.StatusOK, eConfig) |
| 1246 | 1246 |
} |
| 1247 | 1247 |
|
| 1248 |
-func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1248 |
+func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1249 | 1249 |
if vars == nil {
|
| 1250 | 1250 |
return fmt.Errorf("Missing parameter")
|
| 1251 | 1251 |
} |
| ... | ... |
@@ -1268,7 +1258,7 @@ func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w |
| 1268 | 1268 |
return writeJSON(w, http.StatusOK, imageInspect) |
| 1269 | 1269 |
} |
| 1270 | 1270 |
|
| 1271 |
-func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1271 |
+func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1272 | 1272 |
if version.LessThan("1.3") {
|
| 1273 | 1273 |
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
| 1274 | 1274 |
} |
| ... | ... |
@@ -1363,7 +1353,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R |
| 1363 | 1363 |
return nil |
| 1364 | 1364 |
} |
| 1365 | 1365 |
|
| 1366 |
-func (s *Server) postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1366 |
+func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1367 | 1367 |
if vars == nil {
|
| 1368 | 1368 |
return fmt.Errorf("Missing parameter")
|
| 1369 | 1369 |
} |
| ... | ... |
@@ -1413,7 +1403,7 @@ func (s *Server) postContainersCopy(eng *engine.Engine, version version.Version, |
| 1413 | 1413 |
return nil |
| 1414 | 1414 |
} |
| 1415 | 1415 |
|
| 1416 |
-func (s *Server) postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1416 |
+func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1417 | 1417 |
if err := parseForm(r); err != nil {
|
| 1418 | 1418 |
return nil |
| 1419 | 1419 |
} |
| ... | ... |
@@ -1442,7 +1432,7 @@ func (s *Server) postContainerExecCreate(eng *engine.Engine, version version.Ver |
| 1442 | 1442 |
} |
| 1443 | 1443 |
|
| 1444 | 1444 |
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. |
| 1445 |
-func (s *Server) postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1445 |
+func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1446 | 1446 |
if err := parseForm(r); err != nil {
|
| 1447 | 1447 |
return nil |
| 1448 | 1448 |
} |
| ... | ... |
@@ -1495,7 +1485,7 @@ func (s *Server) postContainerExecStart(eng *engine.Engine, version version.Vers |
| 1495 | 1495 |
return nil |
| 1496 | 1496 |
} |
| 1497 | 1497 |
|
| 1498 |
-func (s *Server) postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1498 |
+func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1499 | 1499 |
if err := parseForm(r); err != nil {
|
| 1500 | 1500 |
return err |
| 1501 | 1501 |
} |
| ... | ... |
@@ -1515,7 +1505,7 @@ func (s *Server) postContainerExecResize(eng *engine.Engine, version version.Ver |
| 1515 | 1515 |
return s.daemon.ContainerExecResize(vars["name"], height, width) |
| 1516 | 1516 |
} |
| 1517 | 1517 |
|
| 1518 |
-func (s *Server) optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1518 |
+func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1519 | 1519 |
w.WriteHeader(http.StatusOK) |
| 1520 | 1520 |
return nil |
| 1521 | 1521 |
} |
| ... | ... |
@@ -1526,12 +1516,12 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string |
| 1526 | 1526 |
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
| 1527 | 1527 |
} |
| 1528 | 1528 |
|
| 1529 |
-func (s *Server) ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1529 |
+func (s *Server) ping(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 1530 | 1530 |
_, err := w.Write([]byte{'O', 'K'})
|
| 1531 | 1531 |
return err |
| 1532 | 1532 |
} |
| 1533 | 1533 |
|
| 1534 |
-func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc {
|
|
| 1534 |
+func makeHttpHandler(logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc {
|
|
| 1535 | 1535 |
return func(w http.ResponseWriter, r *http.Request) {
|
| 1536 | 1536 |
// log the request |
| 1537 | 1537 |
logrus.Debugf("Calling %s %s", localMethod, localRoute)
|
| ... | ... |
@@ -1559,7 +1549,7 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local |
| 1559 | 1559 |
return |
| 1560 | 1560 |
} |
| 1561 | 1561 |
|
| 1562 |
- if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
|
|
| 1562 |
+ if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil {
|
|
| 1563 | 1563 |
logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
|
| 1564 | 1564 |
httpError(w, err) |
| 1565 | 1565 |
} |
| ... | ... |
@@ -1567,7 +1557,7 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local |
| 1567 | 1567 |
} |
| 1568 | 1568 |
|
| 1569 | 1569 |
// we keep enableCors just for legacy usage, need to be removed in the future |
| 1570 |
-func createRouter(s *Server, eng *engine.Engine) *mux.Router {
|
|
| 1570 |
+func createRouter(s *Server) *mux.Router {
|
|
| 1571 | 1571 |
r := mux.NewRouter() |
| 1572 | 1572 |
if os.Getenv("DEBUG") != "" {
|
| 1573 | 1573 |
ProfilerSetup(r, "/debug/") |
| ... | ... |
@@ -1644,7 +1634,7 @@ func createRouter(s *Server, eng *engine.Engine) *mux.Router {
|
| 1644 | 1644 |
localMethod := method |
| 1645 | 1645 |
|
| 1646 | 1646 |
// build the handler function |
| 1647 |
- f := makeHttpHandler(eng, s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) |
|
| 1647 |
+ f := makeHttpHandler(s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) |
|
| 1648 | 1648 |
|
| 1649 | 1649 |
// add the new route |
| 1650 | 1650 |
if localRoute == "" {
|
| ... | ... |
@@ -1659,23 +1649,6 @@ func createRouter(s *Server, eng *engine.Engine) *mux.Router {
|
| 1659 | 1659 |
return r |
| 1660 | 1660 |
} |
| 1661 | 1661 |
|
| 1662 |
-// ServeRequest processes a single http request to the docker remote api. |
|
| 1663 |
-// FIXME: refactor this to be part of Server and not require re-creating a new |
|
| 1664 |
-// router each time. This requires first moving ListenAndServe into Server. |
|
| 1665 |
-func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) {
|
|
| 1666 |
- cfg := &ServerConfig{
|
|
| 1667 |
- EnableCors: true, |
|
| 1668 |
- Version: string(apiversion), |
|
| 1669 |
- } |
|
| 1670 |
- api := New(cfg, eng) |
|
| 1671 |
- daemon, _ := eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon)
|
|
| 1672 |
- api.AcceptConnections(daemon) |
|
| 1673 |
- router := createRouter(api, eng) |
|
| 1674 |
- // Insert APIVERSION into the request as a convenience |
|
| 1675 |
- req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
|
|
| 1676 |
- router.ServeHTTP(w, req) |
|
| 1677 |
-} |
|
| 1678 |
- |
|
| 1679 | 1662 |
func allocateDaemonPort(addr string) error {
|
| 1680 | 1663 |
host, port, err := net.SplitHostPort(addr) |
| 1681 | 1664 |
if err != nil {
|
| ... | ... |
@@ -26,7 +26,6 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/daemon/logger/syslog" |
| 27 | 27 |
"github.com/docker/docker/daemon/network" |
| 28 | 28 |
"github.com/docker/docker/daemon/networkdriver/bridge" |
| 29 |
- "github.com/docker/docker/engine" |
|
| 30 | 29 |
"github.com/docker/docker/image" |
| 31 | 30 |
"github.com/docker/docker/links" |
| 32 | 31 |
"github.com/docker/docker/nat" |
| ... | ... |
@@ -600,10 +599,7 @@ func (container *Container) AllocateNetwork() error {
|
| 600 | 600 |
return nil |
| 601 | 601 |
} |
| 602 | 602 |
|
| 603 |
- var ( |
|
| 604 |
- err error |
|
| 605 |
- eng = container.daemon.eng |
|
| 606 |
- ) |
|
| 603 |
+ var err error |
|
| 607 | 604 |
|
| 608 | 605 |
networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "") |
| 609 | 606 |
if err != nil {
|
| ... | ... |
@@ -650,7 +646,7 @@ func (container *Container) AllocateNetwork() error {
|
| 650 | 650 |
container.NetworkSettings.PortMapping = nil |
| 651 | 651 |
|
| 652 | 652 |
for port := range portSpecs {
|
| 653 |
- if err = container.allocatePort(eng, port, bindings); err != nil {
|
|
| 653 |
+ if err = container.allocatePort(port, bindings); err != nil {
|
|
| 654 | 654 |
bridge.Release(container.ID) |
| 655 | 655 |
return err |
| 656 | 656 |
} |
| ... | ... |
@@ -686,8 +682,6 @@ func (container *Container) RestoreNetwork() error {
|
| 686 | 686 |
return nil |
| 687 | 687 |
} |
| 688 | 688 |
|
| 689 |
- eng := container.daemon.eng |
|
| 690 |
- |
|
| 691 | 689 |
// Re-allocate the interface with the same IP and MAC address. |
| 692 | 690 |
if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil {
|
| 693 | 691 |
return err |
| ... | ... |
@@ -695,7 +689,7 @@ func (container *Container) RestoreNetwork() error {
|
| 695 | 695 |
|
| 696 | 696 |
// Re-allocate any previously allocated ports. |
| 697 | 697 |
for port := range container.NetworkSettings.Ports {
|
| 698 |
- if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil {
|
|
| 698 |
+ if err := container.allocatePort(port, container.NetworkSettings.Ports); err != nil {
|
|
| 699 | 699 |
return err |
| 700 | 700 |
} |
| 701 | 701 |
} |
| ... | ... |
@@ -1483,7 +1477,7 @@ func (container *Container) waitForStart() error {
|
| 1483 | 1483 |
return nil |
| 1484 | 1484 |
} |
| 1485 | 1485 |
|
| 1486 |
-func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error {
|
|
| 1486 |
+func (container *Container) allocatePort(port nat.Port, bindings nat.PortMap) error {
|
|
| 1487 | 1487 |
binding := bindings[port] |
| 1488 | 1488 |
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
| 1489 | 1489 |
binding = append(binding, nat.PortBinding{})
|
| ... | ... |
@@ -27,7 +27,6 @@ import ( |
| 27 | 27 |
_ "github.com/docker/docker/daemon/graphdriver/vfs" |
| 28 | 28 |
"github.com/docker/docker/daemon/network" |
| 29 | 29 |
"github.com/docker/docker/daemon/networkdriver/bridge" |
| 30 |
- "github.com/docker/docker/engine" |
|
| 31 | 30 |
"github.com/docker/docker/graph" |
| 32 | 31 |
"github.com/docker/docker/image" |
| 33 | 32 |
"github.com/docker/docker/pkg/archive" |
| ... | ... |
@@ -38,7 +37,6 @@ import ( |
| 38 | 38 |
"github.com/docker/docker/pkg/namesgenerator" |
| 39 | 39 |
"github.com/docker/docker/pkg/parsers" |
| 40 | 40 |
"github.com/docker/docker/pkg/parsers/kernel" |
| 41 |
- "github.com/docker/docker/pkg/pidfile" |
|
| 42 | 41 |
"github.com/docker/docker/pkg/resolvconf" |
| 43 | 42 |
"github.com/docker/docker/pkg/stringid" |
| 44 | 43 |
"github.com/docker/docker/pkg/sysinfo" |
| ... | ... |
@@ -103,7 +101,6 @@ type Daemon struct {
|
| 103 | 103 |
idIndex *truncindex.TruncIndex |
| 104 | 104 |
sysInfo *sysinfo.SysInfo |
| 105 | 105 |
volumes *volumes.Repository |
| 106 |
- eng *engine.Engine |
|
| 107 | 106 |
config *Config |
| 108 | 107 |
containerGraph *graphdb.Database |
| 109 | 108 |
driver graphdriver.Driver |
| ... | ... |
@@ -114,14 +111,6 @@ type Daemon struct {
|
| 114 | 114 |
EventsService *events.Events |
| 115 | 115 |
} |
| 116 | 116 |
|
| 117 |
-// Install installs daemon capabilities to eng. |
|
| 118 |
-func (daemon *Daemon) Install(eng *engine.Engine) error {
|
|
| 119 |
- // FIXME: this hack is necessary for legacy integration tests to access |
|
| 120 |
- // the daemon object. |
|
| 121 |
- eng.HackSetGlobalVar("httpapi.daemon", daemon)
|
|
| 122 |
- return nil |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 | 117 |
// Get looks for a container using the provided information, which could be |
| 126 | 118 |
// one of the following inputs from the caller: |
| 127 | 119 |
// - A full container ID, which will exact match a container in daemon's list |
| ... | ... |
@@ -741,16 +730,7 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig. |
| 741 | 741 |
return nil |
| 742 | 742 |
} |
| 743 | 743 |
|
| 744 |
-// FIXME: harmonize with NewGraph() |
|
| 745 |
-func NewDaemon(config *Config, eng *engine.Engine, registryService *registry.Service) (*Daemon, error) {
|
|
| 746 |
- daemon, err := NewDaemonFromDirectory(config, eng, registryService) |
|
| 747 |
- if err != nil {
|
|
| 748 |
- return nil, err |
|
| 749 |
- } |
|
| 750 |
- return daemon, nil |
|
| 751 |
-} |
|
| 752 |
- |
|
| 753 |
-func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService *registry.Service) (*Daemon, error) {
|
|
| 744 |
+func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) {
|
|
| 754 | 745 |
if config.Mtu == 0 {
|
| 755 | 746 |
config.Mtu = getDefaultNetworkMtu() |
| 756 | 747 |
} |
| ... | ... |
@@ -766,19 +746,6 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 766 | 766 |
} |
| 767 | 767 |
config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge |
| 768 | 768 |
|
| 769 |
- // Claim the pidfile first, to avoid any and all unexpected race conditions. |
|
| 770 |
- // Some of the init doesn't need a pidfile lock - but let's not try to be smart. |
|
| 771 |
- if config.Pidfile != "" {
|
|
| 772 |
- file, err := pidfile.New(config.Pidfile) |
|
| 773 |
- if err != nil {
|
|
| 774 |
- return nil, err |
|
| 775 |
- } |
|
| 776 |
- eng.OnShutdown(func() {
|
|
| 777 |
- // Always release the pidfile last, just in case |
|
| 778 |
- file.Remove() |
|
| 779 |
- }) |
|
| 780 |
- } |
|
| 781 |
- |
|
| 782 | 769 |
// Check that the system is supported and we have sufficient privileges |
| 783 | 770 |
if runtime.GOOS != "linux" {
|
| 784 | 771 |
return nil, fmt.Errorf("The Docker daemon is only supported on linux")
|
| ... | ... |
@@ -826,17 +793,22 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 826 | 826 |
return nil, fmt.Errorf("error initializing graphdriver: %v", err)
|
| 827 | 827 |
} |
| 828 | 828 |
logrus.Debugf("Using graph driver %s", driver)
|
| 829 |
- // register cleanup for graph driver |
|
| 830 |
- eng.OnShutdown(func() {
|
|
| 831 |
- if err := driver.Cleanup(); err != nil {
|
|
| 832 |
- logrus.Errorf("Error during graph storage driver.Cleanup(): %v", err)
|
|
| 829 |
+ |
|
| 830 |
+ d := &Daemon{}
|
|
| 831 |
+ d.driver = driver |
|
| 832 |
+ |
|
| 833 |
+ defer func() {
|
|
| 834 |
+ if err != nil {
|
|
| 835 |
+ if err := d.Shutdown(); err != nil {
|
|
| 836 |
+ logrus.Error(err) |
|
| 837 |
+ } |
|
| 833 | 838 |
} |
| 834 |
- }) |
|
| 839 |
+ }() |
|
| 835 | 840 |
|
| 836 | 841 |
if config.EnableSelinuxSupport {
|
| 837 | 842 |
if selinuxEnabled() {
|
| 838 | 843 |
// As Docker on btrfs and SELinux are incompatible at present, error on both being enabled |
| 839 |
- if driver.String() == "btrfs" {
|
|
| 844 |
+ if d.driver.String() == "btrfs" {
|
|
| 840 | 845 |
return nil, fmt.Errorf("SELinux is not supported with the BTRFS graph driver")
|
| 841 | 846 |
} |
| 842 | 847 |
logrus.Debug("SELinux enabled successfully")
|
| ... | ... |
@@ -854,12 +826,12 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 854 | 854 |
} |
| 855 | 855 |
|
| 856 | 856 |
// Migrate the container if it is aufs and aufs is enabled |
| 857 |
- if err = migrateIfAufs(driver, config.Root); err != nil {
|
|
| 857 |
+ if err := migrateIfAufs(d.driver, config.Root); err != nil {
|
|
| 858 | 858 |
return nil, err |
| 859 | 859 |
} |
| 860 | 860 |
|
| 861 | 861 |
logrus.Debug("Creating images graph")
|
| 862 |
- g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver) |
|
| 862 |
+ g, err := graph.NewGraph(path.Join(config.Root, "graph"), d.driver) |
|
| 863 | 863 |
if err != nil {
|
| 864 | 864 |
return nil, err |
| 865 | 865 |
} |
| ... | ... |
@@ -897,7 +869,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 897 | 897 |
Events: eventsService, |
| 898 | 898 |
Trust: trustService, |
| 899 | 899 |
} |
| 900 |
- repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), tagCfg) |
|
| 900 |
+ repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+d.driver.String()), tagCfg) |
|
| 901 | 901 |
if err != nil {
|
| 902 | 902 |
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
| 903 | 903 |
} |
| ... | ... |
@@ -913,12 +885,8 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 913 | 913 |
if err != nil {
|
| 914 | 914 |
return nil, err |
| 915 | 915 |
} |
| 916 |
- // register graph close on shutdown |
|
| 917 |
- eng.OnShutdown(func() {
|
|
| 918 |
- if err := graph.Close(); err != nil {
|
|
| 919 |
- logrus.Errorf("Error during container graph.Close(): %v", err)
|
|
| 920 |
- } |
|
| 921 |
- }) |
|
| 916 |
+ |
|
| 917 |
+ d.containerGraph = graph |
|
| 922 | 918 |
|
| 923 | 919 |
localCopy := path.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION))
|
| 924 | 920 |
sysInitPath := utils.DockerInitPath(localCopy) |
| ... | ... |
@@ -947,66 +915,67 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService |
| 947 | 947 |
return nil, err |
| 948 | 948 |
} |
| 949 | 949 |
|
| 950 |
- daemon := &Daemon{
|
|
| 951 |
- ID: trustKey.PublicKey().KeyID(), |
|
| 952 |
- repository: daemonRepo, |
|
| 953 |
- containers: &contStore{s: make(map[string]*Container)},
|
|
| 954 |
- execCommands: newExecStore(), |
|
| 955 |
- graph: g, |
|
| 956 |
- repositories: repositories, |
|
| 957 |
- idIndex: truncindex.NewTruncIndex([]string{}),
|
|
| 958 |
- sysInfo: sysInfo, |
|
| 959 |
- volumes: volumes, |
|
| 960 |
- config: config, |
|
| 961 |
- containerGraph: graph, |
|
| 962 |
- driver: driver, |
|
| 963 |
- sysInitPath: sysInitPath, |
|
| 964 |
- execDriver: ed, |
|
| 965 |
- eng: eng, |
|
| 966 |
- statsCollector: newStatsCollector(1 * time.Second), |
|
| 967 |
- defaultLogConfig: config.LogConfig, |
|
| 968 |
- RegistryService: registryService, |
|
| 969 |
- EventsService: eventsService, |
|
| 970 |
- } |
|
| 971 |
- |
|
| 972 |
- eng.OnShutdown(func() {
|
|
| 973 |
- if err := daemon.shutdown(); err != nil {
|
|
| 974 |
- logrus.Errorf("Error during daemon.shutdown(): %v", err)
|
|
| 975 |
- } |
|
| 976 |
- }) |
|
| 977 |
- |
|
| 978 |
- if err := daemon.restore(); err != nil {
|
|
| 950 |
+ d.ID = trustKey.PublicKey().KeyID() |
|
| 951 |
+ d.repository = daemonRepo |
|
| 952 |
+ d.containers = &contStore{s: make(map[string]*Container)}
|
|
| 953 |
+ d.execCommands = newExecStore() |
|
| 954 |
+ d.graph = g |
|
| 955 |
+ d.repositories = repositories |
|
| 956 |
+ d.idIndex = truncindex.NewTruncIndex([]string{})
|
|
| 957 |
+ d.sysInfo = sysInfo |
|
| 958 |
+ d.volumes = volumes |
|
| 959 |
+ d.config = config |
|
| 960 |
+ d.sysInitPath = sysInitPath |
|
| 961 |
+ d.execDriver = ed |
|
| 962 |
+ d.statsCollector = newStatsCollector(1 * time.Second) |
|
| 963 |
+ d.defaultLogConfig = config.LogConfig |
|
| 964 |
+ d.RegistryService = registryService |
|
| 965 |
+ d.EventsService = eventsService |
|
| 966 |
+ |
|
| 967 |
+ if err := d.restore(); err != nil {
|
|
| 979 | 968 |
return nil, err |
| 980 | 969 |
} |
| 981 | 970 |
|
| 982 | 971 |
// set up filesystem watch on resolv.conf for network changes |
| 983 |
- if err := daemon.setupResolvconfWatcher(); err != nil {
|
|
| 972 |
+ if err := d.setupResolvconfWatcher(); err != nil {
|
|
| 984 | 973 |
return nil, err |
| 985 | 974 |
} |
| 986 | 975 |
|
| 987 |
- return daemon, nil |
|
| 976 |
+ return d, nil |
|
| 988 | 977 |
} |
| 989 | 978 |
|
| 990 |
-func (daemon *Daemon) shutdown() error {
|
|
| 991 |
- group := sync.WaitGroup{}
|
|
| 992 |
- logrus.Debug("starting clean shutdown of all containers...")
|
|
| 993 |
- for _, container := range daemon.List() {
|
|
| 994 |
- c := container |
|
| 995 |
- if c.IsRunning() {
|
|
| 996 |
- logrus.Debugf("stopping %s", c.ID)
|
|
| 997 |
- group.Add(1) |
|
| 998 |
- |
|
| 999 |
- go func() {
|
|
| 1000 |
- defer group.Done() |
|
| 1001 |
- if err := c.KillSig(15); err != nil {
|
|
| 1002 |
- logrus.Debugf("kill 15 error for %s - %s", c.ID, err)
|
|
| 1003 |
- } |
|
| 1004 |
- c.WaitStop(-1 * time.Second) |
|
| 1005 |
- logrus.Debugf("container stopped %s", c.ID)
|
|
| 1006 |
- }() |
|
| 979 |
+func (daemon *Daemon) Shutdown() error {
|
|
| 980 |
+ if daemon.containerGraph != nil {
|
|
| 981 |
+ if err := daemon.containerGraph.Close(); err != nil {
|
|
| 982 |
+ logrus.Errorf("Error during container graph.Close(): %v", err)
|
|
| 1007 | 983 |
} |
| 1008 | 984 |
} |
| 1009 |
- group.Wait() |
|
| 985 |
+ if daemon.driver != nil {
|
|
| 986 |
+ if err := daemon.driver.Cleanup(); err != nil {
|
|
| 987 |
+ logrus.Errorf("Error during graph storage driver.Cleanup(): %v", err)
|
|
| 988 |
+ } |
|
| 989 |
+ } |
|
| 990 |
+ if daemon.containers != nil {
|
|
| 991 |
+ group := sync.WaitGroup{}
|
|
| 992 |
+ logrus.Debug("starting clean shutdown of all containers...")
|
|
| 993 |
+ for _, container := range daemon.List() {
|
|
| 994 |
+ c := container |
|
| 995 |
+ if c.IsRunning() {
|
|
| 996 |
+ logrus.Debugf("stopping %s", c.ID)
|
|
| 997 |
+ group.Add(1) |
|
| 998 |
+ |
|
| 999 |
+ go func() {
|
|
| 1000 |
+ defer group.Done() |
|
| 1001 |
+ if err := c.KillSig(15); err != nil {
|
|
| 1002 |
+ logrus.Debugf("kill 15 error for %s - %s", c.ID, err)
|
|
| 1003 |
+ } |
|
| 1004 |
+ c.WaitStop(-1 * time.Second) |
|
| 1005 |
+ logrus.Debugf("container stopped %s", c.ID)
|
|
| 1006 |
+ }() |
|
| 1007 |
+ } |
|
| 1008 |
+ } |
|
| 1009 |
+ group.Wait() |
|
| 1010 |
+ } |
|
| 1010 | 1011 |
|
| 1011 | 1012 |
return nil |
| 1012 | 1013 |
} |
| ... | ... |
@@ -1087,26 +1056,6 @@ func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface |
| 1087 | 1087 |
return nil |
| 1088 | 1088 |
} |
| 1089 | 1089 |
|
| 1090 |
-// Nuke kills all containers then removes all content |
|
| 1091 |
-// from the content root, including images, volumes and |
|
| 1092 |
-// container filesystems. |
|
| 1093 |
-// Again: this will remove your entire docker daemon! |
|
| 1094 |
-// FIXME: this is deprecated, and only used in legacy |
|
| 1095 |
-// tests. Please remove. |
|
| 1096 |
-func (daemon *Daemon) Nuke() error {
|
|
| 1097 |
- var wg sync.WaitGroup |
|
| 1098 |
- for _, container := range daemon.List() {
|
|
| 1099 |
- wg.Add(1) |
|
| 1100 |
- go func(c *Container) {
|
|
| 1101 |
- c.Kill() |
|
| 1102 |
- wg.Done() |
|
| 1103 |
- }(container) |
|
| 1104 |
- } |
|
| 1105 |
- wg.Wait() |
|
| 1106 |
- |
|
| 1107 |
- return os.RemoveAll(daemon.config.Root) |
|
| 1108 |
-} |
|
| 1109 |
- |
|
| 1110 | 1090 |
// FIXME: this is a convenience function for integration tests |
| 1111 | 1091 |
// which need direct access to daemon.graph. |
| 1112 | 1092 |
// Once the tests switch to using engine and jobs, this method |
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
"io" |
| 8 | 8 |
"os" |
| 9 | 9 |
"path/filepath" |
| 10 |
+ "time" |
|
| 10 | 11 |
|
| 11 | 12 |
"github.com/Sirupsen/logrus" |
| 12 | 13 |
apiserver "github.com/docker/docker/api/server" |
| ... | ... |
@@ -14,9 +15,9 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/daemon" |
| 15 | 15 |
_ "github.com/docker/docker/daemon/execdriver/lxc" |
| 16 | 16 |
_ "github.com/docker/docker/daemon/execdriver/native" |
| 17 |
- "github.com/docker/docker/engine" |
|
| 18 | 17 |
"github.com/docker/docker/pkg/homedir" |
| 19 | 18 |
flag "github.com/docker/docker/pkg/mflag" |
| 19 |
+ "github.com/docker/docker/pkg/pidfile" |
|
| 20 | 20 |
"github.com/docker/docker/pkg/signal" |
| 21 | 21 |
"github.com/docker/docker/pkg/system" |
| 22 | 22 |
"github.com/docker/docker/pkg/timeutils" |
| ... | ... |
@@ -83,14 +84,45 @@ func mainDaemon() {
|
| 83 | 83 |
|
| 84 | 84 |
logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
|
| 85 | 85 |
|
| 86 |
- eng := engine.New() |
|
| 87 |
- signal.Trap(eng.Shutdown) |
|
| 86 |
+ var pfile *pidfile.PidFile |
|
| 87 |
+ if daemonCfg.Pidfile != "" {
|
|
| 88 |
+ pf, err := pidfile.New(daemonCfg.Pidfile) |
|
| 89 |
+ if err != nil {
|
|
| 90 |
+ logrus.Fatalf("Error starting daemon: %v", err)
|
|
| 91 |
+ } |
|
| 92 |
+ pfile = pf |
|
| 93 |
+ defer func() {
|
|
| 94 |
+ if err := pfile.Remove(); err != nil {
|
|
| 95 |
+ logrus.Error(err) |
|
| 96 |
+ } |
|
| 97 |
+ }() |
|
| 98 |
+ } |
|
| 88 | 99 |
|
| 89 | 100 |
if err := migrateKey(); err != nil {
|
| 90 | 101 |
logrus.Fatal(err) |
| 91 | 102 |
} |
| 92 | 103 |
daemonCfg.TrustKeyPath = *flTrustKey |
| 93 | 104 |
|
| 105 |
+ registryService := registry.NewService(registryCfg) |
|
| 106 |
+ d, err := daemon.NewDaemon(daemonCfg, registryService) |
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ if pfile != nil {
|
|
| 109 |
+ if err := pfile.Remove(); err != nil {
|
|
| 110 |
+ logrus.Error(err) |
|
| 111 |
+ } |
|
| 112 |
+ } |
|
| 113 |
+ logrus.Fatalf("Error starting daemon: %v", err)
|
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ logrus.Info("Daemon has completed initialization")
|
|
| 117 |
+ |
|
| 118 |
+ logrus.WithFields(logrus.Fields{
|
|
| 119 |
+ "version": dockerversion.VERSION, |
|
| 120 |
+ "commit": dockerversion.GITCOMMIT, |
|
| 121 |
+ "execdriver": d.ExecutionDriver().Name(), |
|
| 122 |
+ "graphdriver": d.GraphDriver().String(), |
|
| 123 |
+ }).Info("Docker daemon")
|
|
| 124 |
+ |
|
| 94 | 125 |
serverConfig := &apiserver.ServerConfig{
|
| 95 | 126 |
Logging: true, |
| 96 | 127 |
EnableCors: daemonCfg.EnableCors, |
| ... | ... |
@@ -104,7 +136,7 @@ func mainDaemon() {
|
| 104 | 104 |
TlsKey: *flKey, |
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 |
- api := apiserver.New(serverConfig, eng) |
|
| 107 |
+ api := apiserver.New(serverConfig) |
|
| 108 | 108 |
|
| 109 | 109 |
// The serve API routine never exits unless an error occurs |
| 110 | 110 |
// We need to start it as a goroutine and wait on it so |
| ... | ... |
@@ -119,40 +151,52 @@ func mainDaemon() {
|
| 119 | 119 |
serveAPIWait <- nil |
| 120 | 120 |
}() |
| 121 | 121 |
|
| 122 |
- registryService := registry.NewService(registryCfg) |
|
| 123 |
- d, err := daemon.NewDaemon(daemonCfg, eng, registryService) |
|
| 124 |
- if err != nil {
|
|
| 125 |
- eng.Shutdown() |
|
| 126 |
- logrus.Fatalf("Error starting daemon: %v", err)
|
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- if err := d.Install(eng); err != nil {
|
|
| 130 |
- eng.Shutdown() |
|
| 131 |
- logrus.Fatalf("Error starting daemon: %v", err)
|
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- logrus.Info("Daemon has completed initialization")
|
|
| 135 |
- |
|
| 136 |
- logrus.WithFields(logrus.Fields{
|
|
| 137 |
- "version": dockerversion.VERSION, |
|
| 138 |
- "commit": dockerversion.GITCOMMIT, |
|
| 139 |
- "execdriver": d.ExecutionDriver().Name(), |
|
| 140 |
- "graphdriver": d.GraphDriver().String(), |
|
| 141 |
- }).Info("Docker daemon")
|
|
| 122 |
+ signal.Trap(func() {
|
|
| 123 |
+ api.Close() |
|
| 124 |
+ <-serveAPIWait |
|
| 125 |
+ shutdownDaemon(d, 15) |
|
| 126 |
+ if pfile != nil {
|
|
| 127 |
+ if err := pfile.Remove(); err != nil {
|
|
| 128 |
+ logrus.Error(err) |
|
| 129 |
+ } |
|
| 130 |
+ } |
|
| 131 |
+ }) |
|
| 142 | 132 |
|
| 143 | 133 |
// after the daemon is done setting up we can tell the api to start |
| 144 | 134 |
// accepting connections with specified daemon |
| 145 | 135 |
api.AcceptConnections(d) |
| 146 | 136 |
|
| 147 | 137 |
// Daemon is fully initialized and handling API traffic |
| 148 |
- // Wait for serve API job to complete |
|
| 138 |
+ // Wait for serve API to complete |
|
| 149 | 139 |
errAPI := <-serveAPIWait |
| 150 |
- eng.Shutdown() |
|
| 140 |
+ shutdownDaemon(d, 15) |
|
| 151 | 141 |
if errAPI != nil {
|
| 142 |
+ if pfile != nil {
|
|
| 143 |
+ if err := pfile.Remove(); err != nil {
|
|
| 144 |
+ logrus.Error(err) |
|
| 145 |
+ } |
|
| 146 |
+ } |
|
| 152 | 147 |
logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
|
| 153 | 148 |
} |
| 154 | 149 |
} |
| 155 | 150 |
|
| 151 |
+// shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case |
|
| 152 |
+// d.Shutdown() is waiting too long to kill container or worst it's |
|
| 153 |
+// blocked there |
|
| 154 |
+func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
|
|
| 155 |
+ ch := make(chan struct{})
|
|
| 156 |
+ go func() {
|
|
| 157 |
+ d.Shutdown() |
|
| 158 |
+ close(ch) |
|
| 159 |
+ }() |
|
| 160 |
+ select {
|
|
| 161 |
+ case <-ch: |
|
| 162 |
+ logrus.Debug("Clean shutdown succeded")
|
|
| 163 |
+ case <-time.After(timeout * time.Second): |
|
| 164 |
+ logrus.Error("Force shutdown daemon")
|
|
| 165 |
+ } |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 156 | 168 |
// currentUserIsOwner checks whether the current user is the owner of the given |
| 157 | 169 |
// file. |
| 158 | 170 |
func currentUserIsOwner(f string) bool {
|
| 159 | 171 |
deleted file mode 100644 |
| ... | ... |
@@ -1,255 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "os" |
|
| 8 |
- "sort" |
|
| 9 |
- "strings" |
|
| 10 |
- "sync" |
|
| 11 |
- "time" |
|
| 12 |
- |
|
| 13 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 14 |
- "github.com/docker/docker/pkg/stringid" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// Installer is a standard interface for objects which can "install" themselves |
|
| 18 |
-// on an engine by registering handlers. |
|
| 19 |
-// This can be used as an entrypoint for external plugins etc. |
|
| 20 |
-type Installer interface {
|
|
| 21 |
- Install(*Engine) error |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-type Handler func(*Job) error |
|
| 25 |
- |
|
| 26 |
-var globalHandlers map[string]Handler |
|
| 27 |
- |
|
| 28 |
-func init() {
|
|
| 29 |
- globalHandlers = make(map[string]Handler) |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func Register(name string, handler Handler) error {
|
|
| 33 |
- _, exists := globalHandlers[name] |
|
| 34 |
- if exists {
|
|
| 35 |
- return fmt.Errorf("Can't overwrite global handler for command %s", name)
|
|
| 36 |
- } |
|
| 37 |
- globalHandlers[name] = handler |
|
| 38 |
- return nil |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-func unregister(name string) {
|
|
| 42 |
- delete(globalHandlers, name) |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// The Engine is the core of Docker. |
|
| 46 |
-// It acts as a store for *containers*, and allows manipulation of these |
|
| 47 |
-// containers by executing *jobs*. |
|
| 48 |
-type Engine struct {
|
|
| 49 |
- handlers map[string]Handler |
|
| 50 |
- catchall Handler |
|
| 51 |
- hack Hack // data for temporary hackery (see hack.go) |
|
| 52 |
- id string |
|
| 53 |
- Stdout io.Writer |
|
| 54 |
- Stderr io.Writer |
|
| 55 |
- Stdin io.Reader |
|
| 56 |
- Logging bool |
|
| 57 |
- tasks sync.WaitGroup |
|
| 58 |
- l sync.RWMutex // lock for shutdown |
|
| 59 |
- shutdownWait sync.WaitGroup |
|
| 60 |
- shutdown bool |
|
| 61 |
- onShutdown []func() // shutdown handlers |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func (eng *Engine) Register(name string, handler Handler) error {
|
|
| 65 |
- _, exists := eng.handlers[name] |
|
| 66 |
- if exists {
|
|
| 67 |
- return fmt.Errorf("Can't overwrite handler for command %s", name)
|
|
| 68 |
- } |
|
| 69 |
- eng.handlers[name] = handler |
|
| 70 |
- return nil |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-func (eng *Engine) RegisterCatchall(catchall Handler) {
|
|
| 74 |
- eng.catchall = catchall |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-// New initializes a new engine. |
|
| 78 |
-func New() *Engine {
|
|
| 79 |
- eng := &Engine{
|
|
| 80 |
- handlers: make(map[string]Handler), |
|
| 81 |
- id: stringid.GenerateRandomID(), |
|
| 82 |
- Stdout: os.Stdout, |
|
| 83 |
- Stderr: os.Stderr, |
|
| 84 |
- Stdin: os.Stdin, |
|
| 85 |
- Logging: true, |
|
| 86 |
- } |
|
| 87 |
- eng.Register("commands", func(job *Job) error {
|
|
| 88 |
- for _, name := range eng.commands() {
|
|
| 89 |
- job.Printf("%s\n", name)
|
|
| 90 |
- } |
|
| 91 |
- return nil |
|
| 92 |
- }) |
|
| 93 |
- // Copy existing global handlers |
|
| 94 |
- for k, v := range globalHandlers {
|
|
| 95 |
- eng.handlers[k] = v |
|
| 96 |
- } |
|
| 97 |
- return eng |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func (eng *Engine) String() string {
|
|
| 101 |
- return fmt.Sprintf("%s", eng.id[:8])
|
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-// Commands returns a list of all currently registered commands, |
|
| 105 |
-// sorted alphabetically. |
|
| 106 |
-func (eng *Engine) commands() []string {
|
|
| 107 |
- names := make([]string, 0, len(eng.handlers)) |
|
| 108 |
- for name := range eng.handlers {
|
|
| 109 |
- names = append(names, name) |
|
| 110 |
- } |
|
| 111 |
- sort.Strings(names) |
|
| 112 |
- return names |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-// Job creates a new job which can later be executed. |
|
| 116 |
-// This function mimics `Command` from the standard os/exec package. |
|
| 117 |
-func (eng *Engine) Job(name string, args ...string) *Job {
|
|
| 118 |
- job := &Job{
|
|
| 119 |
- Eng: eng, |
|
| 120 |
- Name: name, |
|
| 121 |
- Args: args, |
|
| 122 |
- Stdin: NewInput(), |
|
| 123 |
- Stdout: NewOutput(), |
|
| 124 |
- Stderr: NewOutput(), |
|
| 125 |
- env: &Env{},
|
|
| 126 |
- closeIO: true, |
|
| 127 |
- |
|
| 128 |
- cancelled: make(chan struct{}),
|
|
| 129 |
- } |
|
| 130 |
- if eng.Logging {
|
|
| 131 |
- job.Stderr.Add(ioutils.NopWriteCloser(eng.Stderr)) |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- // Catchall is shadowed by specific Register. |
|
| 135 |
- if handler, exists := eng.handlers[name]; exists {
|
|
| 136 |
- job.handler = handler |
|
| 137 |
- } else if eng.catchall != nil && name != "" {
|
|
| 138 |
- // empty job names are illegal, catchall or not. |
|
| 139 |
- job.handler = eng.catchall |
|
| 140 |
- } |
|
| 141 |
- return job |
|
| 142 |
-} |
|
| 143 |
- |
|
| 144 |
-// OnShutdown registers a new callback to be called by Shutdown. |
|
| 145 |
-// This is typically used by services to perform cleanup. |
|
| 146 |
-func (eng *Engine) OnShutdown(h func()) {
|
|
| 147 |
- eng.l.Lock() |
|
| 148 |
- eng.onShutdown = append(eng.onShutdown, h) |
|
| 149 |
- eng.shutdownWait.Add(1) |
|
| 150 |
- eng.l.Unlock() |
|
| 151 |
-} |
|
| 152 |
- |
|
| 153 |
-// Shutdown permanently shuts down eng as follows: |
|
| 154 |
-// - It refuses all new jobs, permanently. |
|
| 155 |
-// - It waits for all active jobs to complete (with no timeout) |
|
| 156 |
-// - It calls all shutdown handlers concurrently (if any) |
|
| 157 |
-// - It returns when all handlers complete, or after 15 seconds, |
|
| 158 |
-// whichever happens first. |
|
| 159 |
-func (eng *Engine) Shutdown() {
|
|
| 160 |
- eng.l.Lock() |
|
| 161 |
- if eng.shutdown {
|
|
| 162 |
- eng.l.Unlock() |
|
| 163 |
- eng.shutdownWait.Wait() |
|
| 164 |
- return |
|
| 165 |
- } |
|
| 166 |
- eng.shutdown = true |
|
| 167 |
- eng.l.Unlock() |
|
| 168 |
- // We don't need to protect the rest with a lock, to allow |
|
| 169 |
- // for other calls to immediately fail with "shutdown" instead |
|
| 170 |
- // of hanging for 15 seconds. |
|
| 171 |
- // This requires all concurrent calls to check for shutdown, otherwise |
|
| 172 |
- // it might cause a race. |
|
| 173 |
- |
|
| 174 |
- // Wait for all jobs to complete. |
|
| 175 |
- // Timeout after 5 seconds. |
|
| 176 |
- tasksDone := make(chan struct{})
|
|
| 177 |
- go func() {
|
|
| 178 |
- eng.tasks.Wait() |
|
| 179 |
- close(tasksDone) |
|
| 180 |
- }() |
|
| 181 |
- select {
|
|
| 182 |
- case <-time.After(time.Second * 5): |
|
| 183 |
- case <-tasksDone: |
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- // Call shutdown handlers, if any. |
|
| 187 |
- // Timeout after 10 seconds. |
|
| 188 |
- for _, h := range eng.onShutdown {
|
|
| 189 |
- go func(h func()) {
|
|
| 190 |
- h() |
|
| 191 |
- eng.shutdownWait.Done() |
|
| 192 |
- }(h) |
|
| 193 |
- } |
|
| 194 |
- done := make(chan struct{})
|
|
| 195 |
- go func() {
|
|
| 196 |
- eng.shutdownWait.Wait() |
|
| 197 |
- close(done) |
|
| 198 |
- }() |
|
| 199 |
- select {
|
|
| 200 |
- case <-time.After(time.Second * 10): |
|
| 201 |
- case <-done: |
|
| 202 |
- } |
|
| 203 |
- return |
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-// IsShutdown returns true if the engine is in the process |
|
| 207 |
-// of shutting down, or already shut down. |
|
| 208 |
-// Otherwise it returns false. |
|
| 209 |
-func (eng *Engine) IsShutdown() bool {
|
|
| 210 |
- eng.l.RLock() |
|
| 211 |
- defer eng.l.RUnlock() |
|
| 212 |
- return eng.shutdown |
|
| 213 |
-} |
|
| 214 |
- |
|
| 215 |
-// ParseJob creates a new job from a text description using a shell-like syntax. |
|
| 216 |
-// |
|
| 217 |
-// The following syntax is used to parse `input`: |
|
| 218 |
-// |
|
| 219 |
-// * Words are separated using standard whitespaces as separators. |
|
| 220 |
-// * Quotes and backslashes are not interpreted. |
|
| 221 |
-// * Words of the form 'KEY=[VALUE]' are added to the job environment. |
|
| 222 |
-// * All other words are added to the job arguments. |
|
| 223 |
-// |
|
| 224 |
-// For example: |
|
| 225 |
-// |
|
| 226 |
-// job, _ := eng.ParseJob("VERBOSE=1 echo hello TEST=true world")
|
|
| 227 |
-// |
|
| 228 |
-// The resulting job will have: |
|
| 229 |
-// job.Args={"echo", "hello", "world"}
|
|
| 230 |
-// job.Env={"VERBOSE":"1", "TEST":"true"}
|
|
| 231 |
-// |
|
| 232 |
-func (eng *Engine) ParseJob(input string) (*Job, error) {
|
|
| 233 |
- // FIXME: use a full-featured command parser |
|
| 234 |
- scanner := bufio.NewScanner(strings.NewReader(input)) |
|
| 235 |
- scanner.Split(bufio.ScanWords) |
|
| 236 |
- var ( |
|
| 237 |
- cmd []string |
|
| 238 |
- env Env |
|
| 239 |
- ) |
|
| 240 |
- for scanner.Scan() {
|
|
| 241 |
- word := scanner.Text() |
|
| 242 |
- kv := strings.SplitN(word, "=", 2) |
|
| 243 |
- if len(kv) == 2 {
|
|
| 244 |
- env.Set(kv[0], kv[1]) |
|
| 245 |
- } else {
|
|
| 246 |
- cmd = append(cmd, word) |
|
| 247 |
- } |
|
| 248 |
- } |
|
| 249 |
- if len(cmd) == 0 {
|
|
| 250 |
- return nil, fmt.Errorf("empty command: '%s'", input)
|
|
| 251 |
- } |
|
| 252 |
- job := eng.Job(cmd[0], cmd[1:]...) |
|
| 253 |
- job.Env().Init(&env) |
|
| 254 |
- return job, nil |
|
| 255 |
-} |
| 256 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,236 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "strings" |
|
| 6 |
- "testing" |
|
| 7 |
- |
|
| 8 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-func TestRegister(t *testing.T) {
|
|
| 12 |
- if err := Register("dummy1", nil); err != nil {
|
|
| 13 |
- t.Fatal(err) |
|
| 14 |
- } |
|
| 15 |
- |
|
| 16 |
- if err := Register("dummy1", nil); err == nil {
|
|
| 17 |
- t.Fatalf("Expecting error, got none")
|
|
| 18 |
- } |
|
| 19 |
- // Register is global so let's cleanup to avoid conflicts |
|
| 20 |
- defer unregister("dummy1")
|
|
| 21 |
- |
|
| 22 |
- eng := New() |
|
| 23 |
- |
|
| 24 |
- //Should fail because global handlers are copied |
|
| 25 |
- //at the engine creation |
|
| 26 |
- if err := eng.Register("dummy1", nil); err == nil {
|
|
| 27 |
- t.Fatalf("Expecting error, got none")
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- if err := eng.Register("dummy2", nil); err != nil {
|
|
| 31 |
- t.Fatal(err) |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- if err := eng.Register("dummy2", nil); err == nil {
|
|
| 35 |
- t.Fatalf("Expecting error, got none")
|
|
| 36 |
- } |
|
| 37 |
- defer unregister("dummy2")
|
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func TestJob(t *testing.T) {
|
|
| 41 |
- eng := New() |
|
| 42 |
- job1 := eng.Job("dummy1", "--level=awesome")
|
|
| 43 |
- |
|
| 44 |
- if job1.handler != nil {
|
|
| 45 |
- t.Fatalf("job1.handler should be empty")
|
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- h := func(j *Job) error {
|
|
| 49 |
- j.Printf("%s\n", j.Name)
|
|
| 50 |
- return nil |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- eng.Register("dummy2", h)
|
|
| 54 |
- defer unregister("dummy2")
|
|
| 55 |
- job2 := eng.Job("dummy2", "--level=awesome")
|
|
| 56 |
- |
|
| 57 |
- if job2.handler == nil {
|
|
| 58 |
- t.Fatalf("job2.handler shouldn't be nil")
|
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- if job2.handler(job2) != nil {
|
|
| 62 |
- t.Fatalf("handler dummy2 was not found in job2")
|
|
| 63 |
- } |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func TestEngineShutdown(t *testing.T) {
|
|
| 67 |
- eng := New() |
|
| 68 |
- if eng.IsShutdown() {
|
|
| 69 |
- t.Fatalf("Engine should not show as shutdown")
|
|
| 70 |
- } |
|
| 71 |
- eng.Shutdown() |
|
| 72 |
- if !eng.IsShutdown() {
|
|
| 73 |
- t.Fatalf("Engine should show as shutdown")
|
|
| 74 |
- } |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func TestEngineCommands(t *testing.T) {
|
|
| 78 |
- eng := New() |
|
| 79 |
- handler := func(job *Job) error { return nil }
|
|
| 80 |
- eng.Register("foo", handler)
|
|
| 81 |
- eng.Register("bar", handler)
|
|
| 82 |
- eng.Register("echo", handler)
|
|
| 83 |
- eng.Register("die", handler)
|
|
| 84 |
- var output bytes.Buffer |
|
| 85 |
- commands := eng.Job("commands")
|
|
| 86 |
- commands.Stdout.Add(&output) |
|
| 87 |
- commands.Run() |
|
| 88 |
- expected := "bar\ncommands\ndie\necho\nfoo\n" |
|
| 89 |
- if result := output.String(); result != expected {
|
|
| 90 |
- t.Fatalf("Unexpected output:\nExpected = %v\nResult = %v\n", expected, result)
|
|
| 91 |
- } |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func TestEngineString(t *testing.T) {
|
|
| 95 |
- eng1 := New() |
|
| 96 |
- eng2 := New() |
|
| 97 |
- s1 := eng1.String() |
|
| 98 |
- s2 := eng2.String() |
|
| 99 |
- if eng1 == eng2 {
|
|
| 100 |
- t.Fatalf("Different engines should have different names (%v == %v)", s1, s2)
|
|
| 101 |
- } |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func TestParseJob(t *testing.T) {
|
|
| 105 |
- eng := New() |
|
| 106 |
- // Verify that the resulting job calls to the right place |
|
| 107 |
- var called bool |
|
| 108 |
- eng.Register("echo", func(job *Job) error {
|
|
| 109 |
- called = true |
|
| 110 |
- return nil |
|
| 111 |
- }) |
|
| 112 |
- input := "echo DEBUG=1 hello world VERBOSITY=42" |
|
| 113 |
- job, err := eng.ParseJob(input) |
|
| 114 |
- if err != nil {
|
|
| 115 |
- t.Fatal(err) |
|
| 116 |
- } |
|
| 117 |
- if job.Name != "echo" {
|
|
| 118 |
- t.Fatalf("Invalid job name: %v", job.Name)
|
|
| 119 |
- } |
|
| 120 |
- if strings.Join(job.Args, ":::") != "hello:::world" {
|
|
| 121 |
- t.Fatalf("Invalid job args: %v", job.Args)
|
|
| 122 |
- } |
|
| 123 |
- if job.Env().Get("DEBUG") != "1" {
|
|
| 124 |
- t.Fatalf("Invalid job env: %v", job.Env)
|
|
| 125 |
- } |
|
| 126 |
- if job.Env().Get("VERBOSITY") != "42" {
|
|
| 127 |
- t.Fatalf("Invalid job env: %v", job.Env)
|
|
| 128 |
- } |
|
| 129 |
- if len(job.Env().Map()) != 2 {
|
|
| 130 |
- t.Fatalf("Invalid job env: %v", job.Env)
|
|
| 131 |
- } |
|
| 132 |
- if err := job.Run(); err != nil {
|
|
| 133 |
- t.Fatal(err) |
|
| 134 |
- } |
|
| 135 |
- if !called {
|
|
| 136 |
- t.Fatalf("Job was not called")
|
|
| 137 |
- } |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-func TestCatchallEmptyName(t *testing.T) {
|
|
| 141 |
- eng := New() |
|
| 142 |
- var called bool |
|
| 143 |
- eng.RegisterCatchall(func(job *Job) error {
|
|
| 144 |
- called = true |
|
| 145 |
- return nil |
|
| 146 |
- }) |
|
| 147 |
- err := eng.Job("").Run()
|
|
| 148 |
- if err == nil {
|
|
| 149 |
- t.Fatalf("Engine.Job(\"\").Run() should return an error")
|
|
| 150 |
- } |
|
| 151 |
- if called {
|
|
| 152 |
- t.Fatalf("Engine.Job(\"\").Run() should return an error")
|
|
| 153 |
- } |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-// Ensure that a job within a job both using the same underlying standard |
|
| 157 |
-// output writer does not close the output of the outer job when the inner |
|
| 158 |
-// job's stdout is wrapped with a NopCloser. When not wrapped, it should |
|
| 159 |
-// close the outer job's output. |
|
| 160 |
-func TestNestedJobSharedOutput(t *testing.T) {
|
|
| 161 |
- var ( |
|
| 162 |
- outerHandler Handler |
|
| 163 |
- innerHandler Handler |
|
| 164 |
- wrapOutput bool |
|
| 165 |
- ) |
|
| 166 |
- |
|
| 167 |
- outerHandler = func(job *Job) error {
|
|
| 168 |
- job.Stdout.Write([]byte("outer1"))
|
|
| 169 |
- |
|
| 170 |
- innerJob := job.Eng.Job("innerJob")
|
|
| 171 |
- |
|
| 172 |
- if wrapOutput {
|
|
| 173 |
- innerJob.Stdout.Add(ioutils.NopWriteCloser(job.Stdout)) |
|
| 174 |
- } else {
|
|
| 175 |
- innerJob.Stdout.Add(job.Stdout) |
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- if err := innerJob.Run(); err != nil {
|
|
| 179 |
- t.Fatal(err) |
|
| 180 |
- } |
|
| 181 |
- |
|
| 182 |
- // If wrapOutput was *false* this write will do nothing. |
|
| 183 |
- // FIXME (jlhawn): It should cause an error to write to |
|
| 184 |
- // closed output. |
|
| 185 |
- job.Stdout.Write([]byte(" outer2"))
|
|
| 186 |
- |
|
| 187 |
- return nil |
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- innerHandler = func(job *Job) error {
|
|
| 191 |
- job.Stdout.Write([]byte(" inner"))
|
|
| 192 |
- |
|
| 193 |
- return nil |
|
| 194 |
- } |
|
| 195 |
- |
|
| 196 |
- eng := New() |
|
| 197 |
- eng.Register("outerJob", outerHandler)
|
|
| 198 |
- eng.Register("innerJob", innerHandler)
|
|
| 199 |
- |
|
| 200 |
- // wrapOutput starts *false* so the expected |
|
| 201 |
- // output of running the outer job will be: |
|
| 202 |
- // |
|
| 203 |
- // "outer1 inner" |
|
| 204 |
- // |
|
| 205 |
- outBuf := new(bytes.Buffer) |
|
| 206 |
- outerJob := eng.Job("outerJob")
|
|
| 207 |
- outerJob.Stdout.Add(outBuf) |
|
| 208 |
- |
|
| 209 |
- if err := outerJob.Run(); err != nil {
|
|
| 210 |
- t.Fatal(err) |
|
| 211 |
- } |
|
| 212 |
- |
|
| 213 |
- expectedOutput := "outer1 inner" |
|
| 214 |
- if outBuf.String() != expectedOutput {
|
|
| 215 |
- t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String())
|
|
| 216 |
- } |
|
| 217 |
- |
|
| 218 |
- // Set wrapOutput to true so that the expected |
|
| 219 |
- // output of running the outer job will be: |
|
| 220 |
- // |
|
| 221 |
- // "outer1 inner outer2" |
|
| 222 |
- // |
|
| 223 |
- wrapOutput = true |
|
| 224 |
- outBuf.Reset() |
|
| 225 |
- outerJob = eng.Job("outerJob")
|
|
| 226 |
- outerJob.Stdout.Add(outBuf) |
|
| 227 |
- |
|
| 228 |
- if err := outerJob.Run(); err != nil {
|
|
| 229 |
- t.Fatal(err) |
|
| 230 |
- } |
|
| 231 |
- |
|
| 232 |
- expectedOutput = "outer1 inner outer2" |
|
| 233 |
- if outBuf.String() != expectedOutput {
|
|
| 234 |
- t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String())
|
|
| 235 |
- } |
|
| 236 |
-} |
| 237 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,313 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "encoding/json" |
|
| 6 |
- "fmt" |
|
| 7 |
- "io" |
|
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 |
- "time" |
|
| 11 |
- |
|
| 12 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type Env []string |
|
| 16 |
- |
|
| 17 |
-// Get returns the last value associated with the given key. If there are no |
|
| 18 |
-// values associated with the key, Get returns the empty string. |
|
| 19 |
-func (env *Env) Get(key string) (value string) {
|
|
| 20 |
- // not using Map() because of the extra allocations https://github.com/docker/docker/pull/7488#issuecomment-51638315 |
|
| 21 |
- for _, kv := range *env {
|
|
| 22 |
- if strings.Index(kv, "=") == -1 {
|
|
| 23 |
- continue |
|
| 24 |
- } |
|
| 25 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 26 |
- if parts[0] != key {
|
|
| 27 |
- continue |
|
| 28 |
- } |
|
| 29 |
- if len(parts) < 2 {
|
|
| 30 |
- value = "" |
|
| 31 |
- } else {
|
|
| 32 |
- value = parts[1] |
|
| 33 |
- } |
|
| 34 |
- } |
|
| 35 |
- return |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func (env *Env) Exists(key string) bool {
|
|
| 39 |
- _, exists := env.Map()[key] |
|
| 40 |
- return exists |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-// Len returns the number of keys in the environment. |
|
| 44 |
-// Note that len(env) might be different from env.Len(), |
|
| 45 |
-// because the same key might be set multiple times. |
|
| 46 |
-func (env *Env) Len() int {
|
|
| 47 |
- return len(env.Map()) |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func (env *Env) Init(src *Env) {
|
|
| 51 |
- (*env) = make([]string, 0, len(*src)) |
|
| 52 |
- for _, val := range *src {
|
|
| 53 |
- (*env) = append((*env), val) |
|
| 54 |
- } |
|
| 55 |
-} |
|
| 56 |
- |
|
| 57 |
-func (env *Env) GetBool(key string) (value bool) {
|
|
| 58 |
- s := strings.ToLower(strings.Trim(env.Get(key), " \t")) |
|
| 59 |
- if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
|
| 60 |
- return false |
|
| 61 |
- } |
|
| 62 |
- return true |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-func (env *Env) SetBool(key string, value bool) {
|
|
| 66 |
- if value {
|
|
| 67 |
- env.Set(key, "1") |
|
| 68 |
- } else {
|
|
| 69 |
- env.Set(key, "0") |
|
| 70 |
- } |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-func (env *Env) GetTime(key string) (time.Time, error) {
|
|
| 74 |
- t, err := time.Parse(time.RFC3339Nano, env.Get(key)) |
|
| 75 |
- return t, err |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-func (env *Env) SetTime(key string, t time.Time) {
|
|
| 79 |
- env.Set(key, t.Format(time.RFC3339Nano)) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func (env *Env) GetInt(key string) int {
|
|
| 83 |
- return int(env.GetInt64(key)) |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-func (env *Env) GetInt64(key string) int64 {
|
|
| 87 |
- s := strings.Trim(env.Get(key), " \t") |
|
| 88 |
- val, err := strconv.ParseInt(s, 10, 64) |
|
| 89 |
- if err != nil {
|
|
| 90 |
- return 0 |
|
| 91 |
- } |
|
| 92 |
- return val |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-func (env *Env) SetInt(key string, value int) {
|
|
| 96 |
- env.Set(key, fmt.Sprintf("%d", value))
|
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func (env *Env) SetInt64(key string, value int64) {
|
|
| 100 |
- env.Set(key, fmt.Sprintf("%d", value))
|
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-// Returns nil if key not found |
|
| 104 |
-func (env *Env) GetList(key string) []string {
|
|
| 105 |
- sval := env.Get(key) |
|
| 106 |
- if sval == "" {
|
|
| 107 |
- return nil |
|
| 108 |
- } |
|
| 109 |
- l := make([]string, 0, 1) |
|
| 110 |
- if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
|
| 111 |
- l = append(l, sval) |
|
| 112 |
- } |
|
| 113 |
- return l |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-func (env *Env) GetSubEnv(key string) *Env {
|
|
| 117 |
- sval := env.Get(key) |
|
| 118 |
- if sval == "" {
|
|
| 119 |
- return nil |
|
| 120 |
- } |
|
| 121 |
- buf := bytes.NewBufferString(sval) |
|
| 122 |
- var sub Env |
|
| 123 |
- if err := sub.Decode(buf); err != nil {
|
|
| 124 |
- return nil |
|
| 125 |
- } |
|
| 126 |
- return &sub |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func (env *Env) SetSubEnv(key string, sub *Env) error {
|
|
| 130 |
- var buf bytes.Buffer |
|
| 131 |
- if err := sub.Encode(&buf); err != nil {
|
|
| 132 |
- return err |
|
| 133 |
- } |
|
| 134 |
- env.Set(key, string(buf.Bytes())) |
|
| 135 |
- return nil |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func (env *Env) GetJson(key string, iface interface{}) error {
|
|
| 139 |
- sval := env.Get(key) |
|
| 140 |
- if sval == "" {
|
|
| 141 |
- return nil |
|
| 142 |
- } |
|
| 143 |
- return json.Unmarshal([]byte(sval), iface) |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-func (env *Env) SetJson(key string, value interface{}) error {
|
|
| 147 |
- sval, err := json.Marshal(value) |
|
| 148 |
- if err != nil {
|
|
| 149 |
- return err |
|
| 150 |
- } |
|
| 151 |
- env.Set(key, string(sval)) |
|
| 152 |
- return nil |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-func (env *Env) SetList(key string, value []string) error {
|
|
| 156 |
- return env.SetJson(key, value) |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-func (env *Env) Set(key, value string) {
|
|
| 160 |
- *env = append(*env, key+"="+value) |
|
| 161 |
-} |
|
| 162 |
- |
|
| 163 |
-func NewDecoder(src io.Reader) *Decoder {
|
|
| 164 |
- return &Decoder{
|
|
| 165 |
- json.NewDecoder(src), |
|
| 166 |
- } |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-type Decoder struct {
|
|
| 170 |
- *json.Decoder |
|
| 171 |
-} |
|
| 172 |
- |
|
| 173 |
-func (decoder *Decoder) Decode() (*Env, error) {
|
|
| 174 |
- m := make(map[string]interface{})
|
|
| 175 |
- if err := decoder.Decoder.Decode(&m); err != nil {
|
|
| 176 |
- return nil, err |
|
| 177 |
- } |
|
| 178 |
- env := &Env{}
|
|
| 179 |
- for key, value := range m {
|
|
| 180 |
- env.SetAuto(key, value) |
|
| 181 |
- } |
|
| 182 |
- return env, nil |
|
| 183 |
-} |
|
| 184 |
- |
|
| 185 |
-// DecodeEnv decodes `src` as a json dictionary, and adds |
|
| 186 |
-// each decoded key-value pair to the environment. |
|
| 187 |
-// |
|
| 188 |
-// If `src` cannot be decoded as a json dictionary, an error |
|
| 189 |
-// is returned. |
|
| 190 |
-func (env *Env) Decode(src io.Reader) error {
|
|
| 191 |
- m := make(map[string]interface{})
|
|
| 192 |
- d := json.NewDecoder(src) |
|
| 193 |
- // We need this or we'll lose data when we decode int64 in json |
|
| 194 |
- d.UseNumber() |
|
| 195 |
- if err := d.Decode(&m); err != nil {
|
|
| 196 |
- return err |
|
| 197 |
- } |
|
| 198 |
- for k, v := range m {
|
|
| 199 |
- env.SetAuto(k, v) |
|
| 200 |
- } |
|
| 201 |
- return nil |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-func (env *Env) SetAuto(k string, v interface{}) {
|
|
| 205 |
- // Issue 7941 - if the value in the incoming JSON is null then treat it |
|
| 206 |
- // as if they never specified the property at all. |
|
| 207 |
- if v == nil {
|
|
| 208 |
- return |
|
| 209 |
- } |
|
| 210 |
- |
|
| 211 |
- // FIXME: we fix-convert float values to int, because |
|
| 212 |
- // encoding/json decodes integers to float64, but cannot encode them back. |
|
| 213 |
- // (See https://golang.org/src/pkg/encoding/json/decode.go#L46) |
|
| 214 |
- if fval, ok := v.(float64); ok {
|
|
| 215 |
- env.SetInt64(k, int64(fval)) |
|
| 216 |
- } else if sval, ok := v.(string); ok {
|
|
| 217 |
- env.Set(k, sval) |
|
| 218 |
- } else if val, err := json.Marshal(v); err == nil {
|
|
| 219 |
- env.Set(k, string(val)) |
|
| 220 |
- } else {
|
|
| 221 |
- env.Set(k, fmt.Sprintf("%v", v))
|
|
| 222 |
- } |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-func changeFloats(v interface{}) interface{} {
|
|
| 226 |
- switch v := v.(type) {
|
|
| 227 |
- case float64: |
|
| 228 |
- return int(v) |
|
| 229 |
- case map[string]interface{}:
|
|
| 230 |
- for key, val := range v {
|
|
| 231 |
- v[key] = changeFloats(val) |
|
| 232 |
- } |
|
| 233 |
- case []interface{}:
|
|
| 234 |
- for idx, val := range v {
|
|
| 235 |
- v[idx] = changeFloats(val) |
|
| 236 |
- } |
|
| 237 |
- } |
|
| 238 |
- return v |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-func (env *Env) Encode(dst io.Writer) error {
|
|
| 242 |
- m := make(map[string]interface{})
|
|
| 243 |
- for k, v := range env.Map() {
|
|
| 244 |
- var val interface{}
|
|
| 245 |
- if err := json.Unmarshal([]byte(v), &val); err == nil {
|
|
| 246 |
- // FIXME: we fix-convert float values to int, because |
|
| 247 |
- // encoding/json decodes integers to float64, but cannot encode them back. |
|
| 248 |
- // (See https://golang.org/src/pkg/encoding/json/decode.go#L46) |
|
| 249 |
- m[k] = changeFloats(val) |
|
| 250 |
- } else {
|
|
| 251 |
- m[k] = v |
|
| 252 |
- } |
|
| 253 |
- } |
|
| 254 |
- if err := json.NewEncoder(dst).Encode(&m); err != nil {
|
|
| 255 |
- return err |
|
| 256 |
- } |
|
| 257 |
- return nil |
|
| 258 |
-} |
|
| 259 |
- |
|
| 260 |
-func (env *Env) WriteTo(dst io.Writer) (int64, error) {
|
|
| 261 |
- wc := ioutils.NewWriteCounter(dst) |
|
| 262 |
- err := env.Encode(wc) |
|
| 263 |
- return wc.Count, err |
|
| 264 |
-} |
|
| 265 |
- |
|
| 266 |
-func (env *Env) Import(src interface{}) (err error) {
|
|
| 267 |
- defer func() {
|
|
| 268 |
- if err != nil {
|
|
| 269 |
- err = fmt.Errorf("ImportEnv: %s", err)
|
|
| 270 |
- } |
|
| 271 |
- }() |
|
| 272 |
- var buf bytes.Buffer |
|
| 273 |
- if err := json.NewEncoder(&buf).Encode(src); err != nil {
|
|
| 274 |
- return err |
|
| 275 |
- } |
|
| 276 |
- if err := env.Decode(&buf); err != nil {
|
|
| 277 |
- return err |
|
| 278 |
- } |
|
| 279 |
- return nil |
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 |
-func (env *Env) Map() map[string]string {
|
|
| 283 |
- m := make(map[string]string) |
|
| 284 |
- for _, kv := range *env {
|
|
| 285 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 286 |
- m[parts[0]] = parts[1] |
|
| 287 |
- } |
|
| 288 |
- return m |
|
| 289 |
-} |
|
| 290 |
- |
|
| 291 |
-// MultiMap returns a representation of env as a |
|
| 292 |
-// map of string arrays, keyed by string. |
|
| 293 |
-// This is the same structure as http headers for example, |
|
| 294 |
-// which allow each key to have multiple values. |
|
| 295 |
-func (env *Env) MultiMap() map[string][]string {
|
|
| 296 |
- m := make(map[string][]string) |
|
| 297 |
- for _, kv := range *env {
|
|
| 298 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 299 |
- m[parts[0]] = append(m[parts[0]], parts[1]) |
|
| 300 |
- } |
|
| 301 |
- return m |
|
| 302 |
-} |
|
| 303 |
- |
|
| 304 |
-// InitMultiMap removes all values in env, then initializes |
|
| 305 |
-// new values from the contents of m. |
|
| 306 |
-func (env *Env) InitMultiMap(m map[string][]string) {
|
|
| 307 |
- (*env) = make([]string, 0, len(m)) |
|
| 308 |
- for k, vals := range m {
|
|
| 309 |
- for _, v := range vals {
|
|
| 310 |
- env.Set(k, v) |
|
| 311 |
- } |
|
| 312 |
- } |
|
| 313 |
-} |
| 314 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,366 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "encoding/json" |
|
| 6 |
- "testing" |
|
| 7 |
- "time" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/docker/pkg/stringutils" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func TestEnvLenZero(t *testing.T) {
|
|
| 13 |
- env := &Env{}
|
|
| 14 |
- if env.Len() != 0 {
|
|
| 15 |
- t.Fatalf("%d", env.Len())
|
|
| 16 |
- } |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func TestEnvLenNotZero(t *testing.T) {
|
|
| 20 |
- env := &Env{}
|
|
| 21 |
- env.Set("foo", "bar")
|
|
| 22 |
- env.Set("ga", "bu")
|
|
| 23 |
- if env.Len() != 2 {
|
|
| 24 |
- t.Fatalf("%d", env.Len())
|
|
| 25 |
- } |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func TestEnvLenDup(t *testing.T) {
|
|
| 29 |
- env := &Env{
|
|
| 30 |
- "foo=bar", |
|
| 31 |
- "foo=baz", |
|
| 32 |
- "a=b", |
|
| 33 |
- } |
|
| 34 |
- // len(env) != env.Len() |
|
| 35 |
- if env.Len() != 2 {
|
|
| 36 |
- t.Fatalf("%d", env.Len())
|
|
| 37 |
- } |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func TestEnvGetDup(t *testing.T) {
|
|
| 41 |
- env := &Env{
|
|
| 42 |
- "foo=bar", |
|
| 43 |
- "foo=baz", |
|
| 44 |
- "foo=bif", |
|
| 45 |
- } |
|
| 46 |
- expected := "bif" |
|
| 47 |
- if v := env.Get("foo"); v != expected {
|
|
| 48 |
- t.Fatalf("expect %q, got %q", expected, v)
|
|
| 49 |
- } |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-func TestNewJob(t *testing.T) {
|
|
| 53 |
- job := mkJob(t, "dummy", "--level=awesome") |
|
| 54 |
- if job.Name != "dummy" {
|
|
| 55 |
- t.Fatalf("Wrong job name: %s", job.Name)
|
|
| 56 |
- } |
|
| 57 |
- if len(job.Args) != 1 {
|
|
| 58 |
- t.Fatalf("Wrong number of job arguments: %d", len(job.Args))
|
|
| 59 |
- } |
|
| 60 |
- if job.Args[0] != "--level=awesome" {
|
|
| 61 |
- t.Fatalf("Wrong job arguments: %s", job.Args[0])
|
|
| 62 |
- } |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-func TestSetenv(t *testing.T) {
|
|
| 66 |
- job := mkJob(t, "dummy") |
|
| 67 |
- job.Setenv("foo", "bar")
|
|
| 68 |
- if val := job.Getenv("foo"); val != "bar" {
|
|
| 69 |
- t.Fatalf("Getenv returns incorrect value: %s", val)
|
|
| 70 |
- } |
|
| 71 |
- |
|
| 72 |
- job.Setenv("bar", "")
|
|
| 73 |
- if val := job.Getenv("bar"); val != "" {
|
|
| 74 |
- t.Fatalf("Getenv returns incorrect value: %s", val)
|
|
| 75 |
- } |
|
| 76 |
- if val := job.Getenv("nonexistent"); val != "" {
|
|
| 77 |
- t.Fatalf("Getenv returns incorrect value: %s", val)
|
|
| 78 |
- } |
|
| 79 |
-} |
|
| 80 |
- |
|
| 81 |
-func TestDecodeEnv(t *testing.T) {
|
|
| 82 |
- job := mkJob(t, "dummy") |
|
| 83 |
- type tmp struct {
|
|
| 84 |
- Id1 int64 |
|
| 85 |
- Id2 int64 |
|
| 86 |
- } |
|
| 87 |
- body := []byte("{\"tags\":{\"Id1\":123, \"Id2\":1234567}}")
|
|
| 88 |
- if err := job.DecodeEnv(bytes.NewBuffer(body)); err != nil {
|
|
| 89 |
- t.Fatalf("DecodeEnv failed: %v", err)
|
|
| 90 |
- } |
|
| 91 |
- mytag := tmp{}
|
|
| 92 |
- if val := job.GetenvJson("tags", &mytag); val != nil {
|
|
| 93 |
- t.Fatalf("GetenvJson returns incorrect value: %s", val)
|
|
| 94 |
- } |
|
| 95 |
- |
|
| 96 |
- if mytag.Id1 != 123 || mytag.Id2 != 1234567 {
|
|
| 97 |
- t.Fatal("Get wrong values set by job.DecodeEnv")
|
|
| 98 |
- } |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-func TestSetenvBool(t *testing.T) {
|
|
| 102 |
- job := mkJob(t, "dummy") |
|
| 103 |
- job.SetenvBool("foo", true)
|
|
| 104 |
- if val := job.GetenvBool("foo"); !val {
|
|
| 105 |
- t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
|
| 106 |
- } |
|
| 107 |
- |
|
| 108 |
- job.SetenvBool("bar", false)
|
|
| 109 |
- if val := job.GetenvBool("bar"); val {
|
|
| 110 |
- t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- if val := job.GetenvBool("nonexistent"); val {
|
|
| 114 |
- t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
|
| 115 |
- } |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-func TestSetenvTime(t *testing.T) {
|
|
| 119 |
- job := mkJob(t, "dummy") |
|
| 120 |
- |
|
| 121 |
- now := time.Now() |
|
| 122 |
- job.SetenvTime("foo", now)
|
|
| 123 |
- if val, err := job.GetenvTime("foo"); err != nil {
|
|
| 124 |
- t.Fatalf("GetenvTime failed to parse: %v", err)
|
|
| 125 |
- } else {
|
|
| 126 |
- nowStr := now.Format(time.RFC3339) |
|
| 127 |
- valStr := val.Format(time.RFC3339) |
|
| 128 |
- if nowStr != valStr {
|
|
| 129 |
- t.Fatalf("GetenvTime returns incorrect value: %s, Expected: %s", valStr, nowStr)
|
|
| 130 |
- } |
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- job.Setenv("bar", "Obviously I'm not a date")
|
|
| 134 |
- if val, err := job.GetenvTime("bar"); err == nil {
|
|
| 135 |
- t.Fatalf("GetenvTime was supposed to fail, instead returned: %s", val)
|
|
| 136 |
- } |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-func TestSetenvInt(t *testing.T) {
|
|
| 140 |
- job := mkJob(t, "dummy") |
|
| 141 |
- |
|
| 142 |
- job.SetenvInt("foo", -42)
|
|
| 143 |
- if val := job.GetenvInt("foo"); val != -42 {
|
|
| 144 |
- t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- job.SetenvInt("bar", 42)
|
|
| 148 |
- if val := job.GetenvInt("bar"); val != 42 {
|
|
| 149 |
- t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
|
| 150 |
- } |
|
| 151 |
- if val := job.GetenvInt("nonexistent"); val != 0 {
|
|
| 152 |
- t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
|
| 153 |
- } |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-func TestSetenvList(t *testing.T) {
|
|
| 157 |
- job := mkJob(t, "dummy") |
|
| 158 |
- |
|
| 159 |
- job.SetenvList("foo", []string{"bar"})
|
|
| 160 |
- if val := job.GetenvList("foo"); len(val) != 1 || val[0] != "bar" {
|
|
| 161 |
- t.Fatalf("GetenvList returns incorrect value: %v", val)
|
|
| 162 |
- } |
|
| 163 |
- |
|
| 164 |
- job.SetenvList("bar", nil)
|
|
| 165 |
- if val := job.GetenvList("bar"); val != nil {
|
|
| 166 |
- t.Fatalf("GetenvList returns incorrect value: %v", val)
|
|
| 167 |
- } |
|
| 168 |
- if val := job.GetenvList("nonexistent"); val != nil {
|
|
| 169 |
- t.Fatalf("GetenvList returns incorrect value: %v", val)
|
|
| 170 |
- } |
|
| 171 |
-} |
|
| 172 |
- |
|
| 173 |
-func TestEnviron(t *testing.T) {
|
|
| 174 |
- job := mkJob(t, "dummy") |
|
| 175 |
- job.Setenv("foo", "bar")
|
|
| 176 |
- val, exists := job.Environ()["foo"] |
|
| 177 |
- if !exists {
|
|
| 178 |
- t.Fatalf("foo not found in the environ")
|
|
| 179 |
- } |
|
| 180 |
- if val != "bar" {
|
|
| 181 |
- t.Fatalf("bar not found in the environ")
|
|
| 182 |
- } |
|
| 183 |
-} |
|
| 184 |
- |
|
| 185 |
-func TestMultiMap(t *testing.T) {
|
|
| 186 |
- e := &Env{}
|
|
| 187 |
- e.Set("foo", "bar")
|
|
| 188 |
- e.Set("bar", "baz")
|
|
| 189 |
- e.Set("hello", "world")
|
|
| 190 |
- m := e.MultiMap() |
|
| 191 |
- e2 := &Env{}
|
|
| 192 |
- e2.Set("old_key", "something something something")
|
|
| 193 |
- e2.InitMultiMap(m) |
|
| 194 |
- if v := e2.Get("old_key"); v != "" {
|
|
| 195 |
- t.Fatalf("%#v", v)
|
|
| 196 |
- } |
|
| 197 |
- if v := e2.Get("bar"); v != "baz" {
|
|
| 198 |
- t.Fatalf("%#v", v)
|
|
| 199 |
- } |
|
| 200 |
- if v := e2.Get("hello"); v != "world" {
|
|
| 201 |
- t.Fatalf("%#v", v)
|
|
| 202 |
- } |
|
| 203 |
-} |
|
| 204 |
- |
|
| 205 |
-func testMap(l int) [][2]string {
|
|
| 206 |
- res := make([][2]string, l) |
|
| 207 |
- for i := 0; i < l; i++ {
|
|
| 208 |
- t := [2]string{stringutils.GenerateRandomAsciiString(5), stringutils.GenerateRandomAsciiString(20)}
|
|
| 209 |
- res[i] = t |
|
| 210 |
- } |
|
| 211 |
- return res |
|
| 212 |
-} |
|
| 213 |
- |
|
| 214 |
-func BenchmarkSet(b *testing.B) {
|
|
| 215 |
- fix := testMap(100) |
|
| 216 |
- b.ResetTimer() |
|
| 217 |
- for i := 0; i < b.N; i++ {
|
|
| 218 |
- env := &Env{}
|
|
| 219 |
- for _, kv := range fix {
|
|
| 220 |
- env.Set(kv[0], kv[1]) |
|
| 221 |
- } |
|
| 222 |
- } |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-func BenchmarkSetJson(b *testing.B) {
|
|
| 226 |
- fix := testMap(100) |
|
| 227 |
- type X struct {
|
|
| 228 |
- f string |
|
| 229 |
- } |
|
| 230 |
- b.ResetTimer() |
|
| 231 |
- for i := 0; i < b.N; i++ {
|
|
| 232 |
- env := &Env{}
|
|
| 233 |
- for _, kv := range fix {
|
|
| 234 |
- if err := env.SetJson(kv[0], X{kv[1]}); err != nil {
|
|
| 235 |
- b.Fatal(err) |
|
| 236 |
- } |
|
| 237 |
- } |
|
| 238 |
- } |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-func BenchmarkGet(b *testing.B) {
|
|
| 242 |
- fix := testMap(100) |
|
| 243 |
- env := &Env{}
|
|
| 244 |
- for _, kv := range fix {
|
|
| 245 |
- env.Set(kv[0], kv[1]) |
|
| 246 |
- } |
|
| 247 |
- b.ResetTimer() |
|
| 248 |
- for i := 0; i < b.N; i++ {
|
|
| 249 |
- for _, kv := range fix {
|
|
| 250 |
- env.Get(kv[0]) |
|
| 251 |
- } |
|
| 252 |
- } |
|
| 253 |
-} |
|
| 254 |
- |
|
| 255 |
-func BenchmarkGetJson(b *testing.B) {
|
|
| 256 |
- fix := testMap(100) |
|
| 257 |
- env := &Env{}
|
|
| 258 |
- type X struct {
|
|
| 259 |
- f string |
|
| 260 |
- } |
|
| 261 |
- for _, kv := range fix {
|
|
| 262 |
- env.SetJson(kv[0], X{kv[1]})
|
|
| 263 |
- } |
|
| 264 |
- b.ResetTimer() |
|
| 265 |
- for i := 0; i < b.N; i++ {
|
|
| 266 |
- for _, kv := range fix {
|
|
| 267 |
- if err := env.GetJson(kv[0], &X{}); err != nil {
|
|
| 268 |
- b.Fatal(err) |
|
| 269 |
- } |
|
| 270 |
- } |
|
| 271 |
- } |
|
| 272 |
-} |
|
| 273 |
- |
|
| 274 |
-func BenchmarkEncode(b *testing.B) {
|
|
| 275 |
- fix := testMap(100) |
|
| 276 |
- env := &Env{}
|
|
| 277 |
- type X struct {
|
|
| 278 |
- f string |
|
| 279 |
- } |
|
| 280 |
- // half a json |
|
| 281 |
- for i, kv := range fix {
|
|
| 282 |
- if i%2 != 0 {
|
|
| 283 |
- if err := env.SetJson(kv[0], X{kv[1]}); err != nil {
|
|
| 284 |
- b.Fatal(err) |
|
| 285 |
- } |
|
| 286 |
- continue |
|
| 287 |
- } |
|
| 288 |
- env.Set(kv[0], kv[1]) |
|
| 289 |
- } |
|
| 290 |
- var writer bytes.Buffer |
|
| 291 |
- b.ResetTimer() |
|
| 292 |
- for i := 0; i < b.N; i++ {
|
|
| 293 |
- env.Encode(&writer) |
|
| 294 |
- writer.Reset() |
|
| 295 |
- } |
|
| 296 |
-} |
|
| 297 |
- |
|
| 298 |
-func BenchmarkDecode(b *testing.B) {
|
|
| 299 |
- fix := testMap(100) |
|
| 300 |
- env := &Env{}
|
|
| 301 |
- type X struct {
|
|
| 302 |
- f string |
|
| 303 |
- } |
|
| 304 |
- // half a json |
|
| 305 |
- for i, kv := range fix {
|
|
| 306 |
- if i%2 != 0 {
|
|
| 307 |
- if err := env.SetJson(kv[0], X{kv[1]}); err != nil {
|
|
| 308 |
- b.Fatal(err) |
|
| 309 |
- } |
|
| 310 |
- continue |
|
| 311 |
- } |
|
| 312 |
- env.Set(kv[0], kv[1]) |
|
| 313 |
- } |
|
| 314 |
- var writer bytes.Buffer |
|
| 315 |
- env.Encode(&writer) |
|
| 316 |
- denv := &Env{}
|
|
| 317 |
- reader := bytes.NewReader(writer.Bytes()) |
|
| 318 |
- b.ResetTimer() |
|
| 319 |
- for i := 0; i < b.N; i++ {
|
|
| 320 |
- err := denv.Decode(reader) |
|
| 321 |
- if err != nil {
|
|
| 322 |
- b.Fatal(err) |
|
| 323 |
- } |
|
| 324 |
- reader.Seek(0, 0) |
|
| 325 |
- } |
|
| 326 |
-} |
|
| 327 |
- |
|
| 328 |
-func TestLongNumbers(t *testing.T) {
|
|
| 329 |
- type T struct {
|
|
| 330 |
- TestNum int64 |
|
| 331 |
- } |
|
| 332 |
- v := T{67108864}
|
|
| 333 |
- var buf bytes.Buffer |
|
| 334 |
- e := &Env{}
|
|
| 335 |
- e.SetJson("Test", v)
|
|
| 336 |
- if err := e.Encode(&buf); err != nil {
|
|
| 337 |
- t.Fatal(err) |
|
| 338 |
- } |
|
| 339 |
- res := make(map[string]T) |
|
| 340 |
- if err := json.Unmarshal(buf.Bytes(), &res); err != nil {
|
|
| 341 |
- t.Fatal(err) |
|
| 342 |
- } |
|
| 343 |
- if res["Test"].TestNum != v.TestNum {
|
|
| 344 |
- t.Fatalf("TestNum %d, expected %d", res["Test"].TestNum, v.TestNum)
|
|
| 345 |
- } |
|
| 346 |
-} |
|
| 347 |
- |
|
| 348 |
-func TestLongNumbersArray(t *testing.T) {
|
|
| 349 |
- type T struct {
|
|
| 350 |
- TestNum []int64 |
|
| 351 |
- } |
|
| 352 |
- v := T{[]int64{67108864}}
|
|
| 353 |
- var buf bytes.Buffer |
|
| 354 |
- e := &Env{}
|
|
| 355 |
- e.SetJson("Test", v)
|
|
| 356 |
- if err := e.Encode(&buf); err != nil {
|
|
| 357 |
- t.Fatal(err) |
|
| 358 |
- } |
|
| 359 |
- res := make(map[string]T) |
|
| 360 |
- if err := json.Unmarshal(buf.Bytes(), &res); err != nil {
|
|
| 361 |
- t.Fatal(err) |
|
| 362 |
- } |
|
| 363 |
- if res["Test"].TestNum[0] != v.TestNum[0] {
|
|
| 364 |
- t.Fatalf("TestNum %d, expected %d", res["Test"].TestNum, v.TestNum)
|
|
| 365 |
- } |
|
| 366 |
-} |
| 367 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,21 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-type Hack map[string]interface{}
|
|
| 4 |
- |
|
| 5 |
-func (eng *Engine) HackGetGlobalVar(key string) interface{} {
|
|
| 6 |
- if eng.hack == nil {
|
|
| 7 |
- return nil |
|
| 8 |
- } |
|
| 9 |
- val, exists := eng.hack[key] |
|
| 10 |
- if !exists {
|
|
| 11 |
- return nil |
|
| 12 |
- } |
|
| 13 |
- return val |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 |
-func (eng *Engine) HackSetGlobalVar(key string, val interface{}) {
|
|
| 17 |
- if eng.hack == nil {
|
|
| 18 |
- eng.hack = make(Hack) |
|
| 19 |
- } |
|
| 20 |
- eng.hack[key] = val |
|
| 21 |
-} |
| 12 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,42 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "net/http" |
|
| 5 |
- "path" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// ServeHTTP executes a job as specified by the http request `r`, and sends the |
|
| 9 |
-// result as an http response. |
|
| 10 |
-// This method allows an Engine instance to be passed as a standard http.Handler interface. |
|
| 11 |
-// |
|
| 12 |
-// Note that the protocol used in this method is a convenience wrapper and is not the canonical |
|
| 13 |
-// implementation of remote job execution. This is because HTTP/1 does not handle stream multiplexing, |
|
| 14 |
-// and so cannot differentiate stdout from stderr. Additionally, headers cannot be added to a response |
|
| 15 |
-// once data has been written to the body, which makes it inconvenient to return metadata such |
|
| 16 |
-// as the exit status. |
|
| 17 |
-// |
|
| 18 |
-func (eng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
| 19 |
- var ( |
|
| 20 |
- jobName = path.Base(r.URL.Path) |
|
| 21 |
- jobArgs, exists = r.URL.Query()["a"] |
|
| 22 |
- ) |
|
| 23 |
- if !exists {
|
|
| 24 |
- jobArgs = []string{}
|
|
| 25 |
- } |
|
| 26 |
- w.Header().Set("Job-Name", jobName)
|
|
| 27 |
- for _, arg := range jobArgs {
|
|
| 28 |
- w.Header().Add("Job-Args", arg)
|
|
| 29 |
- } |
|
| 30 |
- job := eng.Job(jobName, jobArgs...) |
|
| 31 |
- job.Stdout.Add(w) |
|
| 32 |
- job.Stderr.Add(w) |
|
| 33 |
- // FIXME: distinguish job status from engine error in Run() |
|
| 34 |
- // The former should be passed as a special header, the former |
|
| 35 |
- // should cause a 500 status |
|
| 36 |
- w.WriteHeader(http.StatusOK) |
|
| 37 |
- // The exit status cannot be sent reliably with HTTP1, because headers |
|
| 38 |
- // can only be sent before the body. |
|
| 39 |
- // (we could possibly use http footers via chunked encoding, but I couldn't find |
|
| 40 |
- // how to use them in net/http) |
|
| 41 |
- job.Run() |
|
| 42 |
-} |
| 43 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,222 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "strings" |
|
| 8 |
- "sync" |
|
| 9 |
- "time" |
|
| 10 |
- |
|
| 11 |
- "github.com/Sirupsen/logrus" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-// A job is the fundamental unit of work in the docker engine. |
|
| 15 |
-// Everything docker can do should eventually be exposed as a job. |
|
| 16 |
-// For example: execute a process in a container, create a new container, |
|
| 17 |
-// download an archive from the internet, serve the http api, etc. |
|
| 18 |
-// |
|
| 19 |
-// The job API is designed after unix processes: a job has a name, arguments, |
|
| 20 |
-// environment variables, standard streams for input, output and error. |
|
| 21 |
-type Job struct {
|
|
| 22 |
- Eng *Engine |
|
| 23 |
- Name string |
|
| 24 |
- Args []string |
|
| 25 |
- env *Env |
|
| 26 |
- Stdout *Output |
|
| 27 |
- Stderr *Output |
|
| 28 |
- Stdin *Input |
|
| 29 |
- handler Handler |
|
| 30 |
- end time.Time |
|
| 31 |
- closeIO bool |
|
| 32 |
- |
|
| 33 |
- // When closed, the job has been cancelled. |
|
| 34 |
- // Note: not all jobs implement cancellation. |
|
| 35 |
- // See Job.Cancel() and Job.WaitCancelled() |
|
| 36 |
- cancelled chan struct{}
|
|
| 37 |
- cancelOnce sync.Once |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// Run executes the job and blocks until the job completes. |
|
| 41 |
-// If the job fails it returns an error |
|
| 42 |
-func (job *Job) Run() (err error) {
|
|
| 43 |
- defer func() {
|
|
| 44 |
- // Wait for all background tasks to complete |
|
| 45 |
- if job.closeIO {
|
|
| 46 |
- if err := job.Stdout.Close(); err != nil {
|
|
| 47 |
- logrus.Error(err) |
|
| 48 |
- } |
|
| 49 |
- if err := job.Stderr.Close(); err != nil {
|
|
| 50 |
- logrus.Error(err) |
|
| 51 |
- } |
|
| 52 |
- if err := job.Stdin.Close(); err != nil {
|
|
| 53 |
- logrus.Error(err) |
|
| 54 |
- } |
|
| 55 |
- } |
|
| 56 |
- }() |
|
| 57 |
- |
|
| 58 |
- if job.Eng.IsShutdown() && !job.GetenvBool("overrideShutdown") {
|
|
| 59 |
- return fmt.Errorf("engine is shutdown")
|
|
| 60 |
- } |
|
| 61 |
- // FIXME: this is a temporary workaround to avoid Engine.Shutdown |
|
| 62 |
- // waiting 5 seconds for server/api.ServeApi to complete (which it never will) |
|
| 63 |
- // everytime the daemon is cleanly restarted. |
|
| 64 |
- // The permanent fix is to implement Job.Stop and Job.OnStop so that |
|
| 65 |
- // ServeApi can cooperate and terminate cleanly. |
|
| 66 |
- if job.Name != "serveapi" {
|
|
| 67 |
- job.Eng.l.Lock() |
|
| 68 |
- job.Eng.tasks.Add(1) |
|
| 69 |
- job.Eng.l.Unlock() |
|
| 70 |
- defer job.Eng.tasks.Done() |
|
| 71 |
- } |
|
| 72 |
- // FIXME: make this thread-safe |
|
| 73 |
- // FIXME: implement wait |
|
| 74 |
- if !job.end.IsZero() {
|
|
| 75 |
- return fmt.Errorf("%s: job has already completed", job.Name)
|
|
| 76 |
- } |
|
| 77 |
- // Log beginning and end of the job |
|
| 78 |
- if job.Eng.Logging {
|
|
| 79 |
- logrus.Infof("+job %s", job.CallString())
|
|
| 80 |
- defer func() {
|
|
| 81 |
- okerr := "OK" |
|
| 82 |
- if err != nil {
|
|
| 83 |
- okerr = fmt.Sprintf("ERR: %s", err)
|
|
| 84 |
- } |
|
| 85 |
- logrus.Infof("-job %s %s", job.CallString(), okerr)
|
|
| 86 |
- }() |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- if job.handler == nil {
|
|
| 90 |
- return fmt.Errorf("%s: command not found", job.Name)
|
|
| 91 |
- } |
|
| 92 |
- |
|
| 93 |
- var errorMessage = bytes.NewBuffer(nil) |
|
| 94 |
- job.Stderr.Add(errorMessage) |
|
| 95 |
- |
|
| 96 |
- err = job.handler(job) |
|
| 97 |
- job.end = time.Now() |
|
| 98 |
- |
|
| 99 |
- return |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func (job *Job) CallString() string {
|
|
| 103 |
- return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", "))
|
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-func (job *Job) Env() *Env {
|
|
| 107 |
- return job.env |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-func (job *Job) EnvExists(key string) (value bool) {
|
|
| 111 |
- return job.env.Exists(key) |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-func (job *Job) Getenv(key string) (value string) {
|
|
| 115 |
- return job.env.Get(key) |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-func (job *Job) GetenvBool(key string) (value bool) {
|
|
| 119 |
- return job.env.GetBool(key) |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-func (job *Job) SetenvBool(key string, value bool) {
|
|
| 123 |
- job.env.SetBool(key, value) |
|
| 124 |
-} |
|
| 125 |
- |
|
| 126 |
-func (job *Job) GetenvTime(key string) (value time.Time, err error) {
|
|
| 127 |
- return job.env.GetTime(key) |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-func (job *Job) SetenvTime(key string, value time.Time) {
|
|
| 131 |
- job.env.SetTime(key, value) |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-func (job *Job) GetenvSubEnv(key string) *Env {
|
|
| 135 |
- return job.env.GetSubEnv(key) |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func (job *Job) SetenvSubEnv(key string, value *Env) error {
|
|
| 139 |
- return job.env.SetSubEnv(key, value) |
|
| 140 |
-} |
|
| 141 |
- |
|
| 142 |
-func (job *Job) GetenvInt64(key string) int64 {
|
|
| 143 |
- return job.env.GetInt64(key) |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-func (job *Job) GetenvInt(key string) int {
|
|
| 147 |
- return job.env.GetInt(key) |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func (job *Job) SetenvInt64(key string, value int64) {
|
|
| 151 |
- job.env.SetInt64(key, value) |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func (job *Job) SetenvInt(key string, value int) {
|
|
| 155 |
- job.env.SetInt(key, value) |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-// Returns nil if key not found |
|
| 159 |
-func (job *Job) GetenvList(key string) []string {
|
|
| 160 |
- return job.env.GetList(key) |
|
| 161 |
-} |
|
| 162 |
- |
|
| 163 |
-func (job *Job) GetenvJson(key string, iface interface{}) error {
|
|
| 164 |
- return job.env.GetJson(key, iface) |
|
| 165 |
-} |
|
| 166 |
- |
|
| 167 |
-func (job *Job) SetenvJson(key string, value interface{}) error {
|
|
| 168 |
- return job.env.SetJson(key, value) |
|
| 169 |
-} |
|
| 170 |
- |
|
| 171 |
-func (job *Job) SetenvList(key string, value []string) error {
|
|
| 172 |
- return job.env.SetJson(key, value) |
|
| 173 |
-} |
|
| 174 |
- |
|
| 175 |
-func (job *Job) Setenv(key, value string) {
|
|
| 176 |
- job.env.Set(key, value) |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-// DecodeEnv decodes `src` as a json dictionary, and adds |
|
| 180 |
-// each decoded key-value pair to the environment. |
|
| 181 |
-// |
|
| 182 |
-// If `src` cannot be decoded as a json dictionary, an error |
|
| 183 |
-// is returned. |
|
| 184 |
-func (job *Job) DecodeEnv(src io.Reader) error {
|
|
| 185 |
- return job.env.Decode(src) |
|
| 186 |
-} |
|
| 187 |
- |
|
| 188 |
-func (job *Job) EncodeEnv(dst io.Writer) error {
|
|
| 189 |
- return job.env.Encode(dst) |
|
| 190 |
-} |
|
| 191 |
- |
|
| 192 |
-func (job *Job) ImportEnv(src interface{}) (err error) {
|
|
| 193 |
- return job.env.Import(src) |
|
| 194 |
-} |
|
| 195 |
- |
|
| 196 |
-func (job *Job) Environ() map[string]string {
|
|
| 197 |
- return job.env.Map() |
|
| 198 |
-} |
|
| 199 |
- |
|
| 200 |
-func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
|
|
| 201 |
- return fmt.Fprintf(job.Stdout, format, args...) |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-func (job *Job) Errorf(format string, args ...interface{}) (n int, err error) {
|
|
| 205 |
- return fmt.Fprintf(job.Stderr, format, args...) |
|
| 206 |
-} |
|
| 207 |
- |
|
| 208 |
-func (job *Job) SetCloseIO(val bool) {
|
|
| 209 |
- job.closeIO = val |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-// When called, causes the Job.WaitCancelled channel to unblock. |
|
| 213 |
-func (job *Job) Cancel() {
|
|
| 214 |
- job.cancelOnce.Do(func() {
|
|
| 215 |
- close(job.cancelled) |
|
| 216 |
- }) |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 |
-// Returns a channel which is closed ("never blocks") when the job is cancelled.
|
|
| 220 |
-func (job *Job) WaitCancelled() <-chan struct{} {
|
|
| 221 |
- return job.cancelled |
|
| 222 |
-} |
| 223 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,47 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "errors" |
|
| 6 |
- "fmt" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func TestJobOK(t *testing.T) {
|
|
| 11 |
- eng := New() |
|
| 12 |
- eng.Register("return_ok", func(job *Job) error { return nil })
|
|
| 13 |
- err := eng.Job("return_ok").Run()
|
|
| 14 |
- if err != nil {
|
|
| 15 |
- t.Fatalf("Expected: err=%v\nReceived: err=%v", nil, err)
|
|
| 16 |
- } |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func TestJobErr(t *testing.T) {
|
|
| 20 |
- eng := New() |
|
| 21 |
- eng.Register("return_err", func(job *Job) error { return errors.New("return_err") })
|
|
| 22 |
- err := eng.Job("return_err").Run()
|
|
| 23 |
- if err == nil {
|
|
| 24 |
- t.Fatalf("When a job returns error, Run() should return an error")
|
|
| 25 |
- } |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func TestJobStdoutString(t *testing.T) {
|
|
| 29 |
- eng := New() |
|
| 30 |
- // FIXME: test multiple combinations of output and status |
|
| 31 |
- eng.Register("say_something_in_stdout", func(job *Job) error {
|
|
| 32 |
- job.Printf("Hello world\n")
|
|
| 33 |
- return nil |
|
| 34 |
- }) |
|
| 35 |
- |
|
| 36 |
- job := eng.Job("say_something_in_stdout")
|
|
| 37 |
- var outputBuffer = bytes.NewBuffer(nil) |
|
| 38 |
- job.Stdout.Add(outputBuffer) |
|
| 39 |
- if err := job.Run(); err != nil {
|
|
| 40 |
- t.Fatal(err) |
|
| 41 |
- } |
|
| 42 |
- fmt.Println(outputBuffer) |
|
| 43 |
- var output = Tail(outputBuffer, 1) |
|
| 44 |
- if expectedOutput := "Hello world"; output != expectedOutput {
|
|
| 45 |
- t.Fatalf("Stdout last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
|
|
| 46 |
- } |
|
| 47 |
-} |
| 48 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,78 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
- "time" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func TestShutdownEmpty(t *testing.T) {
|
|
| 9 |
- eng := New() |
|
| 10 |
- if eng.IsShutdown() {
|
|
| 11 |
- t.Fatalf("IsShutdown should be false")
|
|
| 12 |
- } |
|
| 13 |
- eng.Shutdown() |
|
| 14 |
- if !eng.IsShutdown() {
|
|
| 15 |
- t.Fatalf("IsShutdown should be true")
|
|
| 16 |
- } |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func TestShutdownAfterRun(t *testing.T) {
|
|
| 20 |
- eng := New() |
|
| 21 |
- eng.Register("foo", func(job *Job) error {
|
|
| 22 |
- return nil |
|
| 23 |
- }) |
|
| 24 |
- if err := eng.Job("foo").Run(); err != nil {
|
|
| 25 |
- t.Fatal(err) |
|
| 26 |
- } |
|
| 27 |
- eng.Shutdown() |
|
| 28 |
- if err := eng.Job("foo").Run(); err == nil {
|
|
| 29 |
- t.Fatalf("%#v", *eng)
|
|
| 30 |
- } |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-// An approximate and racy, but better-than-nothing test that |
|
| 34 |
-// |
|
| 35 |
-func TestShutdownDuringRun(t *testing.T) {
|
|
| 36 |
- var ( |
|
| 37 |
- jobDelay time.Duration = 500 * time.Millisecond |
|
| 38 |
- jobDelayLow time.Duration = 100 * time.Millisecond |
|
| 39 |
- jobDelayHigh time.Duration = 700 * time.Millisecond |
|
| 40 |
- ) |
|
| 41 |
- eng := New() |
|
| 42 |
- var completed bool |
|
| 43 |
- eng.Register("foo", func(job *Job) error {
|
|
| 44 |
- time.Sleep(jobDelay) |
|
| 45 |
- completed = true |
|
| 46 |
- return nil |
|
| 47 |
- }) |
|
| 48 |
- go eng.Job("foo").Run()
|
|
| 49 |
- time.Sleep(50 * time.Millisecond) |
|
| 50 |
- done := make(chan struct{})
|
|
| 51 |
- var startShutdown time.Time |
|
| 52 |
- go func() {
|
|
| 53 |
- startShutdown = time.Now() |
|
| 54 |
- eng.Shutdown() |
|
| 55 |
- close(done) |
|
| 56 |
- }() |
|
| 57 |
- time.Sleep(50 * time.Millisecond) |
|
| 58 |
- if err := eng.Job("foo").Run(); err == nil {
|
|
| 59 |
- t.Fatalf("run on shutdown should fail: %#v", *eng)
|
|
| 60 |
- } |
|
| 61 |
- <-done |
|
| 62 |
- // Verify that Shutdown() blocks for roughly 500ms, instead |
|
| 63 |
- // of returning almost instantly. |
|
| 64 |
- // |
|
| 65 |
- // We use >100ms to leave ample margin for race conditions between |
|
| 66 |
- // goroutines. It's possible (but unlikely in reasonable testing |
|
| 67 |
- // conditions), that this test will cause a false positive or false |
|
| 68 |
- // negative. But it's probably better than not having any test |
|
| 69 |
- // for the 99.999% of time where testing conditions are reasonable. |
|
| 70 |
- if d := time.Since(startShutdown); d.Nanoseconds() < jobDelayLow.Nanoseconds() {
|
|
| 71 |
- t.Fatalf("shutdown did not block long enough: %v", d)
|
|
| 72 |
- } else if d.Nanoseconds() > jobDelayHigh.Nanoseconds() {
|
|
| 73 |
- t.Fatalf("shutdown blocked too long: %v", d)
|
|
| 74 |
- } |
|
| 75 |
- if !completed {
|
|
| 76 |
- t.Fatalf("job did not complete")
|
|
| 77 |
- } |
|
| 78 |
-} |
| 79 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,188 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "strings" |
|
| 8 |
- "sync" |
|
| 9 |
- "unicode" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-type Output struct {
|
|
| 13 |
- sync.Mutex |
|
| 14 |
- dests []io.Writer |
|
| 15 |
- tasks sync.WaitGroup |
|
| 16 |
- used bool |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-// Tail returns the n last lines of a buffer |
|
| 20 |
-// stripped out of trailing white spaces, if any. |
|
| 21 |
-// |
|
| 22 |
-// if n <= 0, returns an empty string |
|
| 23 |
-func Tail(buffer *bytes.Buffer, n int) string {
|
|
| 24 |
- if n <= 0 {
|
|
| 25 |
- return "" |
|
| 26 |
- } |
|
| 27 |
- s := strings.TrimRightFunc(buffer.String(), unicode.IsSpace) |
|
| 28 |
- i := len(s) - 1 |
|
| 29 |
- for ; i >= 0 && n > 0; i-- {
|
|
| 30 |
- if s[i] == '\n' {
|
|
| 31 |
- n-- |
|
| 32 |
- if n == 0 {
|
|
| 33 |
- break |
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- } |
|
| 37 |
- // when i == -1, return the whole string which is s[0:] |
|
| 38 |
- return s[i+1:] |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-// NewOutput returns a new Output object with no destinations attached. |
|
| 42 |
-// Writing to an empty Output will cause the written data to be discarded. |
|
| 43 |
-func NewOutput() *Output {
|
|
| 44 |
- return &Output{}
|
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-// Return true if something was written on this output |
|
| 48 |
-func (o *Output) Used() bool {
|
|
| 49 |
- o.Lock() |
|
| 50 |
- defer o.Unlock() |
|
| 51 |
- return o.used |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-// Add attaches a new destination to the Output. Any data subsequently written |
|
| 55 |
-// to the output will be written to the new destination in addition to all the others. |
|
| 56 |
-// This method is thread-safe. |
|
| 57 |
-func (o *Output) Add(dst io.Writer) {
|
|
| 58 |
- o.Lock() |
|
| 59 |
- defer o.Unlock() |
|
| 60 |
- o.dests = append(o.dests, dst) |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// Set closes and remove existing destination and then attaches a new destination to |
|
| 64 |
-// the Output. Any data subsequently written to the output will be written to the new |
|
| 65 |
-// destination in addition to all the others. This method is thread-safe. |
|
| 66 |
-func (o *Output) Set(dst io.Writer) {
|
|
| 67 |
- o.Close() |
|
| 68 |
- o.Lock() |
|
| 69 |
- defer o.Unlock() |
|
| 70 |
- o.dests = []io.Writer{dst}
|
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-// AddPipe creates an in-memory pipe with io.Pipe(), adds its writing end as a destination, |
|
| 74 |
-// and returns its reading end for consumption by the caller. |
|
| 75 |
-// This is a rough equivalent similar to Cmd.StdoutPipe() in the standard os/exec package. |
|
| 76 |
-// This method is thread-safe. |
|
| 77 |
-func (o *Output) AddPipe() (io.Reader, error) {
|
|
| 78 |
- r, w := io.Pipe() |
|
| 79 |
- o.Add(w) |
|
| 80 |
- return r, nil |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-// Write writes the same data to all registered destinations. |
|
| 84 |
-// This method is thread-safe. |
|
| 85 |
-func (o *Output) Write(p []byte) (n int, err error) {
|
|
| 86 |
- o.Lock() |
|
| 87 |
- defer o.Unlock() |
|
| 88 |
- o.used = true |
|
| 89 |
- var firstErr error |
|
| 90 |
- for _, dst := range o.dests {
|
|
| 91 |
- _, err := dst.Write(p) |
|
| 92 |
- if err != nil && firstErr == nil {
|
|
| 93 |
- firstErr = err |
|
| 94 |
- } |
|
| 95 |
- } |
|
| 96 |
- return len(p), firstErr |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-// Close unregisters all destinations and waits for all background |
|
| 100 |
-// AddTail and AddString tasks to complete. |
|
| 101 |
-// The Close method of each destination is called if it exists. |
|
| 102 |
-func (o *Output) Close() error {
|
|
| 103 |
- o.Lock() |
|
| 104 |
- defer o.Unlock() |
|
| 105 |
- var firstErr error |
|
| 106 |
- for _, dst := range o.dests {
|
|
| 107 |
- if closer, ok := dst.(io.Closer); ok {
|
|
| 108 |
- err := closer.Close() |
|
| 109 |
- if err != nil && firstErr == nil {
|
|
| 110 |
- firstErr = err |
|
| 111 |
- } |
|
| 112 |
- } |
|
| 113 |
- } |
|
| 114 |
- o.tasks.Wait() |
|
| 115 |
- o.dests = nil |
|
| 116 |
- return firstErr |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-type Input struct {
|
|
| 120 |
- src io.Reader |
|
| 121 |
- sync.Mutex |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-// NewInput returns a new Input object with no source attached. |
|
| 125 |
-// Reading to an empty Input will return io.EOF. |
|
| 126 |
-func NewInput() *Input {
|
|
| 127 |
- return &Input{}
|
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-// Read reads from the input in a thread-safe way. |
|
| 131 |
-func (i *Input) Read(p []byte) (n int, err error) {
|
|
| 132 |
- i.Mutex.Lock() |
|
| 133 |
- defer i.Mutex.Unlock() |
|
| 134 |
- if i.src == nil {
|
|
| 135 |
- return 0, io.EOF |
|
| 136 |
- } |
|
| 137 |
- return i.src.Read(p) |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-// Closes the src |
|
| 141 |
-// Not thread safe on purpose |
|
| 142 |
-func (i *Input) Close() error {
|
|
| 143 |
- if i.src != nil {
|
|
| 144 |
- if closer, ok := i.src.(io.Closer); ok {
|
|
| 145 |
- return closer.Close() |
|
| 146 |
- } |
|
| 147 |
- } |
|
| 148 |
- return nil |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-// Add attaches a new source to the input. |
|
| 152 |
-// Add can only be called once per input. Subsequent calls will |
|
| 153 |
-// return an error. |
|
| 154 |
-func (i *Input) Add(src io.Reader) error {
|
|
| 155 |
- i.Mutex.Lock() |
|
| 156 |
- defer i.Mutex.Unlock() |
|
| 157 |
- if i.src != nil {
|
|
| 158 |
- return fmt.Errorf("Maximum number of sources reached: 1")
|
|
| 159 |
- } |
|
| 160 |
- i.src = src |
|
| 161 |
- return nil |
|
| 162 |
-} |
|
| 163 |
- |
|
| 164 |
-// AddEnv starts a new goroutine which will decode all subsequent data |
|
| 165 |
-// as a stream of json-encoded objects, and point `dst` to the last |
|
| 166 |
-// decoded object. |
|
| 167 |
-// The result `env` can be queried using the type-neutral Env interface. |
|
| 168 |
-// It is not safe to query `env` until the Output is closed. |
|
| 169 |
-func (o *Output) AddEnv() (dst *Env, err error) {
|
|
| 170 |
- src, err := o.AddPipe() |
|
| 171 |
- if err != nil {
|
|
| 172 |
- return nil, err |
|
| 173 |
- } |
|
| 174 |
- dst = &Env{}
|
|
| 175 |
- o.tasks.Add(1) |
|
| 176 |
- go func() {
|
|
| 177 |
- defer o.tasks.Done() |
|
| 178 |
- decoder := NewDecoder(src) |
|
| 179 |
- for {
|
|
| 180 |
- env, err := decoder.Decode() |
|
| 181 |
- if err != nil {
|
|
| 182 |
- return |
|
| 183 |
- } |
|
| 184 |
- *dst = *env |
|
| 185 |
- } |
|
| 186 |
- }() |
|
| 187 |
- return dst, nil |
|
| 188 |
-} |
| 189 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,215 +0,0 @@ |
| 1 |
-package engine |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 6 |
- "fmt" |
|
| 7 |
- "io" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "strings" |
|
| 10 |
- "testing" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-type sentinelWriteCloser struct {
|
|
| 14 |
- calledWrite bool |
|
| 15 |
- calledClose bool |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-func (w *sentinelWriteCloser) Write(p []byte) (int, error) {
|
|
| 19 |
- w.calledWrite = true |
|
| 20 |
- return len(p), nil |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func (w *sentinelWriteCloser) Close() error {
|
|
| 24 |
- w.calledClose = true |
|
| 25 |
- return nil |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func TestOutputAddEnv(t *testing.T) {
|
|
| 29 |
- input := "{\"foo\": \"bar\", \"answer_to_life_the_universe_and_everything\": 42}"
|
|
| 30 |
- o := NewOutput() |
|
| 31 |
- result, err := o.AddEnv() |
|
| 32 |
- if err != nil {
|
|
| 33 |
- t.Fatal(err) |
|
| 34 |
- } |
|
| 35 |
- o.Write([]byte(input)) |
|
| 36 |
- o.Close() |
|
| 37 |
- if v := result.Get("foo"); v != "bar" {
|
|
| 38 |
- t.Errorf("Expected %v, got %v", "bar", v)
|
|
| 39 |
- } |
|
| 40 |
- if v := result.GetInt("answer_to_life_the_universe_and_everything"); v != 42 {
|
|
| 41 |
- t.Errorf("Expected %v, got %v", 42, v)
|
|
| 42 |
- } |
|
| 43 |
- if v := result.Get("this-value-doesnt-exist"); v != "" {
|
|
| 44 |
- t.Errorf("Expected %v, got %v", "", v)
|
|
| 45 |
- } |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func TestOutputAddClose(t *testing.T) {
|
|
| 49 |
- o := NewOutput() |
|
| 50 |
- var s sentinelWriteCloser |
|
| 51 |
- o.Add(&s) |
|
| 52 |
- if err := o.Close(); err != nil {
|
|
| 53 |
- t.Fatal(err) |
|
| 54 |
- } |
|
| 55 |
- // Write data after the output is closed. |
|
| 56 |
- // Write should succeed, but no destination should receive it. |
|
| 57 |
- if _, err := o.Write([]byte("foo bar")); err != nil {
|
|
| 58 |
- t.Fatal(err) |
|
| 59 |
- } |
|
| 60 |
- if !s.calledClose {
|
|
| 61 |
- t.Fatal("Output.Close() didn't close the destination")
|
|
| 62 |
- } |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-func TestOutputAddPipe(t *testing.T) {
|
|
| 66 |
- var testInputs = []string{
|
|
| 67 |
- "hello, world!", |
|
| 68 |
- "One\nTwo\nThree", |
|
| 69 |
- "", |
|
| 70 |
- "A line\nThen another nl-terminated line\n", |
|
| 71 |
- "A line followed by an empty line\n\n", |
|
| 72 |
- } |
|
| 73 |
- for _, input := range testInputs {
|
|
| 74 |
- expectedOutput := input |
|
| 75 |
- o := NewOutput() |
|
| 76 |
- r, err := o.AddPipe() |
|
| 77 |
- if err != nil {
|
|
| 78 |
- t.Fatal(err) |
|
| 79 |
- } |
|
| 80 |
- go func(o *Output) {
|
|
| 81 |
- if n, err := o.Write([]byte(input)); err != nil {
|
|
| 82 |
- t.Error(err) |
|
| 83 |
- } else if n != len(input) {
|
|
| 84 |
- t.Errorf("Expected %d, got %d", len(input), n)
|
|
| 85 |
- } |
|
| 86 |
- if err := o.Close(); err != nil {
|
|
| 87 |
- t.Error(err) |
|
| 88 |
- } |
|
| 89 |
- }(o) |
|
| 90 |
- output, err := ioutil.ReadAll(r) |
|
| 91 |
- if err != nil {
|
|
| 92 |
- t.Fatal(err) |
|
| 93 |
- } |
|
| 94 |
- if string(output) != expectedOutput {
|
|
| 95 |
- t.Errorf("Last line is not stored as return string.\nExpected: '%s'\nGot: '%s'", expectedOutput, output)
|
|
| 96 |
- } |
|
| 97 |
- } |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func TestTail(t *testing.T) {
|
|
| 101 |
- var tests = make(map[string][]string) |
|
| 102 |
- tests["hello, world!"] = []string{
|
|
| 103 |
- "", |
|
| 104 |
- "hello, world!", |
|
| 105 |
- "hello, world!", |
|
| 106 |
- "hello, world!", |
|
| 107 |
- } |
|
| 108 |
- tests["One\nTwo\nThree"] = []string{
|
|
| 109 |
- "", |
|
| 110 |
- "Three", |
|
| 111 |
- "Two\nThree", |
|
| 112 |
- "One\nTwo\nThree", |
|
| 113 |
- } |
|
| 114 |
- tests["One\nTwo\n\n\n"] = []string{
|
|
| 115 |
- "", |
|
| 116 |
- "Two", |
|
| 117 |
- "One\nTwo", |
|
| 118 |
- } |
|
| 119 |
- for input, outputs := range tests {
|
|
| 120 |
- for n, expectedOutput := range outputs {
|
|
| 121 |
- output := Tail(bytes.NewBufferString(input), n) |
|
| 122 |
- if output != expectedOutput {
|
|
| 123 |
- t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot : '%s'", n, expectedOutput, output)
|
|
| 124 |
- } |
|
| 125 |
- } |
|
| 126 |
- } |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func lastLine(txt string) string {
|
|
| 130 |
- scanner := bufio.NewScanner(strings.NewReader(txt)) |
|
| 131 |
- var lastLine string |
|
| 132 |
- for scanner.Scan() {
|
|
| 133 |
- lastLine = scanner.Text() |
|
| 134 |
- } |
|
| 135 |
- return lastLine |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func TestOutputAdd(t *testing.T) {
|
|
| 139 |
- o := NewOutput() |
|
| 140 |
- b := &bytes.Buffer{}
|
|
| 141 |
- o.Add(b) |
|
| 142 |
- input := "hello, world!" |
|
| 143 |
- if n, err := o.Write([]byte(input)); err != nil {
|
|
| 144 |
- t.Fatal(err) |
|
| 145 |
- } else if n != len(input) {
|
|
| 146 |
- t.Fatalf("Expected %d, got %d", len(input), n)
|
|
| 147 |
- } |
|
| 148 |
- if output := b.String(); output != input {
|
|
| 149 |
- t.Fatalf("Received wrong data from Add.\nExpected: '%s'\nGot: '%s'", input, output)
|
|
| 150 |
- } |
|
| 151 |
-} |
|
| 152 |
- |
|
| 153 |
-func TestOutputWriteError(t *testing.T) {
|
|
| 154 |
- o := NewOutput() |
|
| 155 |
- buf := &bytes.Buffer{}
|
|
| 156 |
- o.Add(buf) |
|
| 157 |
- r, w := io.Pipe() |
|
| 158 |
- input := "Hello there" |
|
| 159 |
- expectedErr := fmt.Errorf("This is an error")
|
|
| 160 |
- r.CloseWithError(expectedErr) |
|
| 161 |
- o.Add(w) |
|
| 162 |
- n, err := o.Write([]byte(input)) |
|
| 163 |
- if err != expectedErr {
|
|
| 164 |
- t.Fatalf("Output.Write() should return the first error encountered, if any")
|
|
| 165 |
- } |
|
| 166 |
- if buf.String() != input {
|
|
| 167 |
- t.Fatalf("Output.Write() should attempt write on all destinations, even after encountering an error")
|
|
| 168 |
- } |
|
| 169 |
- if n != len(input) {
|
|
| 170 |
- t.Fatalf("Output.Write() should return the size of the input if it successfully writes to at least one destination")
|
|
| 171 |
- } |
|
| 172 |
-} |
|
| 173 |
- |
|
| 174 |
-func TestInputAddEmpty(t *testing.T) {
|
|
| 175 |
- i := NewInput() |
|
| 176 |
- var b bytes.Buffer |
|
| 177 |
- if err := i.Add(&b); err != nil {
|
|
| 178 |
- t.Fatal(err) |
|
| 179 |
- } |
|
| 180 |
- data, err := ioutil.ReadAll(i) |
|
| 181 |
- if err != nil {
|
|
| 182 |
- t.Fatal(err) |
|
| 183 |
- } |
|
| 184 |
- if len(data) > 0 {
|
|
| 185 |
- t.Fatalf("Read from empty input should yield no data")
|
|
| 186 |
- } |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-func TestInputAddTwo(t *testing.T) {
|
|
| 190 |
- i := NewInput() |
|
| 191 |
- var b1 bytes.Buffer |
|
| 192 |
- // First add should succeed |
|
| 193 |
- if err := i.Add(&b1); err != nil {
|
|
| 194 |
- t.Fatal(err) |
|
| 195 |
- } |
|
| 196 |
- var b2 bytes.Buffer |
|
| 197 |
- // Second add should fail |
|
| 198 |
- if err := i.Add(&b2); err == nil {
|
|
| 199 |
- t.Fatalf("Adding a second source should return an error")
|
|
| 200 |
- } |
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-func TestInputAddNotEmpty(t *testing.T) {
|
|
| 204 |
- i := NewInput() |
|
| 205 |
- b := bytes.NewBufferString("hello world\nabc")
|
|
| 206 |
- expectedResult := b.String() |
|
| 207 |
- i.Add(b) |
|
| 208 |
- result, err := ioutil.ReadAll(i) |
|
| 209 |
- if err != nil {
|
|
| 210 |
- t.Fatal(err) |
|
| 211 |
- } |
|
| 212 |
- if string(result) != expectedResult {
|
|
| 213 |
- t.Fatalf("Expected: %v\nReceived: %v", expectedResult, result)
|
|
| 214 |
- } |
|
| 215 |
-} |
| ... | ... |
@@ -24,15 +24,15 @@ func checkPidFileAlreadyExists(path string) error {
|
| 24 | 24 |
return nil |
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 |
-func New(path string) (file *PidFile, err error) {
|
|
| 27 |
+func New(path string) (*PidFile, error) {
|
|
| 28 | 28 |
if err := checkPidFileAlreadyExists(path); err != nil {
|
| 29 | 29 |
return nil, err |
| 30 | 30 |
} |
| 31 |
+ if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
|
|
| 32 |
+ return nil, err |
|
| 33 |
+ } |
|
| 31 | 34 |
|
| 32 |
- file = &PidFile{path: path}
|
|
| 33 |
- err = ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644)
|
|
| 34 |
- |
|
| 35 |
- return file, err |
|
| 35 |
+ return &PidFile{path: path}, nil
|
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
func (file PidFile) Remove() error {
|