diff --git a/main.go b/main.go index 2a16e70..30e7124 100644 --- a/main.go +++ b/main.go @@ -33,21 +33,22 @@ func main() { mux.HandleFunc("/docker-images/", srv.image) mux.HandleFunc("/docker-update", srv.update) - go srv.fetchImages() - - log.Printf("ji-marina listening on %q...\n", srv.addr) - err := http.ListenAndServe(srv.addr, mux) + err := srv.fetchStdlibImages() if err != nil { log.Fatal(err) } + + go srv.fetchImages() + + log.Printf("ji-marina listening on %q...\n", srv.addr) + log.Fatal(http.ListenAndServe(srv.addr, mux)) } type server struct { addr string - mu sync.RWMutex - images []string - cli *client.Client + mu sync.RWMutex + cli *client.Client } func newServer(addr string) *server { @@ -64,13 +65,12 @@ func newServer(addr string) *server { } func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "

Welcome to the Piscine

\n") - + fmt.Fprintf(w, "

Welcome to the Marina

\n") srv.list(w) } func (srv *server) fetchImages() { - ticker := time.NewTicker(5 * time.Minute) + ticker := time.NewTicker(1 * time.Hour) defer ticker.Stop() err := srv.pull() @@ -90,50 +90,90 @@ func (srv *server) fetchImages() { } func (srv *server) pull() error { - srv.mu.Lock() - defer srv.mu.Unlock() - - ctx := context.Background() - - opts := types.ImageSearchOptions{Limit: 100} - imgs, err := srv.cli.ImageSearch(ctx, imagePrefix+"/*", opts) + imgs, err := srv.fetchRI3ImageList() if err != nil { return err } start := time.Now() - set := make(map[string]int, len(imgs)) for _, img := range imgs { - imgStart := time.Now() - log.Printf("pulling %v...\n", img.Name) - opts := types.ImagePullOptions{All: true} - r, err := srv.cli.ImagePull(ctx, img.Name, opts) + err := srv.pullImage(img) if err != nil { - log.Printf("image-pull error: %v\n", err) return err } - defer r.Close() + } + log.Printf("pulled %d images in %v\n", len(imgs), time.Since(start)) + return nil +} - const quiet = false - load, err := srv.cli.ImageLoad(ctx, r, quiet) +func (srv *server) fetchStdlibImages() error { + stdlib := []string{ + "alpine:latest", + "busybox:latest", + "debian:latest", + "centos:latest", + "fedora:latest", + "golang:latest", + "python:latest", + "ubuntu:latest", + } + + start := time.Now() + log.Printf("pulling stdlib images...\n") + for _, name := range stdlib { + err := srv.pullImage(name) if err != nil { - log.Printf("image-load error: %v\n", err) return err } - defer load.Body.Close() - log.Printf("pulling %v... [done] (%v)\n", img.Name, time.Since(imgStart)) - set[img.Name] = 1 } - for _, img := range srv.images { - set[img] = 1 + log.Printf("pulling stdlib images... [done] (%v)\n", time.Since(start)) + return nil +} + +func (srv *server) fetchRI3ImageList() ([]string, error) { + srv.mu.Lock() + defer srv.mu.Unlock() + + ctx := context.Background() + opts := types.ImageSearchOptions{Limit: 100} + imgs, err := srv.cli.ImageSearch(ctx, imagePrefix+"/*", opts) + if err != nil { + return nil, err + } + + images := make([]string, len(imgs)) + for i, img := range imgs { + images[i] = img.Name + } + return images, nil +} + +func (srv *server) pullImage(name string) error { + srv.mu.Lock() + defer srv.mu.Unlock() + + ctx := context.Background() + start := time.Now() + log.Printf("pulling %v...\n", name) + opts := types.ImagePullOptions{} + if strings.HasPrefix(name, imagePrefix+"/") { + opts.All = true + } + r, err := srv.cli.ImagePull(ctx, name, opts) + if err != nil { + log.Printf("image-pull error %q: %v\n", name, err) + return err } - images := make([]string, 0, len(set)) - for img := range set { - images = append(images, img) + defer r.Close() + + const quiet = false + load, err := srv.cli.ImageLoad(ctx, r, quiet) + if err != nil { + log.Printf("image-load error %q: %v\n", name, err) + return err } - sort.Strings(images) - srv.images = images - log.Printf("pulled %d images in %v\n", len(images), time.Since(start)) + defer load.Body.Close() + log.Printf("pulling %v... [done] (%v)\n", name, time.Since(start)) return nil } @@ -202,14 +242,67 @@ func (srv *server) update(w http.ResponseWriter, r *http.Request) { } func (srv *server) list(w io.Writer) { + + images, err := srv.listImages() + + fmt.Fprintf(w, "

Docker images (total=%d)

\n", len(images)) + if err != nil { + log.Printf("

Error retrieving image list:

%v

\n", err) + return + } + fmt.Fprintf(w, "
\n")
+	for _, img := range images {
+		if strings.HasPrefix(img.Name, imagePrefix+"/") {
+			fmt.Fprintf(w, " %-12s   %-50s (%8.3f MB)\n", img.ID[:12], img.Name, float64(img.Size)/1024/1024)
+		}
+	}
+	fmt.Fprintf(w, "\n")
+	for _, img := range images {
+		if !strings.HasPrefix(img.Name, imagePrefix+"/") {
+			fmt.Fprintf(w, " %-12s   %-50s (%8.3f MB)\n", img.ID[:12], img.Name, float64(img.Size)/1024/1024)
+		}
+	}
+	fmt.Fprintf(w, "
\n") +} + +func (srv *server) listImages() ([]dkrImage, error) { srv.mu.RLock() defer srv.mu.RUnlock() - fmt.Fprintf(w, "

Docker images (total=%d)

\n", len(srv.images)) - fmt.Fprintf(w, "\n") + + var images []dkrImage + for _, img := range imgs { + id := img.ID[strings.Index(img.ID, ":")+1:] + for _, tag := range img.RepoTags { + if tag == ":" || tag == "" { + continue + } + images = append(images, dkrImage{ + Name: tag, + ID: id, + Size: img.VirtualSize, + }) + } + } + + sort.Sort(dkrImages(images)) + return images, nil } + +type dkrImage struct { + Name string + ID string + Size int64 +} + +type dkrImages []dkrImage + +func (p dkrImages) Len() int { return len(p) } +func (p dkrImages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p dkrImages) Less(i, j int) bool { return p[i].Name < p[j].Name }