func TestGetImagesJSONLegacyFormat(t *testing.T) { eng := engine.New() var called bool eng.Register("images", func(job *engine.Job) engine.Status { called = true outsLegacy := engine.NewTable("Created", 0) outsLegacy.Add(createEnvFromGetImagesJSONStruct(sampleImage)) if _, err := outsLegacy.WriteListTo(job.Stdout); err != nil { return job.Error(err) } return engine.StatusOK }) r := serveRequestUsingVersion("GET", "/images/json", "1.6", nil, eng, t) if !called { t.Fatal("handler was not called") } assertHttpNotError(r, t) assertContentType(r, "application/json", t) images := engine.NewTable("Created", 0) if _, err := images.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } if images.Len() != 1 { t.Fatalf("Expected 1 image, %d found", images.Len()) } image := images.Data[0] if image.Get("Tag") != "test-tag" { t.Errorf("Expected tag 'test-tag', found '%s'", image.Get("Tag")) } if image.Get("Repository") != "test-name" { t.Errorf("Expected repository 'test-name', found '%s'", image.Get("Repository")) } }
func TestImageTagImageDelete(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) initialImages := getAllImages(eng, t) if err := eng.Job("tag", unitTestImageName, "utest", "tag1").Run(); err != nil { t.Fatal(err) } if err := eng.Job("tag", unitTestImageName, "utest/docker", "tag2").Run(); err != nil { t.Fatal(err) } if err := eng.Job("tag", unitTestImageName, "utest:5000/docker", "tag3").Run(); err != nil { t.Fatal(err) } images := getAllImages(eng, t) nExpected := len(initialImages.Data[0].GetList("RepoTags")) + 3 nActual := len(images.Data[0].GetList("RepoTags")) if nExpected != nActual { t.Errorf("Expected %d images, %d found", nExpected, nActual) } if err := srv.DeleteImage("utest/docker:tag2", engine.NewTable("", 0), true, false, false); err != nil { t.Fatal(err) } images = getAllImages(eng, t) nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 2 nActual = len(images.Data[0].GetList("RepoTags")) if nExpected != nActual { t.Errorf("Expected %d images, %d found", nExpected, nActual) } if err := srv.DeleteImage("utest:5000/docker:tag3", engine.NewTable("", 0), true, false, false); err != nil { t.Fatal(err) } images = getAllImages(eng, t) nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1 nActual = len(images.Data[0].GetList("RepoTags")) if err := srv.DeleteImage("utest:tag1", engine.NewTable("", 0), true, false, false); err != nil { t.Fatal(err) } images = getAllImages(eng, t) if images.Len() != initialImages.Len() { t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len()) } }
func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( buffer *bytes.Buffer job = srv.Eng.Job("images") ) job.Setenv("filter", r.Form.Get("filter")) job.Setenv("all", r.Form.Get("all")) if version >= 1.9 { job.Stdout.Add(w) } else { buffer = bytes.NewBuffer(nil) job.Stdout.Add(buffer) } if err := job.Run(); err != nil { return err } if version < 1.9 { // Send as a valid JSON array outs := engine.NewTable("Created", 0) if _, err := outs.ReadFrom(buffer); err != nil { return err } if version < 1.8 { // Convert to legacy format outsLegacy := engine.NewTable("Created", 0) for _, out := range outs.Data { for _, repoTag := range out.GetList("RepoTags") { parts := strings.Split(repoTag, ":") outLegacy := &engine.Env{} outLegacy.Set("Repository", parts[0]) outLegacy.Set("Tag", parts[1]) outLegacy.Set("ID", out.Get("ID")) outLegacy.SetInt64("Created", out.GetInt64("Created")) outLegacy.SetInt64("Size", out.GetInt64("Size")) outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize")) outsLegacy.Add(outLegacy) } } if _, err := outsLegacy.WriteListTo(w); err != nil { return err } } else if _, err := outs.WriteListTo(w); err != nil { return err } } return nil }
// Search queries the public registry for images matching the specified // search terms, and returns the results. // // Argument syntax: search TERM // // Option environment: // 'authConfig': json-encoded credentials to authenticate against the registry. // The search extends to images only accessible via the credentials. // // 'metaHeaders': extra HTTP headers to include in the request to the registry. // The headers should be passed as a json-encoded dictionary. // // Output: // Results are sent as a collection of structured messages (using engine.Table). // Each result is sent as a separate message. // Results are ordered by number of stars on the public registry. func (s *Service) Search(job *engine.Job) engine.Status { if n := len(job.Args); n != 1 { return job.Errorf("Usage: %s TERM", job.Name) } var ( term = job.Args[0] metaHeaders = map[string][]string{} authConfig = &AuthConfig{} ) job.GetenvJson("authConfig", authConfig) job.GetenvJson("metaHeaders", metaHeaders) r, err := NewRegistry(authConfig, HTTPRequestFactory(metaHeaders), IndexServerAddress()) if err != nil { return job.Error(err) } results, err := r.SearchRepositories(term) if err != nil { return job.Error(err) } outs := engine.NewTable("star_count", 0) for _, result := range results.Results { out := &engine.Env{} out.Import(result) outs.Add(out) } outs.ReverseSort() if _, err := outs.WriteListTo(job.Stdout); err != nil { return job.Error(err) } return engine.StatusOK }
func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( buffer *bytes.Buffer job = srv.Eng.Job("search", r.Form.Get("term")) ) if version >= 1.9 { job.Stdout.Add(w) } else { buffer = bytes.NewBuffer(nil) job.Stdout.Add(buffer) } if err := job.Run(); err != nil { return err } if version < 1.9 { // Send as a valid JSON array outs := engine.NewTable("", 0) if _, err := outs.ReadFrom(buffer); err != nil { return err } if _, err := outs.WriteListTo(w); err != nil { return err } } return nil }
func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var ( buffer *bytes.Buffer job = srv.Eng.Job("history", vars["name"]) ) if version >= 1.9 { job.Stdout.Add(w) } else { buffer = bytes.NewBuffer(nil) job.Stdout.Add(buffer) } if err := job.Run(); err != nil { return err } if version < 1.9 { // Send as a valid JSON array outs := engine.NewTable("Created", 0) if _, err := outs.ReadFrom(buffer); err != nil { return err } if _, err := outs.WriteListTo(w); err != nil { return err } } return nil }
func TestDeleteImages(t *testing.T) { eng := NewTestEngine(t) //we expect errors, so we disable stderr eng.Stderr = ioutil.Discard defer mkRuntimeFromEngine(eng, t).Nuke() initialImages := getImages(eng, t, true, "") if err := eng.Job("tag", unitTestImageName, "test", "test").Run(); err != nil { t.Fatal(err) } images := getImages(eng, t, true, "") if len(images.Data[0].GetList("RepoTags")) != len(initialImages.Data[0].GetList("RepoTags"))+1 { t.Errorf("Expected %d images, %d found", len(initialImages.Data[0].GetList("RepoTags"))+1, len(images.Data[0].GetList("RepoTags"))) } req, err := http.NewRequest("DELETE", "/images/"+unitTestImageID, nil) if err != nil { t.Fatal(err) } r := httptest.NewRecorder() if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusConflict { t.Fatalf("Expected http status 409-conflict, got %v", r.Code) } req2, err := http.NewRequest("DELETE", "/images/test:test", nil) if err != nil { t.Fatal(err) } r2 := httptest.NewRecorder() if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil { t.Fatal(err) } assertHttpNotError(r2, t) if r2.Code != http.StatusOK { t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code) } outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom(r2.Body.Bytes()); err != nil { t.Fatal(err) } if len(outs.Data) != 1 { t.Fatalf("Expected %d event (untagged), got %d", 1, len(outs.Data)) } images = getImages(eng, t, false, "") if images.Len() != initialImages.Len() { t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len()) } }
// docker主机上的容器 func dockerContainers(c *utils.Connection, data []byte) { dst := engine.NewTable("", 0) if _, err := dst.ReadListFrom(data); err != nil { log.Println("Read table error:", err) } if content, err := dst.ToListString(); err != nil { log.Println("Table to string error:", err) } else { log.Println("Containers:", content) } }
// Regression test for being able to untag an image with an existing // container func TestDeleteTagWithExistingContainers(t *testing.T) { eng := NewTestEngine(t) defer nuke(mkRuntimeFromEngine(eng, t)) srv := mkServerFromEngine(eng, t) // Tag the image if err := eng.Job("tag", unitTestImageID, "utest", "tag1").Run(); err != nil { t.Fatal(err) } // Create a container from the image config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil) if err != nil { t.Fatal(err) } id := createNamedTestContainer(eng, config, t, "testingtags") if id == "" { t.Fatal("No id returned") } job := srv.Eng.Job("containers") job.SetenvBool("all", true) outs, err := job.Stdout.AddListTable() if err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } if len(outs.Data) != 1 { t.Fatalf("Expected 1 container got %d", len(outs.Data)) } // Try to remove the tag imgs := engine.NewTable("", 0) if err := srv.DeleteImage("utest:tag1", imgs, true, false, false); err != nil { t.Fatal(err) } if len(imgs.Data) != 1 { t.Fatalf("Should only have deleted one untag %d", len(imgs.Data)) } if untag := imgs.Data[0].Get("Untagged"); untag != "utest:tag1" { t.Fatalf("Expected %s got %s", unitTestImageID, untag) } }
func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( err error outs *engine.Table job = eng.Job("images") ) job.Setenv("filters", r.Form.Get("filters")) // FIXME this parameter could just be a match filter job.Setenv("filter", r.Form.Get("filter")) job.Setenv("all", r.Form.Get("all")) if version.GreaterThanOrEqualTo("1.7") { streamJSON(job, w, false) } else if outs, err = job.Stdout.AddListTable(); err != nil { return err } if err := job.Run(); err != nil { return err } if version.LessThan("1.7") && outs != nil { // Convert to legacy format outsLegacy := engine.NewTable("Created", 0) for _, out := range outs.Data { for _, repoTag := range out.GetList("RepoTags") { parts := strings.Split(repoTag, ":") outLegacy := &engine.Env{} outLegacy.Set("Repository", parts[0]) outLegacy.Set("Tag", parts[1]) outLegacy.Set("Id", out.Get("Id")) outLegacy.SetInt64("Created", out.GetInt64("Created")) outLegacy.SetInt64("Size", out.GetInt64("Size")) outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize")) outsLegacy.Add(outLegacy) } } w.Header().Set("Content-Type", "application/json") if _, err := outsLegacy.WriteListTo(w); err != nil { return err } } return nil }
// docker主机上的镜像 func dockerImages(c *utils.Connection, data []byte) { dst := engine.NewTable("", 0) if _, err := dst.ReadListFrom(data); err != nil { log.Println("Read table error:", err) } for _, env := range dst.Data { created, err := strconv.ParseInt(env.Get("Created"), 10, 64) if err == nil { image := &resource.Image{Host: c.Src, Created: created} registry.RegistryServer.RegisterImage(env.Get("Id"), image) log.Printf("Register image id:%s host:%s", env.Get("Id"), c.Src) } else { log.Printf("Parse image created time error:%s", err) } } }
func TestGetContainersJSON(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) job := eng.Job("containers") job.SetenvBool("all", true) outs, err := job.Stdout.AddTable() if err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } beginLen := len(outs.Data) containerID := createTestContainer(eng, &docker.Config{ Image: unitTestImageID, Cmd: []string{"echo", "test"}, }, t) if containerID == "" { t.Fatalf("Received empty container ID") } req, err := http.NewRequest("GET", "/containers/json?all=1", nil) if err != nil { t.Fatal(err) } r := httptest.NewRecorder() if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) containers := engine.NewTable("", 0) if _, err := containers.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } if len(containers.Data) != beginLen+1 { t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers.Data), beginLen) } if id := containers.Data[0].Get("ID"); id != containerID { t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, id) } }
func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( err error outs *engine.Table job = eng.Job("images") ) job.Setenv("filter", r.Form.Get("filter")) job.Setenv("all", r.Form.Get("all")) if version >= 1.7 { job.Stdout.Add(w) } else if outs, err = job.Stdout.AddListTable(); err != nil { return err } if err := job.Run(); err != nil { return err } if version < 1.7 && outs != nil { // Convert to legacy format outsLegacy := engine.NewTable("Created", 0) for _, out := range outs.Data { for _, repoTag := range out.GetList("RepoTags") { parts := strings.Split(repoTag, ":") outLegacy := &engine.Env{} outLegacy.Set("Repository", parts[0]) outLegacy.Set("Tag", parts[1]) outLegacy.Set("Id", out.Get("Id")) outLegacy.SetInt64("Created", out.GetInt64("Created")) outLegacy.SetInt64("Size", out.GetInt64("Size")) outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize")) outsLegacy.Add(outLegacy) } } if _, err := outsLegacy.WriteListTo(w); err != nil { return err } } return nil }
func (b *dockerClientBackend) ls() ([]string, error) { resp, err := b.client.call("GET", "/containers/json", "") if err != nil { return nil, fmt.Errorf("get: %v", err) } // FIXME: check for response error c := engine.NewTable("Created", 0) body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("read body: %v", err) } if _, err := c.ReadListFrom(body); err != nil { return nil, fmt.Errorf("readlist: %v", err) } names := []string{} for _, env := range c.Data { names = append(names, env.GetList("Names")[0][1:]) } return names, nil }
func TestGetContainersChanges(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) // Create a container and remove a file containerID := createTestContainer(eng, &docker.Config{ Image: unitTestImageID, Cmd: []string{"/bin/rm", "/etc/passwd"}, }, t, ) containerRun(eng, containerID, t) r := httptest.NewRecorder() req, err := http.NewRequest("GET", "/containers/"+containerID+"/changes", nil) if err != nil { t.Fatal(err) } if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) outs := engine.NewTable("", 0) if _, err := outs.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } // Check the changelog success := false for _, elem := range outs.Data { if elem.Get("Path") == "/etc/passwd" && elem.GetInt("Kind") == 2 { success = true } } if !success { t.Fatalf("/etc/passwd as been removed but is not present in the diff") } }
func (t *tutumBackend) ls(ctx *beam.Message) error { resp, err := t.tutumDockerConnector.call("GET", "/containers/json", "") if err != nil { return fmt.Errorf("%s: get: %v", t.tutumDockerConnector.URL.String(), err) } c := engine.NewTable("Created", 0) body, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("%s: read body: %v", t.tutumDockerConnector.URL.String(), err) } if _, err := c.ReadListFrom(body); err != nil { return fmt.Errorf("%s: readlist: %v", t.tutumDockerConnector.URL.String(), err) } ids := []string{} for _, env := range c.Data { ids = append(ids, env.GetList("Id")[0]) } if _, err := ctx.Ret.Send(&beam.Message{Verb: beam.Set, Args: ids}); err != nil { return fmt.Errorf("%s: send response: %v", t.tutumDockerConnector.URL.String(), err) } return nil }
func TestGetImagesHistory(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() r := httptest.NewRecorder() req, err := http.NewRequest("GET", fmt.Sprintf("/images/%s/history", unitTestImageName), nil) if err != nil { t.Fatal(err) } if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) outs := engine.NewTable("Created", 0) if _, err := outs.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } if len(outs.Data) != 1 { t.Errorf("Expected 1 line, %d found", len(outs.Data)) } }
func (settings *NetworkSettings) PortMappingAPI() *engine.Table { var outs = engine.NewTable("", 0) for port, bindings := range settings.Ports { p, _ := parsePort(port.Port()) if len(bindings) == 0 { out := &engine.Env{} out.SetInt("PublicPort", p) out.Set("Type", port.Proto()) outs.Add(out) continue } for _, binding := range bindings { out := &engine.Env{} h, _ := parsePort(binding.HostPort) out.SetInt("PrivatePort", p) out.SetInt("PublicPort", h) out.Set("Type", port.Proto()) out.Set("IP", binding.HostIp) outs.Add(out) } } return outs }
func (b *dockerClientBackend) ls(ctx *beam.Message) error { resp, err := b.client.call("GET", "/containers/json", "") if err != nil { return fmt.Errorf("get: %v", err) } // FIXME: check for response error c := engine.NewTable("Created", 0) body, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("read body: %v", err) } if _, err := c.ReadListFrom(body); err != nil { return fmt.Errorf("readlist: %v", err) } names := []string{} for _, env := range c.Data { names = append(names, env.GetList("Names")[0][1:]) } if _, err := ctx.Ret.Send(&beam.Message{Verb: beam.Set, Args: names}); err != nil { return fmt.Errorf("send response: %v", err) } return nil }
func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( err error outs *engine.Table job = eng.Job("containers") ) job.Setenv("all", r.Form.Get("all")) job.Setenv("size", r.Form.Get("size")) job.Setenv("since", r.Form.Get("since")) job.Setenv("before", r.Form.Get("before")) job.Setenv("limit", r.Form.Get("limit")) if version >= 1.5 { w.Header().Set("Content-Type", "application/json") job.Stdout.Add(w) } else if outs, err = job.Stdout.AddTable(); err != nil { return err } if err = job.Run(); err != nil { return err } if version < 1.5 { // Convert to legacy format for _, out := range outs.Data { ports := engine.NewTable("", 0) ports.ReadListFrom([]byte(out.Get("Ports"))) out.Set("Ports", displayablePorts(ports)) } if _, err = outs.WriteListTo(w); err != nil { return err } } return nil }
func (s *cloud) Install(eng *engine.Engine) error { eng.Register("cloud", func(job *engine.Job) engine.Status { if len(job.Args) < 3 { return job.Errorf("usage: %s <provider> <instance> <zone> <aditional> <provider> <args> <proto>://<addr>", job.Name) } instance := job.Args[1] zone := job.Args[2] var cloud Cloud var err error switch job.Args[0] { case "gce": if len(job.Args) < 4 { return job.Errorf("usage: %s gce <instance> <zone> <project>") } cloud, err = NewCloudGCE(job.Args[3]) if err != nil { return job.Errorf("Unexpected error: %#v", err) } default: return job.Errorf("Unknown cloud provider: %s", job.Args[0]) } ip, err := cloud.GetPublicIPAddress(instance, zone) instanceRunning := len(ip) > 0 if !instanceRunning { log.Print("Instance doesn't exist, creating....") _, err = cloud.CreateInstance(instance, zone) } if err != nil { return job.Errorf("Unexpected error: %#v", err) } remotePort := 8000 localPort := 8001 apiVersion := "v1.10" tunnelUrl, err := url.Parse(fmt.Sprintf("http://localhost:%d/%s/containers/json", localPort, apiVersion)) if err != nil { return job.Errorf("Unexpected error: %#v", err) } tunnel := Tunnel{*tunnelUrl} if !tunnel.isActive() { fmt.Printf("Creating tunnel") _, err = cloud.OpenSecureTunnel(instance, zone, localPort, remotePort) if err != nil { return job.Errorf("Failed to open tunnel: %#v", err) } } host := fmt.Sprintf("tcp://localhost:%d", localPort) client := newClient() client.setURL(host) client.version = apiVersion //job.Eng.Register("inspect", func(job *engine.Job) engine.Status { // resp, err := client.call("GET", "/containers/ job.Eng.Register("create", func(job *engine.Job) engine.Status { container := Container{ Image: job.Getenv("Image"), Tty: job.Getenv("Tty") == "true", } data, err := json.Marshal(container) resp, err := client.call("POST", "/containers/create", string(data)) if err != nil { return job.Errorf("post: %v", err) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return job.Errorf("read body: %#v", err) } var containerOut Container err = json.Unmarshal([]byte(body), &containerOut) _, err = job.Printf("%s\n", containerOut.Id) if err != nil { return job.Errorf("write body: %#v", err) } log.Printf("%s", string(body)) return engine.StatusOK }) job.Eng.Register("start", func(job *engine.Job) engine.Status { path := fmt.Sprintf("/containers/%s/start", job.Args[0]) resp, err := client.call("POST", path, "{\"Binds\":[],\"ContainerIDFile\":\"\",\"LxcConf\":[],\"Privileged\":false,\"PortBindings\":{},\"Links\":null,\"PublishAllPorts\":false,\"Dns\":null,\"DnsSearch\":[],\"VolumesFrom\":[]}") if err != nil { return job.Errorf("post: %v", err) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return job.Errorf("read body: %#v", err) } log.Printf("%s", string(body)) return engine.StatusOK }) job.Eng.Register("containers", func(job *engine.Job) engine.Status { path := fmt.Sprintf( "/containers/json?all=%s&size=%s&since=%s&before=%s&limit=%s", url.QueryEscape(job.Getenv("all")), url.QueryEscape(job.Getenv("size")), url.QueryEscape(job.Getenv("since")), url.QueryEscape(job.Getenv("before")), url.QueryEscape(job.Getenv("limit")), ) resp, err := client.call("GET", path, "") if err != nil { return job.Errorf("get: %v", err) } // FIXME: check for response error c := engine.NewTable("Created", 0) body, err := ioutil.ReadAll(resp.Body) if err != nil { return job.Errorf("read body: %v", err) } fmt.Printf("---> '%s'\n", body) if _, err := c.ReadListFrom(body); err != nil { return job.Errorf("readlist: %v", err) } c.WriteListTo(job.Stdout) return engine.StatusOK }) job.Eng.Register("container_delete", func(job *engine.Job) engine.Status { log.Printf("%#v", job.Args) path := "/containers/" + job.Args[0] resp, err := client.call("DELETE", path, "") if err != nil { return job.Errorf("delete: %v", err) } log.Printf("%#v", resp) return engine.StatusOK }) job.Eng.Register("stop", func(job *engine.Job) engine.Status { log.Printf("%#v", job.Args) path := "/containers/" + job.Args[0] + "/stop" resp, err := client.call("POST", path, "") if err != nil { return job.Errorf("delete: %v", err) } log.Printf("%#v", resp) return engine.StatusOK }) job.Eng.RegisterCatchall(func(job *engine.Job) engine.Status { log.Printf("%#v %#v %#v", *job, job.Env(), job.Args) return engine.StatusOK }) return engine.StatusOK }) return nil }
func (t *tutumBackend) Install(eng *engine.Engine) error { eng.Register("tutum", func(job *engine.Job) engine.Status { if len(job.Args) == 1 { tutumConnector = job.Args[0] } if !tutum.IsAuthenticated() { return job.Errorf("You need to provide your Tutum credentials in ~/.tutum or environment variables TUTUM_USER and TUTUM_APIKEY") } job.Eng.Register("containers", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/json?all=%s&limit=%s", url.QueryEscape(job.Getenv("all")), url.QueryEscape(job.Getenv("limit")), ) resp, err := tutumConnectorCall("GET", path, "") if err != nil { return job.Errorf("%s: get: %v", path, err) } c := engine.NewTable("Created", 0) body, err := ioutil.ReadAll(resp.Body) if err != nil { return job.Errorf("%s: read body: %v", path, err) } if _, err := c.ReadListFrom(body); err != nil { return job.Errorf("%s: readlist: %v", path, err) } c.WriteListTo(job.Stdout) return engine.StatusOK }) job.Eng.Register("create", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/create", ) config := runconfig.ContainerConfigFromJob(job) data, err := json.Marshal(config) if err != nil { return job.Errorf("%s: json marshal: %v", path, err) } resp, err := tutumConnectorCall("POST", path, string(data)) if err != nil { return job.Errorf("%s: post: %v", path, err) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return job.Errorf("%s: read body: %#v", path, err) } var containerOut struct { Id string Warnings []string } err = json.Unmarshal([]byte(body), &containerOut) _, err = job.Printf("%s\n", containerOut.Id) if err != nil { return job.Errorf("%s: write body: %#v", path, err) } log.Printf("%s", string(body)) return engine.StatusOK }) job.Eng.Register("container_delete", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s?force=%s", job.Args[0], url.QueryEscape(job.Getenv("forceRemove")), ) _, err := tutumConnectorCall("DELETE", path, "") if err != nil { return job.Errorf("%s: delete: %v", path, err) } return engine.StatusOK }) job.Eng.Register("start", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf("/containers/%s/start", job.Args[0]) config := runconfig.ContainerConfigFromJob(job) data, err := json.Marshal(config) if err != nil { return job.Errorf("%s: json marshal: %v", path, err) } _, err = tutumConnectorCall("POST", path, string(data)) if err != nil { return job.Errorf("%s: post: %v", path, err) } return engine.StatusOK }) job.Eng.Register("stop", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s/stop?t=%s", job.Args[0], url.QueryEscape(job.Getenv("t")), ) _, err := tutumConnectorCall("POST", path, "") if err != nil { return job.Errorf("%s: post: %v", path, err) } return engine.StatusOK }) job.Eng.Register("kill", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s/kill?signal=%s", job.Args[0], job.Args[1], ) _, err := tutumConnectorCall("POST", path, "") if err != nil { return job.Errorf("%s: post: %v", path, err) } return engine.StatusOK }) job.Eng.Register("restart", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s/restart?t=%s", job.Args[0], url.QueryEscape(job.Getenv("t")), ) _, err := tutumConnectorCall("POST", path, "") if err != nil { return job.Errorf("%s: post: %v", path, err) } return engine.StatusOK }) job.Eng.Register("inspect", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s/json", job.Args[0], ) resp, err := tutumConnectorCall("GET", path, "") if err != nil { return job.Errorf("%s: get: %v", path, err) } _, err = io.Copy(job.Stdout, resp.Body) if err != nil { return job.Errorf("%s: copy stream: %v", path, err) } return engine.StatusOK }) job.Eng.Register("logs", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := fmt.Sprintf( "/containers/%s/logs?stdout=%s&stderr=%s", job.Args[0], url.QueryEscape(job.Getenv("stdout")), url.QueryEscape(job.Getenv("stderr")), ) resp, err := tutumConnectorCall("GET", path, "") if err != nil { return job.Errorf("%s: get: %v", path, err) } _, err = io.Copy(job.Stdout, resp.Body) if err != nil { return job.Errorf("%s: copy stream: %v", path, err) } return engine.StatusOK }) job.Eng.Register("version", func(job *engine.Job) engine.Status { log.Printf("Received '%s' operation....", job.Name) path := "/version" resp, err := tutumConnectorCall("GET", path, "") if err != nil { return job.Errorf("%s: get: %v", path, err) } _, err = io.Copy(job.Stdout, resp.Body) if err != nil { return job.Errorf("%s: copy stream: %v", path, err) } return engine.StatusOK }) job.Eng.RegisterCatchall(func(job *engine.Job) engine.Status { return job.Errorf("Operation not yet supported: %s", job.Name) }) return engine.StatusOK }) return nil }
func TestGetImagesJSON(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) job := eng.Job("images") initialImages, err := job.Stdout.AddListTable() if err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } req, err := http.NewRequest("GET", "/images/json?all=0", nil) if err != nil { t.Fatal(err) } r := httptest.NewRecorder() if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) images := engine.NewTable("Created", 0) if _, err := images.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } if images.Len() != initialImages.Len() { t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len()) } found := false for _, img := range images.Data { if strings.Contains(img.GetList("RepoTags")[0], unitTestImageName) { found = true break } } if !found { t.Errorf("Expected image %s, %+v found", unitTestImageName, images) } r2 := httptest.NewRecorder() // all=1 initialImages = getAllImages(eng, t) req2, err := http.NewRequest("GET", "/images/json?all=true", nil) if err != nil { t.Fatal(err) } if err := docker.ServeRequest(srv, docker.APIVERSION, r2, req2); err != nil { t.Fatal(err) } assertHttpNotError(r2, t) images2 := engine.NewTable("ID", 0) if _, err := images2.ReadListFrom(r2.Body.Bytes()); err != nil { t.Fatal(err) } if images2.Len() != initialImages.Len() { t.Errorf("Expected %d image, %d found", initialImages.Len(), images2.Len()) } found = false for _, img := range images2.Data { if img.Get("ID") == unitTestImageID { found = true break } } if !found { t.Errorf("Retrieved image Id differs, expected %s, received %+v", unitTestImageID, images2) } r3 := httptest.NewRecorder() // filter=a req3, err := http.NewRequest("GET", "/images/json?filter=aaaaaaaaaa", nil) if err != nil { t.Fatal(err) } if err := docker.ServeRequest(srv, docker.APIVERSION, r3, req3); err != nil { t.Fatal(err) } assertHttpNotError(r3, t) images3 := engine.NewTable("ID", 0) if _, err := images3.ReadListFrom(r3.Body.Bytes()); err != nil { t.Fatal(err) } if images3.Len() != 0 { t.Errorf("Expected 0 image, %d found", images3.Len()) } }
func (rax *RaxCloud) run(ctx *cli.Context, eng *engine.Engine) (err error) { if err = rax.configure(ctx); err == nil { if _, name, err := rax.findTargetHost(); err == nil { if name == "" { rax.createHost(DASS_TARGET_PREFIX + RandomString()[:12]) } } else { return err } eng.Register("create", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := "/containers/create" config := runconfig.ContainerConfigFromJob(job) if data, err := json.Marshal(config); err != nil { return job.Errorf("marshaling failure : %v", err) } else if resp, err := client.Post(path, string(data)); err != nil { return job.Error(err) } else { var container struct { Id string Warnings []string } if body, err := ioutil.ReadAll(resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } else if err := json.Unmarshal([]byte(body), &container); err != nil { return job.Errorf("Failed to read container info from body: %v", err) } else { job.Printf("%s\n", container.Id) } } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("container_delete", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s?force=%s", job.Args[0], url.QueryEscape(job.Getenv("forceRemove"))) if _, err := client.Delete(path, ""); err != nil { return job.Error(err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("containers", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/json?all=%s&limit=%s", url.QueryEscape(job.Getenv("all")), url.QueryEscape(job.Getenv("limit"))) if resp, err := client.Get(path, ""); err != nil { return job.Error(err) } else { created := engine.NewTable("Created", 0) if body, err := ioutil.ReadAll(resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } else if created.ReadListFrom(body); err != nil { return job.Errorf("Failed to read list from body: %v", err) } else { created.WriteListTo(job.Stdout) } } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("version", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := "/version" if resp, err := client.Get(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } else if _, err := io.Copy(job.Stdout, resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("start", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/start", job.Args[0]) config := runconfig.ContainerConfigFromJob(job) if data, err := json.Marshal(config); err != nil { return job.Errorf("marshaling failure : %v", err) } else if _, err := client.Post(path, string(data)); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("stop", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/stop?t=%s", job.Args[0], url.QueryEscape(job.Getenv("t"))) if _, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("kill", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/kill?signal=%s", job.Args[0], job.Args[1]) if _, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("restart", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/restart?t=%s", url.QueryEscape(job.Getenv("t"))) if _, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("inspect", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/json", job.Args[0]) if resp, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } else if _, err := io.Copy(job.Stdout, resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("attach", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/attach?stream=%s&stdout=%s&stderr=%s", job.Args[0], url.QueryEscape(job.Getenv("stream")), url.QueryEscape(job.Getenv("stdout")), url.QueryEscape(job.Getenv("stderr"))) if resp, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } else if _, err := io.Copy(job.Stdout, resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("pull", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/images/create?fromImage=%s&tag=%s", job.Args[0], url.QueryEscape(job.Getenv("tag"))) if resp, err := client.Post(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } else if _, err := io.Copy(job.Stdout, resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.Register("logs", func(job *engine.Job) (status engine.Status) { if ctx, err := rax.getHostContext(); err == nil { defer ctx.Close() return ctx.exec(job, func(client HttpClient) (status engine.Status) { path := fmt.Sprintf("/containers/%s/logs?stdout=%s&stderr=%s", job.Args[0], url.QueryEscape(job.Getenv("stdout")), url.QueryEscape(job.Getenv("stderr"))) if resp, err := client.Get(path, ""); err != nil { return job.Errorf("Failed call %s(path:%s): %v", job.Name, path, err) } else if _, err := io.Copy(job.Stdout, resp.Body); err != nil { return job.Errorf("Failed to copy response body: %v", err) } return engine.StatusOK }) } else { return job.Errorf("Failed to create host context: %v", err) } }) eng.RegisterCatchall(func(job *engine.Job) engine.Status { log.Printf("[UNIMPLEMENTED] %s %#v %#v %#v", job.Name, *job, job.Env(), job.Args) return engine.StatusOK }) } return err }
func TestRmi(t *testing.T) { eng := NewTestEngine(t) srv := mkServerFromEngine(eng, t) defer mkRuntimeFromEngine(eng, t).Nuke() initialImages := getAllImages(eng, t) config, hostConfig, _, err := runconfig.Parse([]string{unitTestImageID, "echo", "test"}, nil) if err != nil { t.Fatal(err) } containerID := createTestContainer(eng, config, t) //To remove job := eng.Job("start", containerID) if err := job.ImportEnv(hostConfig); err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } if err := eng.Job("wait", containerID).Run(); err != nil { t.Fatal(err) } job = eng.Job("commit", containerID) job.Setenv("repo", "test") var imageID string job.Stdout.AddString(&imageID) if err := job.Run(); err != nil { t.Fatal(err) } if err := eng.Job("tag", imageID, "test", "0.1").Run(); err != nil { t.Fatal(err) } containerID = createTestContainer(eng, config, t) //To remove job = eng.Job("start", containerID) if err := job.ImportEnv(hostConfig); err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } if err := eng.Job("wait", containerID).Run(); err != nil { t.Fatal(err) } job = eng.Job("commit", containerID) job.Setenv("repo", "test") if err := job.Run(); err != nil { t.Fatal(err) } images := getAllImages(eng, t) if images.Len()-initialImages.Len() != 2 { t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len()) } if err = srv.DeleteImage(imageID, engine.NewTable("", 0), true, false, false); err != nil { t.Fatal(err) } images = getAllImages(eng, t) if images.Len()-initialImages.Len() != 1 { t.Fatalf("Expected 1 new image, found %d.", images.Len()-initialImages.Len()) } for _, image := range images.Data { if strings.Contains(unitTestImageID, image.Get("Id")) { continue } if image.GetList("RepoTags")[0] == "<none>:<none>" { t.Fatalf("Expected tagged image, got untagged one.") } } }
func TestContainerOrphaning(t *testing.T) { // setup a temporary directory tmpDir, err := ioutil.TempDir("", "project") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // setup a CLI and server cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr) defer cleanup(globalEngine, t) srv := mkServerFromEngine(globalEngine, t) // closure to build something buildSomething := func(template string, image string) string { dockerfile := path.Join(tmpDir, "Dockerfile") replacer := strings.NewReplacer("{IMAGE}", unitTestImageID) contents := replacer.Replace(template) ioutil.WriteFile(dockerfile, []byte(contents), 0x777) if err := cli.CmdBuild("-t", image, tmpDir); err != nil { t.Fatal(err) } img, err := srv.ImageInspect(image) if err != nil { t.Fatal(err) } return img.ID } // build an image imageName := "orphan-test" template1 := ` from {IMAGE} cmd ["/bin/echo", "holla"] ` img1 := buildSomething(template1, imageName) // create a container using the fist image if err := cli.CmdRun(imageName); err != nil { t.Fatal(err) } // build a new image that splits lineage template2 := ` from {IMAGE} cmd ["/bin/echo", "holla"] expose 22 ` buildSomething(template2, imageName) // remove the second image by name resp := engine.NewTable("", 0) if err := srv.DeleteImage(imageName, resp, true, false, false); err == nil { t.Fatal("Expected error, got none") } // see if we deleted the first image (and orphaned the container) for _, i := range resp.Data { if img1 == i.Get("Deleted") { t.Fatal("Orphaned image with container") } } }