1
0
Fork 0
mirror of https://github.com/imjasonh/kontain.me synced 2026-07-08 09:04:54 +00:00
kontain.me/cmd/random/main.go
2021-02-05 12:06:36 -05:00

116 lines
3.2 KiB
Go

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/imjasonh/kontain.me/pkg/serve"
)
func main() {
ctx := context.Background()
st, err := serve.NewStorage(ctx)
if err != nil {
log.Fatalf("serve.NewStorage: %v", err)
}
http.Handle("/v2/", &server{
info: log.New(os.Stdout, "I ", log.Ldate|log.Ltime|log.Lshortfile),
error: log.New(os.Stderr, "E ", log.Ldate|log.Ltime|log.Lshortfile),
storage: st,
})
http.Handle("/", http.RedirectHandler("https://github.com/imjasonh/kontain.me/blob/main/cmd/random", http.StatusSeeOther))
log.Println("Starting...")
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
log.Printf("Listening on port %s", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
type server struct {
info, error *log.Logger
storage *serve.Storage
}
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.info.Println("handler:", r.Method, r.URL)
path := strings.TrimPrefix(r.URL.String(), "/v2/")
switch {
case path == "":
// API Version check.
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
return
case strings.Contains(path, "/blobs/"),
strings.Contains(path, "/manifests/sha256:"):
// Extract requested blob digest and redirect to serve it from GCS.
// If it doesn't exist, this will return 404.
parts := strings.Split(r.URL.Path, "/")
digest := parts[len(parts)-1]
serve.Blob(w, r, digest)
case strings.Contains(path, "/manifests/"):
s.serveRandomManifest(w, r)
default:
serve.Error(w, serve.ErrNotFound)
}
}
// Capture up to 99 layers of up to 99.9MB each.
var randomTagRE = regexp.MustCompile("([0-9]{1,2})x([0-9]{1,8})")
// random.kontain.me:3x10mb
// random.kontain.me(:latest) -> 1x10mb
func (s *server) serveRandomManifest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tagOrDigest := strings.TrimPrefix(r.URL.Path, "/v2/manifests/")
var num, size int64 = 1, 10000000 // 10MB
// If request is for image by digest, try to serve it from GCS.
if strings.HasPrefix(tagOrDigest, "sha256:") {
desc, err := s.storage.BlobExists(ctx, tagOrDigest)
if err != nil {
s.error.Printf("ERROR (storage.BlobExists): %s", err)
serve.Error(w, serve.ErrNotFound)
return
}
if r.Method == http.MethodHead {
w.Header().Set("Docker-Content-Digest", tagOrDigest)
w.Header().Set("Content-Type", string(desc.MediaType))
w.Header().Set("Content-Length", fmt.Sprintf("%d", desc.Size))
return
}
serve.Blob(w, r, tagOrDigest)
return
}
// Captured requested num + size from tag.
all := randomTagRE.FindStringSubmatch(tagOrDigest)
if len(all) >= 3 {
num, _ = strconv.ParseInt(all[1], 10, 64)
size, _ = strconv.ParseInt(all[2], 10, 64)
}
s.info.Printf("generating random image with %d layers of %d bytes", num, size)
// Generate a random image.
img, err := random.Image(size, num)
if err != nil {
s.error.Printf("ERROR (random.Image): %s", err)
serve.Error(w, err)
return
}
if err := s.storage.ServeManifest(w, r, img); err != nil {
s.error.Printf("ERROR (storage.ServeManifest): %v", err)
serve.Error(w, err)
return
}
}