198 lines
4.7 KiB
Go
198 lines
4.7 KiB
Go
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 := `<html><body>`
|
|
out += `<h3>Image ` + origin + ` Is Tag to ` + image + ` And Push Success</h3>`
|
|
|
|
if checkMirrorFromRedis(name) || checkMirror(name) {
|
|
out += "Alread Transfer"
|
|
} else {
|
|
cache[origin] = "0"
|
|
out += `<h4>Transfer Log</h4>
|
|
<pre>
|
|
` + transferImage(origin, image) + `
|
|
</pre>`
|
|
delete(cache, origin)
|
|
}
|
|
rc.Set(name, time.Now().Unix(), 0)
|
|
out += `<h3>You Can Use below Command To Pull...</h3>
|
|
<pre>
|
|
docker pull ` + image + `
|
|
docker tag ` + image + ` ` + origin + `
|
|
docker rmi ` + image + `
|
|
</pre>`
|
|
out += "</body></html>"
|
|
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])
|
|
}
|