package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "net/http" "os/exec" "strconv" "strings" "time" "github.com/go-redis/redis" ) var ( cache = map[string]string{} target string client = &http.Client{} rc *redis.Client ) const ( docker = "docker" ) func main() { b := flag.String("b", ":8080", "Server Listen Port") t := flag.String("t", "registry.cn-hangzhou.aliyuncs.com/miaowoo", "Target Repo") u := flag.String("u", "", "Target Repo UserName") p := flag.String("p", "", "Target Repo Password") r := flag.String("r", "127.0.0.1:6379", "Redis Server Host") flag.Parse() target = *t if _, err := exec.LookPath(docker); err != nil { fmt.Printf("Error Can't Find %s Client...", docker) return } loginResult, err := exec.Command(docker, "login", *t, "-u", *u, "-p", *p).Output() if err != nil { fmt.Printf("Error Can't Login To %s Username %s Password %s ...\nError: %s", *t, *u, *p, err.Error()) return } fmt.Printf(string(loginResult)) rc = redis.NewClient(&redis.Options{ Addr: *r, }) _, err = rc.Ping().Result() if err != nil { fmt.Printf("Redis Connect Error: %s\n", err.Error()) return } fmt.Printf("Redis Server %s Connect Success\n", *r) startServer(*b) } func startServer(b string) { http.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) { if strings.HasPrefix(req.RequestURI, "/api") { handleAPI(resp, req) return } handleNormal(resp, req) }) fmt.Println("Start Server Listen On: " + b) http.ListenAndServe(b, nil) } func handleAPI(w http.ResponseWriter, r *http.Request) { fmt.Println("[A] " + r.RequestURI) } func getToken(image string) string { resp, _ := http.Get("https://dockerauth.cn-hangzhou.aliyuncs.com/auth?service=registry.aliyuncs.com:cn-hangzhou:26842&scope=repository:miaowoo/" + image + ":pull") var result []byte result, _ = ioutil.ReadAll(resp.Body) var auth map[string]string json.Unmarshal(result, &auth) return auth["token"] } func checkMirror(image string) bool { i := image v := "latest" if strings.Contains(image, ":") { arr := strings.Split(image, ":") i = arr[0] v = arr[1] } url := fmt.Sprintf("https://registry.cn-hangzhou.aliyuncs.com/v2/miaowoo/%s/manifests/%s", i, v) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+getToken(i)) resp, _ := client.Do(req) fmt.Println("[C] " + strconv.Itoa(resp.StatusCode)) return resp.StatusCode == 200 } func handleNormal(resp http.ResponseWriter, req *http.Request) { fmt.Println("[N] " + req.RequestURI) origin := subString(req.RequestURI, 1, len(req.RequestURI)) if origin == "" || strings.Contains(origin, "?") || strings.Contains(origin, "=") || strings.Contains(req.RequestURI, ".ico") || strings.Contains(req.RequestURI, ".txt") || strings.Contains(origin, target) { resp.Write([]byte("Noop...")) return } if _, ok := cache[origin]; ok { resp.Write([]byte("Image " + origin + " Is Syncing... \nProcess " + string(cache[origin]) + "... \nPlease Wait...")) return } name := strings.Replace(origin, "/", "_", -1) image := target + "/" + name out := `` out += `

Image ` + origin + ` Is Tag to ` + image + ` And Push Success

` if checkMirrorFromRedis(name) || checkMirror(name) { out += "Alread Transfer" } else { cache[origin] = "0" out += `

Transfer Log

` + transferImage(origin, image) + `
` delete(cache, origin) } rc.Set(name, time.Now().Unix(), 0) out += `

You Can Use below Command To Pull...

docker pull ` + image + `
docker tag ` + image + ` ` + origin + `
docker rmi ` + image + `
` out += "" resp.Write([]byte(out)) } func checkMirrorFromRedis(image string) bool { _, err := rc.Get(image).Result() return err != redis.Nil } func transferImage(origin string, image string) string { out := "" cache[origin] = "10" out += runCommand(docker, "pull", origin) cache[origin] = "30" out += runCommand(docker, "tag", origin, image) cache[origin] = "50" out += runCommand(docker, "push", image) cache[origin] = "70" out += runCommand(docker, "rmi", origin) cache[origin] = "90" out += runCommand(docker, "rmi", image) cache[origin] = "100" return out } func runCommand(command string, args ...string) string { result, err := exec.Command(command, args...).Output() out := string(result) if err != nil { out = out + err.Error() } return out } func subString(str string, begin, length int) (substr string) { // 将字符串的转换成[]rune rs := []rune(str) lth := len(rs) // 简单的越界判断 if begin < 0 { begin = 0 } if begin >= lth { begin = lth } end := begin + length if end > lth { end = lth } // 返回子串 return string(rs[begin:end]) }