package main import ( "fmt" "log" "net/http" "net/http/httputil" "net/url" "os" ) type AutoRegistry struct { host string port string backendRegistryUrl string reverseProxy *httputil.ReverseProxy } func NewAutoRegistry(host, port, backendRegistryUrl string) *AutoRegistry { proxy := AutoRegistry{ host: host, port: port, backendRegistryUrl: backendRegistryUrl, } url, err := url.Parse(backendRegistryUrl) if err != nil { panic(err) } proxy.reverseProxy = httputil.NewSingleHostReverseProxy(url) return &proxy } func (autoProxy *AutoRegistry) setupHandler() { http.HandleFunc("/", autoProxy.requestHandler()) } // ProxyRequestHandler handles the http request using proxy func (autoProxy *AutoRegistry) requestHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { autoProxy.pushImageIfNeeded(r, autoProxy.host+":"+autoProxy.port) autoProxy.reverseProxy.ServeHTTP(w, r) } } func (autoProxy *AutoRegistry) pushImageIfNeeded(r *http.Request, registry string) { // below will catch error but can return old image to the user. // This will lead to a lot of troubleshooting. Better is to fail the request to the client // when there is a problem with docker. //defer func() { // if r := recover(); r != nil { // log.Println("ERROR: Docker interaction failed: ", r) // } //}() log.Printf("Got request: %s, url: %s\n", r.Method, r.URL.Path) if r.Method != "HEAD" && r.Method != "GET" { return } image_name, tag_name, ok := ParseImage(r.URL.Path) if !ok { // not a manifest request return } docker_id, push_tag := GetDockerImageId(registry, image_name, tag_name) log.Printf("Docker id %s\n", docker_id) if docker_id == "" { return } registry_id := GetRegistryId(autoProxy.backendRegistryUrl, image_name, tag_name) if docker_id == registry_id { log.Printf("Image %s:%s is up to date in registry\n", image_name, tag_name) } log.Printf("image %s:%s is not up to date in registry, pushing it\n", image_name, tag_name) PushDockerImage(push_tag) } func printHelp() { fmt.Println("Usage: proxy ") } func main() { // make sure we can connect to docker and fail immediately otherwise CheckDockerAvailable() if len(os.Args) != 4 { printHelp() } host := os.Args[1] port := os.Args[2] backendRegistry := os.Args[3] log.Printf("Running with: host %s port %s backend %s\n", host, port, backendRegistry) proxy := NewAutoRegistry(host, port, backendRegistry) proxy.setupHandler() // handle all requests to your server using the proxy log.Fatal(http.ListenAndServe(":"+port, nil)) }