From e6f52dc89bb7d58a9dbaa02b21100bf702967fc3 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sun, 23 May 2021 21:23:36 +0200 Subject: [PATCH 01/13] feat: support symbolic link with relative asset loading --- src/etcdkeeper/main.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index d89b0f8..82a8b95 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -84,7 +84,22 @@ func main() { if err != nil { log.Fatal(err) } - rootPath := filepath.Dir(wd) + exStat, err := os.Lstat(wd) + if err != nil { + log.Fatal(err) + } + + readWd := wd + // check if executable path is symlink + if exStat.Mode()&os.ModeSymlink != 0 { + // if symlink evaluate it to get real path + readWd, err = filepath.EvalSymlinks(wd) + if err != nil { + log.Printf("failed evaluating executable symlink path (%s).\netcdkeeper will use symlink for loading asset", wd) + } + } + + rootPath := filepath.Dir(readWd) // Session management sessmgr, err = session.NewManager("memory", "_etcdkeeper_session", 86400) From 09737cf98733fa744a1a9d3883e324b768993855 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sat, 21 Aug 2021 16:03:01 +0200 Subject: [PATCH 02/13] refactor: apply go fmt on code base --- src/etcdkeeper/main.go | 113 ++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index 82a8b95..86deded 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -8,9 +8,6 @@ import ( _ "etcdkeeper/session/providers/memory" "flag" "fmt" - "github.com/coreos/etcd/client" - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/pkg/transport" "io" "log" "net/http" @@ -21,6 +18,10 @@ import ( "strings" "sync" "time" + + "github.com/coreos/etcd/client" + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/pkg/transport" "google.golang.org/grpc" ) @@ -36,8 +37,8 @@ var ( rootUsers = make(map[string]*userInfo) // host:rootUser rootUesrsV2 = make(map[string]*userInfo) // host:rootUser - sessmgr *session.Manager - mu sync.Mutex + sessmgr *session.Manager + mu sync.Mutex ) type userInfo struct { @@ -47,7 +48,7 @@ type userInfo struct { } func main() { - host := flag.String("h","0.0.0.0","host name or ip address") + host := flag.String("h", "0.0.0.0", "host name or ip address") port := flag.Int("p", 8080, "port") flag.CommandLine.Parse(os.Args[1:]) @@ -114,7 +115,7 @@ func main() { http.Handle("/", http.FileServer(http.Dir(rootPath + "/assets"))) // view static directory log.Printf("listening on %s:%d\n", *host, *port) - err = http.ListenAndServe(*host + ":" + strconv.Itoa(*port), nil) + err = http.ListenAndServe(*host+":"+strconv.Itoa(*port), nil) if err != nil { log.Fatal(err) } @@ -124,7 +125,6 @@ func nothing(_ http.ResponseWriter, _ *http.Request) { // Nothing } - //func v2request(w http.ResponseWriter, r *http.Request){ // if err := r.ParseForm(); err != nil { // log.Println(err.Error()) @@ -167,12 +167,12 @@ func connectV2(w http.ResponseWriter, r *http.Request) { if *useAuth { _, ok := rootUesrsV2[host] if !ok && uname != "root" { - b, _ := json.Marshal(map[string]interface{}{"status":"root"}) + b, _ := json.Marshal(map[string]interface{}{"status": "root"}) io.WriteString(w, string(b)) return } if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status":"login"}) + b, _ := json.Marshal(map[string]interface{}{"status": "login"}) io.WriteString(w, string(b)) return } @@ -181,17 +181,17 @@ func connectV2(w http.ResponseWriter, r *http.Request) { if uinfo, ok := sess.Get("uinfov2").(*userInfo); ok { if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) io.WriteString(w, string(b)) return } } - uinfo := &userInfo{host:host, uname:uname, passwd:passwd} + uinfo := &userInfo{host: host, uname: uname, passwd: passwd} _, err := newClientV2(uinfo) if err != nil { log.Println(r.Method, "v2", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status":"error", "message":err.Error()}) + b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) io.WriteString(w, string(b)) return } @@ -206,7 +206,7 @@ func connectV2(w http.ResponseWriter, r *http.Request) { } log.Println(r.Method, "v2", "connect success.") info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) io.WriteString(w, string(b)) } @@ -231,15 +231,15 @@ func putV2(w http.ResponseWriter, r *http.Request) { if err != nil { log.Println(err.Error()) } - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{TTL:time.Duration(sec)*time.Second, Dir:isDir}) + _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{TTL: time.Duration(sec) * time.Second, Dir: isDir}) } else { - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{Dir:isDir}) + _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{Dir: isDir}) } if err != nil { data["errorCode"] = 500 data["message"] = err.Error() } else { - if resp, err := kapi.Get(context.Background(), key, &client.GetOptions{Recursive:true, Sort:true}); err != nil { + if resp, err := kapi.Get(context.Background(), key, &client.GetOptions{Recursive: true, Sort: true}); err != nil { data["errorCode"] = err.Error() } else { if resp.Node != nil { @@ -256,7 +256,7 @@ func putV2(w http.ResponseWriter, r *http.Request) { } var dataByte []byte - if dataByte, err = json.Marshal(data);err != nil { + if dataByte, err = json.Marshal(data); err != nil { io.WriteString(w, err.Error()) } else { io.WriteString(w, string(dataByte)) @@ -300,7 +300,7 @@ func getV2(w http.ResponseWriter, r *http.Request) { max = min all := make(map[int][]map[string]interface{}) if key == separator { - all[min] = []map[string]interface{}{{"key":key, "value":"", "dir":true, "nodes":make([]map[string]interface{}, 0)}} + all[min] = []map[string]interface{}{{"key": key, "value": "", "dir": true, "nodes": make([]map[string]interface{}, 0)}} } for _, p := range permissions { pKey, pRange := p[0], p[1] @@ -309,7 +309,7 @@ func getV2(w http.ResponseWriter, r *http.Request) { if pRange == "c" { pKey += separator } - opt = &client.GetOptions{Recursive:true, Sort:true} + opt = &client.GetOptions{Recursive: true, Sort: true} } if resp, err := kapi.Get(context.Background(), pKey, opt); err != nil { data["errorCode"] = 500 @@ -319,7 +319,7 @@ func getV2(w http.ResponseWriter, r *http.Request) { data["errorCode"] = 500 data["message"] = "The node does not exist." } else { - max = getNode(resp.Node , key, all, min, max) + max = getNode(resp.Node, key, all, min, max) } } } @@ -335,7 +335,7 @@ func getV2(w http.ResponseWriter, r *http.Request) { pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) pa["dir"] = true } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string) + separator) { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+separator) { pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) pa["dir"] = true } @@ -355,7 +355,7 @@ func getV2(w http.ResponseWriter, r *http.Request) { var dataByte []byte var err error - if dataByte, err = json.Marshal(data);err != nil { + if dataByte, err = json.Marshal(data); err != nil { io.WriteString(w, err.Error()) } else { io.WriteString(w, string(dataByte)) @@ -366,7 +366,7 @@ func nodesSort(node map[string]interface{}) { if v, ok := node["nodes"]; ok && v != nil { a := v.([]map[string]interface{}) if len(a) != 0 { - for i := 0; i < len(a) - 1; i++ { + for i := 0; i < len(a)-1; i++ { nodesSort(a[i]) for j := i + 1; j < len(a); j++ { if a[j]["key"].(string) < a[i]["key"].(string) { @@ -374,7 +374,7 @@ func nodesSort(node map[string]interface{}) { } } } - nodesSort(a[len(a) - 1]) + nodesSort(a[len(a)-1]) } } } @@ -389,7 +389,7 @@ func getNode(node *client.Node, selKey string, all map[int][]map[string]interfac if k == "" { continue } - nodeMap := map[string]interface{}{"key": k, "dir":true, "nodes":make([]map[string]interface{}, 0)} + nodeMap := map[string]interface{}{"key": k, "dir": true, "nodes": make([]map[string]interface{}, 0)} if k == node.Key { nodeMap["value"] = node.Value nodeMap["dir"] = node.Dir @@ -402,7 +402,7 @@ func getNode(node *client.Node, selKey string, all map[int][]map[string]interfac max = keylevel } - if _, ok := all[keylevel];!ok { + if _, ok := all[keylevel]; !ok { all[keylevel] = make([]map[string]interface{}, 0) } var isExist bool @@ -433,7 +433,7 @@ func delV2(w http.ResponseWriter, r *http.Request) { isDir, _ := strconv.ParseBool(dir) if isDir { - if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive:true, Dir:true}); err != nil { + if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive: true, Dir: true}); err != nil { io.WriteString(w, err.Error()) return } @@ -517,10 +517,10 @@ func getPermissionPrefixV2(host, uname, key string) ([][]string, error) { for _, ks := range role.Permissions.KV.Read { var k string if strings.HasSuffix(ks, "*") { - k = ks[:len(ks) - 1] + k = ks[:len(ks)-1] set[k] = "p" } else if strings.HasSuffix(ks, "/*") { - k = ks[:len(ks) - 2] + k = ks[:len(ks)-2] set[k] = "c" } else { if _, ok := set[ks]; !ok { @@ -585,12 +585,12 @@ func connect(w http.ResponseWriter, r *http.Request) { if *useAuth { if _, ok := rootUsers[host]; !ok && uname != "root" { // no root user - b, _ := json.Marshal(map[string]interface{}{"status":"root"}) + b, _ := json.Marshal(map[string]interface{}{"status": "root"}) io.WriteString(w, string(b)) return } if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status":"login"}) + b, _ := json.Marshal(map[string]interface{}{"status": "login"}) io.WriteString(w, string(b)) return } @@ -599,17 +599,17 @@ func connect(w http.ResponseWriter, r *http.Request) { if uinfo, ok := sess.Get("uinfo").(*userInfo); ok { if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) io.WriteString(w, string(b)) return } } - uinfo := &userInfo{host:host, uname:uname, passwd:passwd} + uinfo := &userInfo{host: host, uname: uname, passwd: passwd} c, err := newClient(uinfo) if err != nil { log.Println(r.Method, "v3", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status":"error", "message":err.Error()}) + b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) io.WriteString(w, string(b)) return } @@ -625,7 +625,7 @@ func connect(w http.ResponseWriter, r *http.Request) { } log.Println(r.Method, "v3", "connect success.") info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status":"running", "info":info}) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) io.WriteString(w, string(b)) } @@ -657,7 +657,7 @@ func put(w http.ResponseWriter, r *http.Request) { data["errorCode"] = 500 data["message"] = err.Error() } else { - if resp, err := cli.Get(context.Background(), key);err != nil { + if resp, err := cli.Get(context.Background(), key); err != nil { data["errorCode"] = 500 data["errorCode"] = err.Error() } else { @@ -676,7 +676,7 @@ func put(w http.ResponseWriter, r *http.Request) { } var dataByte []byte - if dataByte, err = json.Marshal(data);err != nil { + if dataByte, err = json.Marshal(data); err != nil { io.WriteString(w, err.Error()) } else { io.WriteString(w, string(dataByte)) @@ -743,7 +743,7 @@ func get(w http.ResponseWriter, r *http.Request) { } data["node"] = pnode } else { - if resp, err := cli.Get(context.Background(), key);err != nil { + if resp, err := cli.Get(context.Background(), key); err != nil { data["errorCode"] = 500 data["message"] = err.Error() } else { @@ -767,7 +767,7 @@ func get(w http.ResponseWriter, r *http.Request) { var dataByte []byte var err error - if dataByte, err = json.Marshal(data);err != nil { + if dataByte, err = json.Marshal(data); err != nil { io.WriteString(w, err.Error()) } else { io.WriteString(w, string(dataByte)) @@ -781,7 +781,7 @@ func getPath(w http.ResponseWriter, r *http.Request) { data = make(map[string]interface{}) /* {1:["/"], 2:["/foo", "/foo2"], 3:["/foo/bar", "/foo2/bar"], 4:["/foo/bar/test"]} - */ + */ all = make(map[int][]map[string]interface{}) min int max int @@ -826,7 +826,7 @@ func getPath(w http.ResponseWriter, r *http.Request) { //prefixKey = originKey } max = min - all[min] = []map[string]interface{}{{"key":originKey}} + all[min] = []map[string]interface{}{{"key": originKey}} if presp != nil && presp.Count != 0 { all[min][0]["value"] = string(presp.Kvs[0].Value) all[min][0]["ttl"] = getTTL(cli, presp.Kvs[0].Lease) @@ -857,12 +857,12 @@ func getPath(w http.ResponseWriter, r *http.Request) { continue } keys := strings.Split(string(kv.Key), separator) // /foo/bar - for i := range keys { // ["", "foo", "bar"] + for i := range keys { // ["", "foo", "bar"] k := strings.Join(keys[0:i+1], separator) if k == "" { continue } - node := map[string]interface{}{"key":k} + node := map[string]interface{}{"key": k} if node["key"].(string) == string(kv.Key) { node["value"] = string(kv.Value) if key == string(kv.Key) { @@ -878,7 +878,7 @@ func getPath(w http.ResponseWriter, r *http.Request) { max = level } - if _, ok := all[level];!ok { + if _, ok := all[level]; !ok { all[level] = make([]map[string]interface{}, 0) } levelNodes := all[level] @@ -904,7 +904,7 @@ func getPath(w http.ResponseWriter, r *http.Request) { pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) pa["dir"] = true } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string) +separator) { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+separator) { pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) pa["dir"] = true } @@ -914,7 +914,7 @@ func getPath(w http.ResponseWriter, r *http.Request) { } } data = all[min][0] - if dataByte, err := json.Marshal(map[string]interface{}{"node":data});err != nil { + if dataByte, err := json.Marshal(map[string]interface{}{"node": data}); err != nil { io.WriteString(w, err.Error()) } else { io.WriteString(w, string(dataByte)) @@ -928,13 +928,13 @@ func del(w http.ResponseWriter, r *http.Request) { dir := r.FormValue("dir") log.Println("DELETE", "v3", key) - if _, err := cli.Delete(context.Background(), key);err != nil { + if _, err := cli.Delete(context.Background(), key); err != nil { io.WriteString(w, err.Error()) return } if dir == "true" { - if _, err := cli.Delete(context.Background(), key +separator, clientv3.WithPrefix());err != nil { + if _, err := cli.Delete(context.Background(), key+separator, clientv3.WithPrefix()); err != nil { io.WriteString(w, err.Error()) return } @@ -987,10 +987,10 @@ func newClient(uinfo *userInfo) (*clientv3.Client, error) { } conf := clientv3.Config{ - Endpoints: endpoints, - DialTimeout: time.Second * time.Duration(*connectTimeout), - TLS: tlsConfig, DialOptions: []grpc.DialOption{grpc.WithBlock()}, + Endpoints: endpoints, + TLS: tlsConfig, + DialTimeout: time.Second * time.Duration(*connectTimeout), } if *useAuth { conf.Username = uinfo.uname @@ -1061,7 +1061,6 @@ func getInfo(host string) map[string]string { } defer rootClient.Close() - status, err := rootClient.Status(context.Background(), host) if err != nil { log.Fatal(err) @@ -1071,8 +1070,8 @@ func getInfo(host string) map[string]string { log.Fatal(err) } kb := 1024 - mb := kb*1024 - gb := mb*1024 + mb := kb * 1024 + gb := mb * 1024 var sizeStr string for _, m := range mems.Members { if m.ID == status.Leader { @@ -1104,5 +1103,5 @@ func getInfo(host string) map[string]string { } func size(num int, unit int) (n, rem int) { - return num/unit, num - (num/unit)*unit -} \ No newline at end of file + return num / unit, num - (num/unit)*unit +} From eb850c7f6ec7a8e83cc27c258e57117ecb53990d Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sat, 21 Aug 2021 22:01:31 +0200 Subject: [PATCH 03/13] feat: add grpc max receive message size option cf fork https://github.com/evildecay/etcdkeeper/compare/master...jim3ma:master#diff-d65e495a9fb96c8100cdcae69243e4ae485682af6b9ecad1e5b64f38645cc111R60 --- src/etcdkeeper/main.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index 86deded..cfc9db1 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -34,11 +34,12 @@ var ( keyfile = flag.String("key", "", "identify secure client using this TLS key file (v3)") useAuth = flag.Bool("auth", false, "use auth") connectTimeout = flag.Int("timeout", 5, "ETCD client connect timeout") + grpcMaxMsgSize = flag.Int("grpcMaxMsgSize", 64*1024*1024, "ETCDv3 grpc client max receive msg size") rootUsers = make(map[string]*userInfo) // host:rootUser rootUesrsV2 = make(map[string]*userInfo) // host:rootUser - sessmgr *session.Manager - mu sync.Mutex + sessmgr *session.Manager + mu sync.Mutex ) type userInfo struct { @@ -112,7 +113,7 @@ func main() { }) //log.Println(http.Dir(rootPath + "/assets")) - http.Handle("/", http.FileServer(http.Dir(rootPath + "/assets"))) // view static directory + http.Handle("/", http.FileServer(http.Dir(rootPath+"/assets"))) // view static directory log.Printf("listening on %s:%d\n", *host, *port) err = http.ListenAndServe(*host+":"+strconv.Itoa(*port), nil) @@ -987,10 +988,10 @@ func newClient(uinfo *userInfo) (*clientv3.Client, error) { } conf := clientv3.Config{ - DialOptions: []grpc.DialOption{grpc.WithBlock()}, Endpoints: endpoints, TLS: tlsConfig, DialTimeout: time.Second * time.Duration(*connectTimeout), + DialOptions: []grpc.DialOption{grpc.WithBlock(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*grpcMaxMsgSize))}, } if *useAuth { conf.Username = uinfo.uname From a8d3bd7ad329beb94aa8ba6fcc1d492087e25bb2 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sat, 21 Aug 2021 22:21:46 +0200 Subject: [PATCH 04/13] Upgrade: dockerfile go to 1.17.0 and alpine 3.14.1 split entrypoint and CMD like PR https://github.com/evildecay/etcdkeeper/compare/master...Jonwing:master#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R16 --- Dockerfile | 28 ++++++++++++---------------- src/etcdkeeper/go.mod | 14 ++++++++++++-- src/etcdkeeper/go.sum | 5 ----- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2e48cd3..9e39d93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,26 @@ -FROM golang:1.12 as build +FROM golang:1.17-alpine3.14 as build -ENV GO111MODULE on -ENV GOPROXY "https://goproxy.io" +WORKDIR /app +ADD . /app +WORKDIR /app/src/etcdkeeper -WORKDIR /opt -RUN mkdir etcdkeeper -ADD . /opt/etcdkeeper -WORKDIR /opt/etcdkeeper/src/etcdkeeper +ENV CGO_ENABLED=0 -RUN go mod download \ - && go build -o etcdkeeper.bin main.go +RUN go mod download +RUN go build -o ../../etcdkeeper -ldflags='-w -s' -a -tags netgo -installsuffix netgo main.go - -FROM alpine:3.10 +FROM alpine:3.14.1 ENV HOST="0.0.0.0" ENV PORT="8080" -# RUN apk add --no-cache ca-certificates - -RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 +RUN apk add --no-cache ca-certificates WORKDIR /opt/etcdkeeper -COPY --from=build /opt/etcdkeeper/src/etcdkeeper/etcdkeeper.bin . +COPY --from=build /app/etcdkeeper . ADD assets assets EXPOSE ${PORT} -ENTRYPOINT ./etcdkeeper.bin -h $HOST -p $PORT \ No newline at end of file +ENTRYPOINT ["./etcdkeeper"] +CMD "-h $HOST -p $PORT" diff --git a/src/etcdkeeper/go.mod b/src/etcdkeeper/go.mod index 8e66bf9..db13372 100644 --- a/src/etcdkeeper/go.mod +++ b/src/etcdkeeper/go.mod @@ -1,6 +1,6 @@ module etcdkeeper -go 1.12 +go 1.17 require ( github.com/coreos/bbolt v1.3.3 // indirect @@ -23,7 +23,6 @@ require ( github.com/soheilhy/cmux v0.1.4 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - go.etcd.io/bbolt v1.3.3 // indirect go.uber.org/atomic v1.4.0 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.10.0 // indirect @@ -34,3 +33,14 @@ require ( google.golang.org/grpc v1.23.0 sigs.k8s.io/yaml v1.1.0 // indirect ) + +require ( + github.com/golang/protobuf v1.3.2 // indirect + github.com/json-iterator/go v1.1.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/pkg/errors v0.8.0 // indirect + golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 // indirect + golang.org/x/text v0.3.0 // indirect + gopkg.in/yaml.v2 v2.2.1 // indirect +) diff --git a/src/etcdkeeper/go.sum b/src/etcdkeeper/go.sum index 14a0b70..660a2ab 100644 --- a/src/etcdkeeper/go.sum +++ b/src/etcdkeeper/go.sum @@ -65,10 +65,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -112,8 +110,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -170,7 +166,6 @@ google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= From 8c86b31c7cfcc5361c050e3b7be517028e97931a Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sun, 22 Aug 2021 15:49:42 +0200 Subject: [PATCH 05/13] add non root user etcdkeeper into dockerfile PR https://github.com/evildecay/etcdkeeper/compare/master...frederikleemans:master#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R30 --- Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9e39d93..0af7200 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,11 +16,16 @@ ENV PORT="8080" RUN apk add --no-cache ca-certificates +# Create a user 'etcdkeeper' member of group 'etcdkeeper' +RUN addgroup -S etcdkeeper && \ + adduser -S -D -h /etcdkeeper -G etcdkeeper etcdkeeper + WORKDIR /opt/etcdkeeper -COPY --from=build /app/etcdkeeper . -ADD assets assets +COPY --from=build --chown=etcdkeeper:etcdkeeper /app/etcdkeeper . +ADD --chown=etcdkeeper:etcdkeeper assets assets EXPOSE ${PORT} +USER etcdkeeper ENTRYPOINT ["./etcdkeeper"] CMD "-h $HOST -p $PORT" From dcab13989042ae5052dff57548fa756cbed8ecfc Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sun, 22 Aug 2021 16:39:24 +0200 Subject: [PATCH 06/13] add golangci configuration like PR https://github.com/evildecay/etcdkeeper/compare/master...aschenmaker:master#diff-981a25288b197f45c6ff2b2860cdb655cad2fec7b5166b5f82b65803f100e986 --- src/etcdkeeper/.golangci.yml | 721 +++++++++++++++++++++++++++++++++++ src/etcdkeeper/main.go | 27 -- 2 files changed, 721 insertions(+), 27 deletions(-) create mode 100644 src/etcdkeeper/.golangci.yml diff --git a/src/etcdkeeper/.golangci.yml b/src/etcdkeeper/.golangci.yml new file mode 100644 index 0000000..d981b06 --- /dev/null +++ b/src/etcdkeeper/.golangci.yml @@ -0,0 +1,721 @@ +# This file contains all available configuration options +# with their default values. + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: + - mytag + + # which dirs to skip: issues from them won't be reported; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but default dirs are skipped independently + # from this option's value (see skip-dirs-use-default). + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-dirs: + - src/external_libs + - autogenerated_by_my_lib + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-files: + - ".*\\.my\\.go$" + - lib/bad.go + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + # modules-download-mode: mod + + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: false + + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions + # default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + # make issues output unique by line, default is true + uniq-by-line: true + + # add a prefix to the output file references; default is no prefix + path-prefix: "" + + # sorts results by: filepath, line and column + sort-results: false + + +# all available settings of specific linters +linters-settings: + + cyclop: + # the maximal code complexity to report + max-complexity: 20 + # the maximal average package complexity. If it's higher than 0.0 (float) the check is enabled (default 0.0) + package-average: 0.0 + # should ignore tests (default false) + skip-tests: false + + dogsled: + # checks assignments with too many blank identifiers; default is 2 + max-blank-identifiers: 2 + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 200 + + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + ignore: fmt:.*,io/ioutil:^Read.* + + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + + + errorlint: + # Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats + errorf: true + # Check for plain type assertions and type switches + asserts: true + # Check for plain error comparisons + comparison: true + + exhaustive: + # check switch statements in generated files also + check-generated: false + # indicates that switch statements are to be considered exhaustive if a + # 'default' case is present, even if all enum members aren't listed in the + # switch + default-signifies-exhaustive: false + + # exhaustivestruct: + # # Struct Patterns is list of expressions to match struct packages and names + # # The struct packages have the form example.com/package.ExampleStruct + # # The matching patterns can use matching syntax from https://pkg.go.dev/path#Match + # # If this list is empty, all structs are tested. + # struct-patterns: + # - '*.Test' + # - 'example.com/package.ExampleStruct' + + forbidigo: + # Forbid the following identifiers (identifiers are written using regexp): + forbid: + - ^print.*$ + - 'fmt\.Print.*' + # Exclude godoc examples from forbidigo checks. Default is true. + exclude_godoc_examples: false + + funlen: + lines: 200 + statements: 100 + + gocognit: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 30 + + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 5 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 3 + + gocritic: + # Which checks should be enabled; can't be combined with 'disabled-checks'; + # See https://go-critic.github.io/overview#checks-overview + # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` + # By default list of stable checks is used. + # enabled-checks: + # - rangeValCopy + + # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty + disabled-checks: + - regexpMust + + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". + enabled-tags: + - performance + disabled-tags: + - experimental + + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: # must be valid enabled check name + # whether to restrict checker to params only (default true) + paramsOnly: true + elseif: + # whether to skip balanced if-else pairs (default true) + skipBalanced: true + hugeParam: + # size in bytes that makes the warning trigger (default 80) + sizeThreshold: 80 + # nestingReduce: + # # min number of statements inside a branch to trigger a warning (default 5) + # bodyWidth: 5 + rangeExprCopy: + # size in bytes that makes the warning trigger (default 512) + sizeThreshold: 512 + # whether to check test functions (default true) + skipTestFuncs: true + # rangeValCopy: + # # size in bytes that makes the warning trigger (default 128) + # sizeThreshold: 32 + # # whether to check test functions (default true) + # skipTestFuncs: true + + # truncateCmp: + # # whether to skip int/uint/uintptr types (default true) + # skipArchDependent: true + underef: + # whether to skip (*x).method() calls where x is a pointer receiver (default true) + skipRecvDeref: true + # unnamedResult: + # # whether to check exported functions + # checkExported: true + + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 20 + + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - NOTE + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + gofumpt: + # Choose whether or not to use the extra rules that are disabled + # by default + extra-rules: false + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + gomnd: + settings: + mnd: + # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + checks: + - argument + - case + - condition + - operation + - return + - assign + # ignored-numbers: 1000 + # ignored-files: magic_.*.go + # ignored-functions: math.* + + gomoddirectives: + # Allow local `replace` directives. Default is false. + replace-local: false + # List of allowed `replace` directives. Default is empty. + replace-allow-list: + - launchpad.net/gocheck + # Allow to not explain why the version has been retracted in the `retract` directives. Default is false. + retract-allow-no-explanation: false + # Forbid the use of the `exclude` directives. Default is false. + exclude-forbidden: false + + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + includes: + - G306 + - G101 + - G109 + # To specify a set of rules to explicitly exclude. + # Available rules: https://github.com/securego/gosec#available-rules + excludes: + - G401 + - G204 + # To specify the configuration of rules. + # The configuration of rules is not fully documented by gosec: + # https://github.com/securego/gosec#configuration + # https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/rules/rulelist.go#L60-L102 + config: + G306: "0600" + G101: + pattern: "(?i)example" + ignore_entropy: false + entropy_threshold: "80.0" + per_char_threshold: "3.0" + truncate: "32" + + gosimple: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + govet: + # report about shadowed variables + check-shadowing: false + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + + # enable or disable analyzers by name + # run `go tool vet help` to see all analyzers + enable: + - atomicalign + enable-all: false + disable: + - shadow + disable-all: false + + depguard: + list-type: blacklist + include-go-root: false + packages: + - github.com/sirupsen/logrus + packages-with-error-message: + # specify an error message to output when a blacklisted package is used + - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + + ifshort: + # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. + # Has higher priority than max-decl-chars. + max-decl-lines: 2 + # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax. + max-decl-chars: 30 + + + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 150 + # tab width in spaces. Default to 1. + tab-width: 1 + + makezero: + # Allow only slices initialized with a length of zero. Default is false. + always: false + + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - someword + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + promlinter: + # Promlinter cannot infer all metrics name in static analysis. + # Enable strict mode will also include the errors caused by failing to parse the args. + strict: false + # Please refer to https://github.com/yeya24/promlinter#usage for detailed usage. + disabled-linters: + # - "Help" + # - "MetricUnits" + # - "Counter" + # - "HistogramSummaryReserved" + # - "MetricTypeInName" + # - "ReservedChars" + # - "CamelCase" + # - "lintUnitAbbreviations" + + nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: false + # Disable to ensure that nolint directives don't have a leading space. Default is true. + allow-leading-space: true + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true + + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + + revive: + # see https://github.com/mgechev/revive#available-rules for details. + ignore-generated-header: true + severity: warning + rules: + - name: indent-error-flow + severity: warning + # - name: add-constant + # severity: warning + # arguments: + # - maxLitCount: "3" + # allowStrs: '""' + # allowInts: "0,1,2" + # allowFloats: "0.0,0.,1.0,1.,2.0,2." + - name: unused-parameter + severity: warning + - name: unreachable-code + severity: warning + - name: atomic + severity: error + - name: unexported-naming + severity: warning + - name: unconditional-recursion + severity: error + - name: early-return + severity: warning + - name: duplicated-imports + severity: warning + - name: range-val-address + severity: warning + - name: waitgroup-by-value + severity: warning + - name: range-val-in-closure + severity: warning + + staticcheck: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + stylecheck: + # Select the Go version to target. The default is '1.13'. + go: "1.17" + # https://staticcheck.io/docs/options#checks + checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ] + # https://staticcheck.io/docs/options#dot_import_whitelist + dot-import-whitelist: + - fmt + # https://staticcheck.io/docs/options#initialisms + initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ] + # https://staticcheck.io/docs/options#http_status_code_whitelist + http-status-code-whitelist: [ "200", "400", "404", "500" ] + + tagliatelle: + # check the struck tag name case + case: + # use the struct field name to check the name of the struct tag + use-field-name: true + rules: + # any struct tag type can be used. + # support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + json: snake + yaml: camel + xml: camel + bson: camel + avro: snake + mapstructure: kebab + + testpackage: + # regexp pattern to skip files + skip-regexp: (export|internal)_test\.go + + thelper: + # The following configurations enable all checks. It can be omitted because all checks are enabled by default. + # You can enable only required checks deleting unnecessary checks. + test: + first: true + name: true + begin: true + benchmark: + first: true + name: true + begin: true + tb: + first: true + name: true + begin: true + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + unused: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + + wrapcheck: + # An array of strings that specify substrings of signatures to ignore. + # If this set, it will override the default set of ignored signatures. + # See https://github.com/tomarrell/wrapcheck#configuration for more information. + ignoreSigs: + - .Errorf( + - errors.New( + - errors.Unwrap( + - .Wrap( + - .Wrapf( + - .WithMessage( + wsl: + # See https://github.com/bombsimon/wsl/blob/master/doc/configuration.md for + # documentation of available settings. These are the defaults for + # `golangci-lint`. + allow-assign-and-anything: true + allow-assign-and-call: true + allow-cuddle-declarations: true + allow-multiline-assign: true + allow-separated-leading-comment: true + allow-trailing-comment: true + force-case-trailing-whitespace: 0 + force-err-cuddling: false + force-short-decl-cuddling: false + strict-append: true + +linters: + enable: + - megacheck + - govet + - asciicheck + - bodyclose + - durationcheck + - errcheck + - errorlint + - exhaustive + - exportloopref + - gosec + - gosimple + - ineffassign + - makezero + - nilerr + # - noctx + - rowserrcheck + - sqlclosecheck + - staticcheck + - typecheck + - unparam + - unused + - varcheck + - cyclop + - dogsled + - dupl + - forbidigo + - forcetypeassert + - funlen + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - goimports + - gomoddirectives + - gomodguard + - ifshort + - lll + - misspell + - nestif + - predeclared + - revive + # - tagliatelle + - testpackage + - tparallel + - unconvert + - wastedassign + - whitespace + disable: + - noctx + - maligned + - prealloc + - gochecknoglobals + - gochecknoinits + - gofumpt + - goheader + - gomnd + - goprintffuncname + - importas + - nolintlint + - paralleltest + - promlinter + - stylecheck + - thelper + - wrapcheck + - scopelint + - nakedret # 对命名返回值的写法会判断错误 + - wsl + - nlreturn + - gci # 与gofmt冲突 + disable-all: false + presets: + - bugs + - unused + fast: false + + +issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + - abcdef + + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + + # Exclude known linters from partially hard-vendored code, + # which is impossible to exclude via "nolint" comments. + - path: internal/hmac/ + text: "weak cryptographic primitive" + linters: + - gosec + + # Exclude some staticcheck messages + - linters: + - staticcheck + text: "SA9003:" + + # Exclude lll issues for long lines with go:generate + - linters: + - lll + source: "^//go:generate " + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # The default value is false. If set to true exclude and exclude-rules + # regular expressions become case sensitive. + exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. By default it's empty. + include: + - EXC0002 # disable excluding of issues about comments from golint + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + # new-from-rev: REV + + # Show only new issues created in git patch with set file path. + # new-from-patch: path/to/patch/file + + # Fix found issues (if it's supported by the linter) + fix: false + +severity: + # Default value is empty string. + # Set the default severity for issues. If severity rules are defined and the issues + # do not match or no severity is provided to the rule this will be the default + # severity applied. Severities should match the supported severity names of the + # selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + default-severity: error + + # The default value is false. + # If set to true severity-rules regular expressions become case sensitive. + case-sensitive: false + + # Default value is empty list. + # When a list of severity rules are provided, severity information will be added to lint + # issues. Severity rules have the same filtering capability as exclude rules except you + # are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + rules: + - linters: + - dupl + severity: info diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index cfc9db1..d74744f 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -126,33 +126,6 @@ func nothing(_ http.ResponseWriter, _ *http.Request) { // Nothing } -//func v2request(w http.ResponseWriter, r *http.Request){ -// if err := r.ParseForm(); err != nil { -// log.Println(err.Error()) -// } -// log.Println(r.Method, "v2", r.FormValue("url"), r.PostForm.Encode()) -// -// body := strings.NewReader(r.PostForm.Encode()) -// req, err := http.NewRequest(r.Method, r.Form.Get("url"), body) -// if err != nil { -// io.WriteString(w, err.Error()) -// return -// } -// req.Header.Set("Content-Type", "application/x-www-form-urlencoded") -// client := &http.Client{Timeout: 10*time.Second} // important!!! -// resp, err := client.Do(req) -// if err != nil { -// io.WriteString(w, err.Error()) -// }else { -// result, err := ioutil.ReadAll(resp.Body) -// if err != nil { -// io.WriteString(w, "Get data failed: " + err.Error()) -// } else { -// io.WriteString(w, string(result)) -// } -// } -//} - // v2 api func connectV2(w http.ResponseWriter, r *http.Request) { mu.Lock() From 6196628160ef5ae273434956319552c91bb44f6b Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Sun, 22 Aug 2021 15:37:24 +0200 Subject: [PATCH 07/13] Add makefile to run docker build like PR https://github.com/evildecay/etcdkeeper/compare/master...ivanabc:master#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52R1 --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d7e003 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: all + +all: + docker image build -t etcdkeeper:1.0.0-snapshot . + +lint: + cd src/etcdkeeper && golangci-lint run --new-from-rev=HEAD~ From 42c0fbaa626e0cfc50a356931f92421ff75ff0ee Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Tue, 24 Aug 2021 13:48:21 +0200 Subject: [PATCH 08/13] Feat: use fs.FS from embed go 1.16 instead of http.FileSystem for static resource PR : https://github.com/evildecay/etcdkeeper/compare/master...raochq:master#diff-2f93e3d311b56ca676aeab947b7cae8e616e1421a073bc213c56294053f108d8R102 --- .../assets/static}/etcdkeeper/index.html | 0 .../assets/static}/framework/ace/ace.js | 0 .../static}/framework/ace/ext-beautify.js | 0 .../ace/ext-elastic_tabstops_lite.js | 0 .../assets/static}/framework/ace/ext-emmet.js | 0 .../static}/framework/ace/ext-error_marker.js | 0 .../framework/ace/ext-keybinding_menu.js | 0 .../framework/ace/ext-language_tools.js | 0 .../static}/framework/ace/ext-linking.js | 0 .../static}/framework/ace/ext-modelist.js | 0 .../static}/framework/ace/ext-old_ie.js | 0 .../static}/framework/ace/ext-searchbox.js | 0 .../framework/ace/ext-settings_menu.js | 0 .../static}/framework/ace/ext-spellcheck.js | 0 .../assets/static}/framework/ace/ext-split.js | 0 .../framework/ace/ext-static_highlight.js | 0 .../static}/framework/ace/ext-statusbar.js | 0 .../static}/framework/ace/ext-textarea.js | 0 .../static}/framework/ace/ext-themelist.js | 0 .../static}/framework/ace/ext-whitespace.js | 0 .../static}/framework/ace/keybinding-emacs.js | 0 .../static}/framework/ace/keybinding-vim.js | 0 .../assets/static}/framework/ace/mode-abap.js | 0 .../assets/static}/framework/ace/mode-abc.js | 0 .../framework/ace/mode-actionscript.js | 0 .../assets/static}/framework/ace/mode-ada.js | 0 .../static}/framework/ace/mode-apache_conf.js | 0 .../static}/framework/ace/mode-applescript.js | 0 .../static}/framework/ace/mode-asciidoc.js | 0 .../framework/ace/mode-assembly_x86.js | 0 .../static}/framework/ace/mode-autohotkey.js | 0 .../static}/framework/ace/mode-batchfile.js | 0 .../assets/static}/framework/ace/mode-bro.js | 0 .../static}/framework/ace/mode-c9search.js | 0 .../static}/framework/ace/mode-c_cpp.js | 0 .../static}/framework/ace/mode-cirru.js | 0 .../static}/framework/ace/mode-clojure.js | 0 .../static}/framework/ace/mode-cobol.js | 0 .../static}/framework/ace/mode-coffee.js | 0 .../static}/framework/ace/mode-coldfusion.js | 0 .../static}/framework/ace/mode-csharp.js | 0 .../assets/static}/framework/ace/mode-css.js | 0 .../static}/framework/ace/mode-curly.js | 0 .../assets/static}/framework/ace/mode-d.js | 0 .../assets/static}/framework/ace/mode-dart.js | 0 .../assets/static}/framework/ace/mode-diff.js | 0 .../static}/framework/ace/mode-django.js | 0 .../static}/framework/ace/mode-dockerfile.js | 0 .../assets/static}/framework/ace/mode-dot.js | 0 .../static}/framework/ace/mode-drools.js | 0 .../static}/framework/ace/mode-eiffel.js | 0 .../assets/static}/framework/ace/mode-ejs.js | 0 .../static}/framework/ace/mode-elixir.js | 0 .../assets/static}/framework/ace/mode-elm.js | 0 .../static}/framework/ace/mode-erlang.js | 0 .../static}/framework/ace/mode-forth.js | 0 .../static}/framework/ace/mode-fortran.js | 0 .../assets/static}/framework/ace/mode-ftl.js | 0 .../static}/framework/ace/mode-gcode.js | 0 .../static}/framework/ace/mode-gherkin.js | 0 .../static}/framework/ace/mode-gitignore.js | 0 .../assets/static}/framework/ace/mode-glsl.js | 0 .../static}/framework/ace/mode-gobstones.js | 0 .../static}/framework/ace/mode-golang.js | 0 .../static}/framework/ace/mode-groovy.js | 0 .../assets/static}/framework/ace/mode-haml.js | 0 .../static}/framework/ace/mode-handlebars.js | 0 .../static}/framework/ace/mode-haskell.js | 0 .../framework/ace/mode-haskell_cabal.js | 0 .../assets/static}/framework/ace/mode-haxe.js | 0 .../static}/framework/ace/mode-hjson.js | 0 .../assets/static}/framework/ace/mode-html.js | 0 .../static}/framework/ace/mode-html_elixir.js | 0 .../static}/framework/ace/mode-html_ruby.js | 0 .../assets/static}/framework/ace/mode-ini.js | 0 .../assets/static}/framework/ace/mode-io.js | 0 .../assets/static}/framework/ace/mode-jack.js | 0 .../assets/static}/framework/ace/mode-jade.js | 0 .../assets/static}/framework/ace/mode-java.js | 0 .../static}/framework/ace/mode-javascript.js | 0 .../assets/static}/framework/ace/mode-json.js | 0 .../static}/framework/ace/mode-jsoniq.js | 0 .../assets/static}/framework/ace/mode-jsp.js | 0 .../assets/static}/framework/ace/mode-jsx.js | 0 .../static}/framework/ace/mode-julia.js | 0 .../static}/framework/ace/mode-kotlin.js | 0 .../static}/framework/ace/mode-latex.js | 0 .../assets/static}/framework/ace/mode-less.js | 0 .../static}/framework/ace/mode-liquid.js | 0 .../assets/static}/framework/ace/mode-lisp.js | 0 .../static}/framework/ace/mode-livescript.js | 0 .../static}/framework/ace/mode-logiql.js | 0 .../assets/static}/framework/ace/mode-lsl.js | 0 .../assets/static}/framework/ace/mode-lua.js | 0 .../static}/framework/ace/mode-luapage.js | 0 .../static}/framework/ace/mode-lucene.js | 0 .../static}/framework/ace/mode-makefile.js | 0 .../static}/framework/ace/mode-markdown.js | 0 .../assets/static}/framework/ace/mode-mask.js | 0 .../static}/framework/ace/mode-matlab.js | 0 .../assets/static}/framework/ace/mode-maze.js | 0 .../assets/static}/framework/ace/mode-mel.js | 0 .../static}/framework/ace/mode-mushcode.js | 0 .../static}/framework/ace/mode-mysql.js | 0 .../assets/static}/framework/ace/mode-nix.js | 0 .../assets/static}/framework/ace/mode-nsis.js | 0 .../static}/framework/ace/mode-objectivec.js | 0 .../static}/framework/ace/mode-ocaml.js | 0 .../static}/framework/ace/mode-pascal.js | 0 .../assets/static}/framework/ace/mode-perl.js | 0 .../static}/framework/ace/mode-pgsql.js | 0 .../assets/static}/framework/ace/mode-php.js | 0 .../static}/framework/ace/mode-plain_text.js | 0 .../static}/framework/ace/mode-powershell.js | 0 .../static}/framework/ace/mode-praat.js | 0 .../static}/framework/ace/mode-prolog.js | 0 .../static}/framework/ace/mode-properties.js | 0 .../static}/framework/ace/mode-protobuf.js | 0 .../static}/framework/ace/mode-python.js | 0 .../assets/static}/framework/ace/mode-r.js | 0 .../static}/framework/ace/mode-razor.js | 0 .../assets/static}/framework/ace/mode-rdoc.js | 0 .../static}/framework/ace/mode-rhtml.js | 0 .../assets/static}/framework/ace/mode-rst.js | 0 .../assets/static}/framework/ace/mode-ruby.js | 0 .../assets/static}/framework/ace/mode-rust.js | 0 .../assets/static}/framework/ace/mode-sass.js | 0 .../assets/static}/framework/ace/mode-scad.js | 0 .../static}/framework/ace/mode-scala.js | 0 .../static}/framework/ace/mode-scheme.js | 0 .../assets/static}/framework/ace/mode-scss.js | 0 .../assets/static}/framework/ace/mode-sh.js | 0 .../assets/static}/framework/ace/mode-sjs.js | 0 .../static}/framework/ace/mode-smarty.js | 0 .../static}/framework/ace/mode-snippets.js | 0 .../framework/ace/mode-soy_template.js | 0 .../static}/framework/ace/mode-space.js | 0 .../assets/static}/framework/ace/mode-sql.js | 0 .../static}/framework/ace/mode-sqlserver.js | 0 .../static}/framework/ace/mode-stylus.js | 0 .../assets/static}/framework/ace/mode-svg.js | 0 .../static}/framework/ace/mode-swift.js | 0 .../assets/static}/framework/ace/mode-tcl.js | 0 .../assets/static}/framework/ace/mode-tex.js | 0 .../assets/static}/framework/ace/mode-text.js | 0 .../static}/framework/ace/mode-textile.js | 0 .../assets/static}/framework/ace/mode-toml.js | 0 .../assets/static}/framework/ace/mode-tsx.js | 0 .../assets/static}/framework/ace/mode-twig.js | 0 .../static}/framework/ace/mode-typescript.js | 0 .../assets/static}/framework/ace/mode-vala.js | 0 .../static}/framework/ace/mode-vbscript.js | 0 .../static}/framework/ace/mode-velocity.js | 0 .../static}/framework/ace/mode-verilog.js | 0 .../assets/static}/framework/ace/mode-vhdl.js | 0 .../static}/framework/ace/mode-wollok.js | 0 .../assets/static}/framework/ace/mode-xml.js | 0 .../static}/framework/ace/mode-xquery.js | 0 .../assets/static}/framework/ace/mode-yaml.js | 0 .../static}/framework/ace/snippets/abap.js | 0 .../static}/framework/ace/snippets/abc.js | 0 .../framework/ace/snippets/actionscript.js | 0 .../static}/framework/ace/snippets/ada.js | 0 .../framework/ace/snippets/apache_conf.js | 0 .../framework/ace/snippets/applescript.js | 0 .../framework/ace/snippets/asciidoc.js | 0 .../framework/ace/snippets/assembly_x86.js | 0 .../framework/ace/snippets/autohotkey.js | 0 .../framework/ace/snippets/batchfile.js | 0 .../static}/framework/ace/snippets/bro.js | 0 .../framework/ace/snippets/c9search.js | 0 .../static}/framework/ace/snippets/c_cpp.js | 0 .../static}/framework/ace/snippets/cirru.js | 0 .../static}/framework/ace/snippets/clojure.js | 0 .../static}/framework/ace/snippets/cobol.js | 0 .../static}/framework/ace/snippets/coffee.js | 0 .../framework/ace/snippets/coldfusion.js | 0 .../static}/framework/ace/snippets/csharp.js | 0 .../static}/framework/ace/snippets/css.js | 0 .../static}/framework/ace/snippets/curly.js | 0 .../static}/framework/ace/snippets/d.js | 0 .../static}/framework/ace/snippets/dart.js | 0 .../static}/framework/ace/snippets/diff.js | 0 .../static}/framework/ace/snippets/django.js | 0 .../framework/ace/snippets/dockerfile.js | 0 .../static}/framework/ace/snippets/dot.js | 0 .../static}/framework/ace/snippets/drools.js | 0 .../static}/framework/ace/snippets/eiffel.js | 0 .../static}/framework/ace/snippets/ejs.js | 0 .../static}/framework/ace/snippets/elixir.js | 0 .../static}/framework/ace/snippets/elm.js | 0 .../static}/framework/ace/snippets/erlang.js | 0 .../static}/framework/ace/snippets/forth.js | 0 .../static}/framework/ace/snippets/fortran.js | 0 .../static}/framework/ace/snippets/ftl.js | 0 .../static}/framework/ace/snippets/gcode.js | 0 .../static}/framework/ace/snippets/gherkin.js | 0 .../framework/ace/snippets/gitignore.js | 0 .../static}/framework/ace/snippets/glsl.js | 0 .../framework/ace/snippets/gobstones.js | 0 .../static}/framework/ace/snippets/golang.js | 0 .../static}/framework/ace/snippets/groovy.js | 0 .../static}/framework/ace/snippets/haml.js | 0 .../framework/ace/snippets/handlebars.js | 0 .../static}/framework/ace/snippets/haskell.js | 0 .../framework/ace/snippets/haskell_cabal.js | 0 .../static}/framework/ace/snippets/haxe.js | 0 .../static}/framework/ace/snippets/hjson.js | 0 .../static}/framework/ace/snippets/html.js | 0 .../framework/ace/snippets/html_elixir.js | 0 .../framework/ace/snippets/html_ruby.js | 0 .../static}/framework/ace/snippets/ini.js | 0 .../static}/framework/ace/snippets/io.js | 0 .../static}/framework/ace/snippets/jack.js | 0 .../static}/framework/ace/snippets/jade.js | 0 .../static}/framework/ace/snippets/java.js | 0 .../framework/ace/snippets/javascript.js | 0 .../static}/framework/ace/snippets/json.js | 0 .../static}/framework/ace/snippets/jsoniq.js | 0 .../static}/framework/ace/snippets/jsp.js | 0 .../static}/framework/ace/snippets/jsx.js | 0 .../static}/framework/ace/snippets/julia.js | 0 .../static}/framework/ace/snippets/kotlin.js | 0 .../static}/framework/ace/snippets/latex.js | 0 .../static}/framework/ace/snippets/less.js | 0 .../static}/framework/ace/snippets/liquid.js | 0 .../static}/framework/ace/snippets/lisp.js | 0 .../framework/ace/snippets/livescript.js | 0 .../static}/framework/ace/snippets/logiql.js | 0 .../static}/framework/ace/snippets/lsl.js | 0 .../static}/framework/ace/snippets/lua.js | 0 .../static}/framework/ace/snippets/luapage.js | 0 .../static}/framework/ace/snippets/lucene.js | 0 .../framework/ace/snippets/makefile.js | 0 .../framework/ace/snippets/markdown.js | 0 .../static}/framework/ace/snippets/mask.js | 0 .../static}/framework/ace/snippets/matlab.js | 0 .../static}/framework/ace/snippets/maze.js | 0 .../static}/framework/ace/snippets/mel.js | 0 .../framework/ace/snippets/mushcode.js | 0 .../static}/framework/ace/snippets/mysql.js | 0 .../static}/framework/ace/snippets/nix.js | 0 .../static}/framework/ace/snippets/nsis.js | 0 .../framework/ace/snippets/objectivec.js | 0 .../static}/framework/ace/snippets/ocaml.js | 0 .../static}/framework/ace/snippets/pascal.js | 0 .../static}/framework/ace/snippets/perl.js | 0 .../static}/framework/ace/snippets/pgsql.js | 0 .../static}/framework/ace/snippets/php.js | 0 .../framework/ace/snippets/plain_text.js | 0 .../framework/ace/snippets/powershell.js | 0 .../static}/framework/ace/snippets/praat.js | 0 .../static}/framework/ace/snippets/prolog.js | 0 .../framework/ace/snippets/properties.js | 0 .../framework/ace/snippets/protobuf.js | 0 .../static}/framework/ace/snippets/python.js | 0 .../static}/framework/ace/snippets/r.js | 0 .../static}/framework/ace/snippets/razor.js | 0 .../static}/framework/ace/snippets/rdoc.js | 0 .../static}/framework/ace/snippets/rhtml.js | 0 .../static}/framework/ace/snippets/rst.js | 0 .../static}/framework/ace/snippets/ruby.js | 0 .../static}/framework/ace/snippets/rust.js | 0 .../static}/framework/ace/snippets/sass.js | 0 .../static}/framework/ace/snippets/scad.js | 0 .../static}/framework/ace/snippets/scala.js | 0 .../static}/framework/ace/snippets/scheme.js | 0 .../static}/framework/ace/snippets/scss.js | 0 .../static}/framework/ace/snippets/sh.js | 0 .../static}/framework/ace/snippets/sjs.js | 0 .../static}/framework/ace/snippets/smarty.js | 0 .../framework/ace/snippets/snippets.js | 0 .../framework/ace/snippets/soy_template.js | 0 .../static}/framework/ace/snippets/space.js | 0 .../static}/framework/ace/snippets/sql.js | 0 .../framework/ace/snippets/sqlserver.js | 0 .../static}/framework/ace/snippets/stylus.js | 0 .../static}/framework/ace/snippets/svg.js | 0 .../static}/framework/ace/snippets/swift.js | 0 .../static}/framework/ace/snippets/tcl.js | 0 .../static}/framework/ace/snippets/tex.js | 0 .../static}/framework/ace/snippets/text.js | 0 .../static}/framework/ace/snippets/textile.js | 0 .../static}/framework/ace/snippets/toml.js | 0 .../static}/framework/ace/snippets/tsx.js | 0 .../static}/framework/ace/snippets/twig.js | 0 .../framework/ace/snippets/typescript.js | 0 .../static}/framework/ace/snippets/vala.js | 0 .../framework/ace/snippets/vbscript.js | 0 .../framework/ace/snippets/velocity.js | 0 .../static}/framework/ace/snippets/verilog.js | 0 .../static}/framework/ace/snippets/vhdl.js | 0 .../static}/framework/ace/snippets/wollok.js | 0 .../static}/framework/ace/snippets/xml.js | 0 .../static}/framework/ace/snippets/xquery.js | 0 .../static}/framework/ace/snippets/yaml.js | 0 .../static}/framework/ace/theme-ambiance.js | 0 .../static}/framework/ace/theme-chaos.js | 0 .../static}/framework/ace/theme-chrome.js | 0 .../static}/framework/ace/theme-clouds.js | 0 .../framework/ace/theme-clouds_midnight.js | 0 .../static}/framework/ace/theme-cobalt.js | 0 .../framework/ace/theme-crimson_editor.js | 0 .../static}/framework/ace/theme-dawn.js | 0 .../framework/ace/theme-dreamweaver.js | 0 .../static}/framework/ace/theme-eclipse.js | 0 .../static}/framework/ace/theme-github.js | 0 .../framework/ace/theme-idle_fingers.js | 0 .../static}/framework/ace/theme-iplastic.js | 0 .../framework/ace/theme-katzenmilch.js | 0 .../static}/framework/ace/theme-kr_theme.js | 0 .../static}/framework/ace/theme-kuroir.js | 0 .../static}/framework/ace/theme-merbivore.js | 0 .../framework/ace/theme-merbivore_soft.js | 0 .../framework/ace/theme-mono_industrial.js | 0 .../static}/framework/ace/theme-monokai.js | 0 .../framework/ace/theme-pastel_on_dark.js | 0 .../framework/ace/theme-solarized_dark.js | 0 .../framework/ace/theme-solarized_light.js | 0 .../static}/framework/ace/theme-sqlserver.js | 0 .../static}/framework/ace/theme-terminal.js | 0 .../static}/framework/ace/theme-textmate.js | 0 .../static}/framework/ace/theme-tomorrow.js | 0 .../framework/ace/theme-tomorrow_night.js | 0 .../ace/theme-tomorrow_night_blue.js | 0 .../ace/theme-tomorrow_night_bright.js | 0 .../ace/theme-tomorrow_night_eighties.js | 0 .../static}/framework/ace/theme-twilight.js | 0 .../framework/ace/theme-vibrant_ink.js | 0 .../static}/framework/ace/theme-xcode.js | 0 .../static}/framework/ace/worker-coffee.js | 0 .../static}/framework/ace/worker-css.js | 0 .../static}/framework/ace/worker-html.js | 0 .../framework/ace/worker-javascript.js | 0 .../static}/framework/ace/worker-json.js | 0 .../static}/framework/ace/worker-lua.js | 0 .../static}/framework/ace/worker-php.js | 0 .../static}/framework/ace/worker-xml.js | 0 .../static}/framework/ace/worker-xquery.js | 0 .../static}/framework/custom/css/style.css | 0 .../static}/framework/custom/js/common.js | 0 .../framework/easyui/jquery.easyui.min.js | 0 .../static}/framework/easyui/jquery.min.js | 0 .../framework/easyui/locale/easyui-lang-af.js | 0 .../framework/easyui/locale/easyui-lang-am.js | 0 .../framework/easyui/locale/easyui-lang-ar.js | 0 .../framework/easyui/locale/easyui-lang-bg.js | 0 .../framework/easyui/locale/easyui-lang-ca.js | 0 .../framework/easyui/locale/easyui-lang-cs.js | 0 .../framework/easyui/locale/easyui-lang-cz.js | 0 .../framework/easyui/locale/easyui-lang-da.js | 0 .../framework/easyui/locale/easyui-lang-de.js | 0 .../framework/easyui/locale/easyui-lang-el.js | 0 .../framework/easyui/locale/easyui-lang-en.js | 0 .../framework/easyui/locale/easyui-lang-es.js | 0 .../framework/easyui/locale/easyui-lang-fr.js | 0 .../framework/easyui/locale/easyui-lang-it.js | 0 .../framework/easyui/locale/easyui-lang-jp.js | 0 .../framework/easyui/locale/easyui-lang-ko.js | 0 .../framework/easyui/locale/easyui-lang-nl.js | 0 .../framework/easyui/locale/easyui-lang-pl.js | 0 .../easyui/locale/easyui-lang-pt_BR.js | 0 .../framework/easyui/locale/easyui-lang-ru.js | 0 .../easyui/locale/easyui-lang-sv_SE.js | 0 .../framework/easyui/locale/easyui-lang-tr.js | 0 .../easyui/locale/easyui-lang-zh_CN.js | 0 .../easyui/locale/easyui-lang-zh_TW.js | 0 .../easyui/themes/bootstrap/easyui.css | 0 .../bootstrap/images/accordion_arrows.png | Bin .../easyui/themes/bootstrap/images/blank.gif | Bin .../bootstrap/images/calendar_arrows.png | Bin .../themes/bootstrap/images/combo_arrow.png | Bin .../bootstrap/images/datagrid_icons.png | Bin .../themes/bootstrap/images/datebox_arrow.png | Bin .../themes/bootstrap/images/layout_arrows.png | Bin .../themes/bootstrap/images/linkbutton_bg.png | Bin .../themes/bootstrap/images/loading.gif | Bin .../themes/bootstrap/images/menu_arrows.png | Bin .../bootstrap/images/messager_icons.png | Bin .../bootstrap/images/pagination_icons.png | Bin .../themes/bootstrap/images/panel_tools.png | Bin .../bootstrap/images/searchbox_button.png | Bin .../themes/bootstrap/images/slider_handle.png | Bin .../bootstrap/images/spinner_arrows.png | Bin .../themes/bootstrap/images/tabs_icons.png | Bin .../themes/bootstrap/images/tree_icons.png | Bin .../bootstrap/images/validatebox_warning.png | Bin .../easyui/themes/default/easyui.css | 0 .../default/images/accordion_arrows.png | Bin .../easyui/themes/default/images/blank.gif | Bin .../themes/default/images/calendar_arrows.png | Bin .../themes/default/images/combo_arrow.png | Bin .../themes/default/images/datagrid_icons.png | Bin .../themes/default/images/datebox_arrow.png | Bin .../themes/default/images/layout_arrows.png | Bin .../themes/default/images/linkbutton_bg.png | Bin .../easyui/themes/default/images/loading.gif | Bin .../themes/default/images/menu_arrows.png | Bin .../themes/default/images/messager_icons.png | Bin .../default/images/pagination_icons.png | Bin .../themes/default/images/panel_tools.png | Bin .../default/images/searchbox_button.png | Bin .../themes/default/images/slider_handle.png | Bin .../themes/default/images/spinner_arrows.png | Bin .../themes/default/images/tabs_icons.png | Bin .../themes/default/images/tree_icons.png | Bin .../default/images/validatebox_warning.png | Bin .../static}/framework/easyui/themes/icon.css | 0 .../framework/easyui/themes/icons/back.png | Bin .../framework/easyui/themes/icons/blank.gif | Bin .../framework/easyui/themes/icons/cancel.png | Bin .../framework/easyui/themes/icons/clear.png | Bin .../framework/easyui/themes/icons/cut.png | Bin .../framework/easyui/themes/icons/dir.png | Bin .../easyui/themes/icons/edit_add.png | Bin .../easyui/themes/icons/edit_remove.png | Bin .../framework/easyui/themes/icons/export.png | Bin .../easyui/themes/icons/filesave.png | Bin .../framework/easyui/themes/icons/filter.png | Bin .../framework/easyui/themes/icons/help.png | Bin .../framework/easyui/themes/icons/import.png | Bin .../easyui/themes/icons/large_chart.png | Bin .../easyui/themes/icons/large_clipart.png | Bin .../easyui/themes/icons/large_picture.png | Bin .../easyui/themes/icons/large_shapes.png | Bin .../easyui/themes/icons/large_smartart.png | Bin .../framework/easyui/themes/icons/lock.png | Bin .../framework/easyui/themes/icons/man.png | Bin .../easyui/themes/icons/mini_add.png | Bin .../easyui/themes/icons/mini_edit.png | Bin .../easyui/themes/icons/mini_refresh.png | Bin .../framework/easyui/themes/icons/more.png | Bin .../framework/easyui/themes/icons/no.png | Bin .../framework/easyui/themes/icons/ok.png | Bin .../framework/easyui/themes/icons/path.png | Bin .../framework/easyui/themes/icons/pencil.png | Bin .../framework/easyui/themes/icons/print.png | Bin .../framework/easyui/themes/icons/redo.png | Bin .../framework/easyui/themes/icons/reload.png | Bin .../framework/easyui/themes/icons/search.png | Bin .../framework/easyui/themes/icons/server.png | Bin .../framework/easyui/themes/icons/sum.png | Bin .../framework/easyui/themes/icons/text.png | Bin .../framework/easyui/themes/icons/tip.png | Bin .../framework/easyui/themes/icons/undo.png | Bin .../framework/easyui/themes/icons/user.png | Bin .../framework/easyui/themes/metro/easyui.css | 0 .../themes/metro/images/accordion_arrows.png | Bin .../easyui/themes/metro/images/blank.gif | Bin .../themes/metro/images/calendar_arrows.png | Bin .../themes/metro/images/combo_arrow.png | Bin .../themes/metro/images/datagrid_icons.png | Bin .../themes/metro/images/datebox_arrow.png | Bin .../themes/metro/images/layout_arrows.png | Bin .../themes/metro/images/linkbutton_bg.png | Bin .../easyui/themes/metro/images/loading.gif | Bin .../themes/metro/images/menu_arrows.png | Bin .../themes/metro/images/messager_icons.png | Bin .../themes/metro/images/pagination_icons.png | Bin .../themes/metro/images/panel_tools.png | Bin .../themes/metro/images/searchbox_button.png | Bin .../themes/metro/images/slider_handle.png | Bin .../themes/metro/images/spinner_arrows.png | Bin .../easyui/themes/metro/images/tabs_icons.png | Bin .../easyui/themes/metro/images/tree_icons.png | Bin .../metro/images/validatebox_warning.png | Bin .../assets/static}/framework/favicon.ico | Bin .../framework/jquery/jquery.json-2.2.js | 0 .../static}/framework/js.cookie-2.1.4.min.js | 0 .../assets/static}/framework/logo.png | Bin src/etcdkeeper/main.go | 42 +++++++----------- 471 files changed, 17 insertions(+), 25 deletions(-) rename {assets => src/etcdkeeper/assets/static}/etcdkeeper/index.html (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ace.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-beautify.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-elastic_tabstops_lite.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-emmet.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-error_marker.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-keybinding_menu.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-language_tools.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-linking.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-modelist.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-old_ie.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-searchbox.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-settings_menu.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-spellcheck.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-split.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-static_highlight.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-statusbar.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-textarea.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-themelist.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/ext-whitespace.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/keybinding-emacs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/keybinding-vim.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-abap.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-abc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-actionscript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ada.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-apache_conf.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-applescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-asciidoc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-assembly_x86.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-autohotkey.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-batchfile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-bro.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-c9search.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-c_cpp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-cirru.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-clojure.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-cobol.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-coffee.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-coldfusion.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-csharp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-css.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-curly.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-d.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-dart.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-diff.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-django.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-dockerfile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-dot.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-drools.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-eiffel.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ejs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-elixir.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-elm.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-erlang.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-forth.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-fortran.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ftl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-gcode.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-gherkin.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-gitignore.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-glsl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-gobstones.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-golang.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-groovy.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-haml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-handlebars.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-haskell.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-haskell_cabal.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-haxe.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-hjson.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-html.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-html_elixir.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-html_ruby.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ini.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-io.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-jack.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-jade.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-java.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-javascript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-json.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-jsoniq.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-jsp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-jsx.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-julia.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-kotlin.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-latex.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-less.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-liquid.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-lisp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-livescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-logiql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-lsl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-lua.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-luapage.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-lucene.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-makefile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-markdown.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-mask.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-matlab.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-maze.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-mel.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-mushcode.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-mysql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-nix.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-nsis.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-objectivec.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ocaml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-pascal.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-perl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-pgsql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-php.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-plain_text.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-powershell.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-praat.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-prolog.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-properties.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-protobuf.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-python.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-r.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-razor.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-rdoc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-rhtml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-rst.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-ruby.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-rust.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-sass.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-scad.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-scala.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-scheme.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-scss.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-sh.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-sjs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-smarty.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-snippets.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-soy_template.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-space.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-sql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-sqlserver.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-stylus.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-svg.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-swift.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-tcl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-tex.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-text.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-textile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-toml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-tsx.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-twig.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-typescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-vala.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-vbscript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-velocity.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-verilog.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-vhdl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-wollok.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-xml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-xquery.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/mode-yaml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/abap.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/abc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/actionscript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ada.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/apache_conf.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/applescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/asciidoc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/assembly_x86.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/autohotkey.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/batchfile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/bro.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/c9search.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/c_cpp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/cirru.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/clojure.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/cobol.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/coffee.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/coldfusion.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/csharp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/css.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/curly.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/d.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/dart.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/diff.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/django.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/dockerfile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/dot.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/drools.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/eiffel.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ejs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/elixir.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/elm.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/erlang.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/forth.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/fortran.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ftl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/gcode.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/gherkin.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/gitignore.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/glsl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/gobstones.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/golang.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/groovy.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/haml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/handlebars.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/haskell.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/haskell_cabal.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/haxe.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/hjson.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/html.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/html_elixir.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/html_ruby.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ini.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/io.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/jack.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/jade.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/java.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/javascript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/json.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/jsoniq.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/jsp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/jsx.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/julia.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/kotlin.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/latex.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/less.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/liquid.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/lisp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/livescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/logiql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/lsl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/lua.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/luapage.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/lucene.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/makefile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/markdown.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/mask.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/matlab.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/maze.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/mel.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/mushcode.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/mysql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/nix.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/nsis.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/objectivec.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ocaml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/pascal.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/perl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/pgsql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/php.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/plain_text.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/powershell.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/praat.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/prolog.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/properties.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/protobuf.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/python.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/r.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/razor.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/rdoc.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/rhtml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/rst.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/ruby.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/rust.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/sass.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/scad.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/scala.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/scheme.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/scss.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/sh.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/sjs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/smarty.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/snippets.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/soy_template.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/space.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/sql.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/sqlserver.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/stylus.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/svg.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/swift.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/tcl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/tex.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/text.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/textile.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/toml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/tsx.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/twig.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/typescript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/vala.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/vbscript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/velocity.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/verilog.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/vhdl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/wollok.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/xml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/xquery.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/snippets/yaml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-ambiance.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-chaos.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-chrome.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-clouds.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-clouds_midnight.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-cobalt.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-crimson_editor.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-dawn.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-dreamweaver.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-eclipse.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-github.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-idle_fingers.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-iplastic.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-katzenmilch.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-kr_theme.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-kuroir.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-merbivore.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-merbivore_soft.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-mono_industrial.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-monokai.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-pastel_on_dark.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-solarized_dark.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-solarized_light.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-sqlserver.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-terminal.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-textmate.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-tomorrow.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-tomorrow_night.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-tomorrow_night_blue.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-tomorrow_night_bright.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-tomorrow_night_eighties.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-twilight.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-vibrant_ink.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/theme-xcode.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-coffee.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-css.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-html.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-javascript.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-json.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-lua.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-php.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-xml.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/ace/worker-xquery.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/custom/css/style.css (100%) rename {assets => src/etcdkeeper/assets/static}/framework/custom/js/common.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/jquery.easyui.min.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/jquery.min.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-af.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-am.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-ar.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-bg.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-ca.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-cs.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-cz.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-da.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-de.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-el.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-en.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-es.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-fr.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-it.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-jp.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-ko.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-nl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-pl.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-pt_BR.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-ru.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-sv_SE.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-tr.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-zh_CN.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/locale/easyui-lang-zh_TW.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/easyui.css (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/accordion_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/blank.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/calendar_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/combo_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/datagrid_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/datebox_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/layout_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/linkbutton_bg.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/loading.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/menu_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/messager_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/pagination_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/panel_tools.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/searchbox_button.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/slider_handle.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/spinner_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/tabs_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/tree_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/bootstrap/images/validatebox_warning.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/easyui.css (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/accordion_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/blank.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/calendar_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/combo_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/datagrid_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/datebox_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/layout_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/linkbutton_bg.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/loading.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/menu_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/messager_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/pagination_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/panel_tools.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/searchbox_button.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/slider_handle.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/spinner_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/tabs_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/tree_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/default/images/validatebox_warning.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icon.css (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/back.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/blank.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/cancel.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/clear.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/cut.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/dir.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/edit_add.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/edit_remove.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/export.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/filesave.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/filter.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/help.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/import.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/large_chart.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/large_clipart.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/large_picture.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/large_shapes.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/large_smartart.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/lock.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/man.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/mini_add.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/mini_edit.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/mini_refresh.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/more.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/no.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/ok.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/path.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/pencil.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/print.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/redo.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/reload.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/search.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/server.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/sum.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/text.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/tip.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/undo.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/icons/user.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/easyui.css (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/accordion_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/blank.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/calendar_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/combo_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/datagrid_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/datebox_arrow.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/layout_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/linkbutton_bg.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/loading.gif (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/menu_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/messager_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/pagination_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/panel_tools.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/searchbox_button.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/slider_handle.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/spinner_arrows.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/tabs_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/tree_icons.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/easyui/themes/metro/images/validatebox_warning.png (100%) rename {assets => src/etcdkeeper/assets/static}/framework/favicon.ico (100%) rename {assets => src/etcdkeeper/assets/static}/framework/jquery/jquery.json-2.2.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/js.cookie-2.1.4.min.js (100%) rename {assets => src/etcdkeeper/assets/static}/framework/logo.png (100%) diff --git a/assets/etcdkeeper/index.html b/src/etcdkeeper/assets/static/etcdkeeper/index.html similarity index 100% rename from assets/etcdkeeper/index.html rename to src/etcdkeeper/assets/static/etcdkeeper/index.html diff --git a/assets/framework/ace/ace.js b/src/etcdkeeper/assets/static/framework/ace/ace.js similarity index 100% rename from assets/framework/ace/ace.js rename to src/etcdkeeper/assets/static/framework/ace/ace.js diff --git a/assets/framework/ace/ext-beautify.js b/src/etcdkeeper/assets/static/framework/ace/ext-beautify.js similarity index 100% rename from assets/framework/ace/ext-beautify.js rename to src/etcdkeeper/assets/static/framework/ace/ext-beautify.js diff --git a/assets/framework/ace/ext-elastic_tabstops_lite.js b/src/etcdkeeper/assets/static/framework/ace/ext-elastic_tabstops_lite.js similarity index 100% rename from assets/framework/ace/ext-elastic_tabstops_lite.js rename to src/etcdkeeper/assets/static/framework/ace/ext-elastic_tabstops_lite.js diff --git a/assets/framework/ace/ext-emmet.js b/src/etcdkeeper/assets/static/framework/ace/ext-emmet.js similarity index 100% rename from assets/framework/ace/ext-emmet.js rename to src/etcdkeeper/assets/static/framework/ace/ext-emmet.js diff --git a/assets/framework/ace/ext-error_marker.js b/src/etcdkeeper/assets/static/framework/ace/ext-error_marker.js similarity index 100% rename from assets/framework/ace/ext-error_marker.js rename to src/etcdkeeper/assets/static/framework/ace/ext-error_marker.js diff --git a/assets/framework/ace/ext-keybinding_menu.js b/src/etcdkeeper/assets/static/framework/ace/ext-keybinding_menu.js similarity index 100% rename from assets/framework/ace/ext-keybinding_menu.js rename to src/etcdkeeper/assets/static/framework/ace/ext-keybinding_menu.js diff --git a/assets/framework/ace/ext-language_tools.js b/src/etcdkeeper/assets/static/framework/ace/ext-language_tools.js similarity index 100% rename from assets/framework/ace/ext-language_tools.js rename to src/etcdkeeper/assets/static/framework/ace/ext-language_tools.js diff --git a/assets/framework/ace/ext-linking.js b/src/etcdkeeper/assets/static/framework/ace/ext-linking.js similarity index 100% rename from assets/framework/ace/ext-linking.js rename to src/etcdkeeper/assets/static/framework/ace/ext-linking.js diff --git a/assets/framework/ace/ext-modelist.js b/src/etcdkeeper/assets/static/framework/ace/ext-modelist.js similarity index 100% rename from assets/framework/ace/ext-modelist.js rename to src/etcdkeeper/assets/static/framework/ace/ext-modelist.js diff --git a/assets/framework/ace/ext-old_ie.js b/src/etcdkeeper/assets/static/framework/ace/ext-old_ie.js similarity index 100% rename from assets/framework/ace/ext-old_ie.js rename to src/etcdkeeper/assets/static/framework/ace/ext-old_ie.js diff --git a/assets/framework/ace/ext-searchbox.js b/src/etcdkeeper/assets/static/framework/ace/ext-searchbox.js similarity index 100% rename from assets/framework/ace/ext-searchbox.js rename to src/etcdkeeper/assets/static/framework/ace/ext-searchbox.js diff --git a/assets/framework/ace/ext-settings_menu.js b/src/etcdkeeper/assets/static/framework/ace/ext-settings_menu.js similarity index 100% rename from assets/framework/ace/ext-settings_menu.js rename to src/etcdkeeper/assets/static/framework/ace/ext-settings_menu.js diff --git a/assets/framework/ace/ext-spellcheck.js b/src/etcdkeeper/assets/static/framework/ace/ext-spellcheck.js similarity index 100% rename from assets/framework/ace/ext-spellcheck.js rename to src/etcdkeeper/assets/static/framework/ace/ext-spellcheck.js diff --git a/assets/framework/ace/ext-split.js b/src/etcdkeeper/assets/static/framework/ace/ext-split.js similarity index 100% rename from assets/framework/ace/ext-split.js rename to src/etcdkeeper/assets/static/framework/ace/ext-split.js diff --git a/assets/framework/ace/ext-static_highlight.js b/src/etcdkeeper/assets/static/framework/ace/ext-static_highlight.js similarity index 100% rename from assets/framework/ace/ext-static_highlight.js rename to src/etcdkeeper/assets/static/framework/ace/ext-static_highlight.js diff --git a/assets/framework/ace/ext-statusbar.js b/src/etcdkeeper/assets/static/framework/ace/ext-statusbar.js similarity index 100% rename from assets/framework/ace/ext-statusbar.js rename to src/etcdkeeper/assets/static/framework/ace/ext-statusbar.js diff --git a/assets/framework/ace/ext-textarea.js b/src/etcdkeeper/assets/static/framework/ace/ext-textarea.js similarity index 100% rename from assets/framework/ace/ext-textarea.js rename to src/etcdkeeper/assets/static/framework/ace/ext-textarea.js diff --git a/assets/framework/ace/ext-themelist.js b/src/etcdkeeper/assets/static/framework/ace/ext-themelist.js similarity index 100% rename from assets/framework/ace/ext-themelist.js rename to src/etcdkeeper/assets/static/framework/ace/ext-themelist.js diff --git a/assets/framework/ace/ext-whitespace.js b/src/etcdkeeper/assets/static/framework/ace/ext-whitespace.js similarity index 100% rename from assets/framework/ace/ext-whitespace.js rename to src/etcdkeeper/assets/static/framework/ace/ext-whitespace.js diff --git a/assets/framework/ace/keybinding-emacs.js b/src/etcdkeeper/assets/static/framework/ace/keybinding-emacs.js similarity index 100% rename from assets/framework/ace/keybinding-emacs.js rename to src/etcdkeeper/assets/static/framework/ace/keybinding-emacs.js diff --git a/assets/framework/ace/keybinding-vim.js b/src/etcdkeeper/assets/static/framework/ace/keybinding-vim.js similarity index 100% rename from assets/framework/ace/keybinding-vim.js rename to src/etcdkeeper/assets/static/framework/ace/keybinding-vim.js diff --git a/assets/framework/ace/mode-abap.js b/src/etcdkeeper/assets/static/framework/ace/mode-abap.js similarity index 100% rename from assets/framework/ace/mode-abap.js rename to src/etcdkeeper/assets/static/framework/ace/mode-abap.js diff --git a/assets/framework/ace/mode-abc.js b/src/etcdkeeper/assets/static/framework/ace/mode-abc.js similarity index 100% rename from assets/framework/ace/mode-abc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-abc.js diff --git a/assets/framework/ace/mode-actionscript.js b/src/etcdkeeper/assets/static/framework/ace/mode-actionscript.js similarity index 100% rename from assets/framework/ace/mode-actionscript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-actionscript.js diff --git a/assets/framework/ace/mode-ada.js b/src/etcdkeeper/assets/static/framework/ace/mode-ada.js similarity index 100% rename from assets/framework/ace/mode-ada.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ada.js diff --git a/assets/framework/ace/mode-apache_conf.js b/src/etcdkeeper/assets/static/framework/ace/mode-apache_conf.js similarity index 100% rename from assets/framework/ace/mode-apache_conf.js rename to src/etcdkeeper/assets/static/framework/ace/mode-apache_conf.js diff --git a/assets/framework/ace/mode-applescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-applescript.js similarity index 100% rename from assets/framework/ace/mode-applescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-applescript.js diff --git a/assets/framework/ace/mode-asciidoc.js b/src/etcdkeeper/assets/static/framework/ace/mode-asciidoc.js similarity index 100% rename from assets/framework/ace/mode-asciidoc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-asciidoc.js diff --git a/assets/framework/ace/mode-assembly_x86.js b/src/etcdkeeper/assets/static/framework/ace/mode-assembly_x86.js similarity index 100% rename from assets/framework/ace/mode-assembly_x86.js rename to src/etcdkeeper/assets/static/framework/ace/mode-assembly_x86.js diff --git a/assets/framework/ace/mode-autohotkey.js b/src/etcdkeeper/assets/static/framework/ace/mode-autohotkey.js similarity index 100% rename from assets/framework/ace/mode-autohotkey.js rename to src/etcdkeeper/assets/static/framework/ace/mode-autohotkey.js diff --git a/assets/framework/ace/mode-batchfile.js b/src/etcdkeeper/assets/static/framework/ace/mode-batchfile.js similarity index 100% rename from assets/framework/ace/mode-batchfile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-batchfile.js diff --git a/assets/framework/ace/mode-bro.js b/src/etcdkeeper/assets/static/framework/ace/mode-bro.js similarity index 100% rename from assets/framework/ace/mode-bro.js rename to src/etcdkeeper/assets/static/framework/ace/mode-bro.js diff --git a/assets/framework/ace/mode-c9search.js b/src/etcdkeeper/assets/static/framework/ace/mode-c9search.js similarity index 100% rename from assets/framework/ace/mode-c9search.js rename to src/etcdkeeper/assets/static/framework/ace/mode-c9search.js diff --git a/assets/framework/ace/mode-c_cpp.js b/src/etcdkeeper/assets/static/framework/ace/mode-c_cpp.js similarity index 100% rename from assets/framework/ace/mode-c_cpp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-c_cpp.js diff --git a/assets/framework/ace/mode-cirru.js b/src/etcdkeeper/assets/static/framework/ace/mode-cirru.js similarity index 100% rename from assets/framework/ace/mode-cirru.js rename to src/etcdkeeper/assets/static/framework/ace/mode-cirru.js diff --git a/assets/framework/ace/mode-clojure.js b/src/etcdkeeper/assets/static/framework/ace/mode-clojure.js similarity index 100% rename from assets/framework/ace/mode-clojure.js rename to src/etcdkeeper/assets/static/framework/ace/mode-clojure.js diff --git a/assets/framework/ace/mode-cobol.js b/src/etcdkeeper/assets/static/framework/ace/mode-cobol.js similarity index 100% rename from assets/framework/ace/mode-cobol.js rename to src/etcdkeeper/assets/static/framework/ace/mode-cobol.js diff --git a/assets/framework/ace/mode-coffee.js b/src/etcdkeeper/assets/static/framework/ace/mode-coffee.js similarity index 100% rename from assets/framework/ace/mode-coffee.js rename to src/etcdkeeper/assets/static/framework/ace/mode-coffee.js diff --git a/assets/framework/ace/mode-coldfusion.js b/src/etcdkeeper/assets/static/framework/ace/mode-coldfusion.js similarity index 100% rename from assets/framework/ace/mode-coldfusion.js rename to src/etcdkeeper/assets/static/framework/ace/mode-coldfusion.js diff --git a/assets/framework/ace/mode-csharp.js b/src/etcdkeeper/assets/static/framework/ace/mode-csharp.js similarity index 100% rename from assets/framework/ace/mode-csharp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-csharp.js diff --git a/assets/framework/ace/mode-css.js b/src/etcdkeeper/assets/static/framework/ace/mode-css.js similarity index 100% rename from assets/framework/ace/mode-css.js rename to src/etcdkeeper/assets/static/framework/ace/mode-css.js diff --git a/assets/framework/ace/mode-curly.js b/src/etcdkeeper/assets/static/framework/ace/mode-curly.js similarity index 100% rename from assets/framework/ace/mode-curly.js rename to src/etcdkeeper/assets/static/framework/ace/mode-curly.js diff --git a/assets/framework/ace/mode-d.js b/src/etcdkeeper/assets/static/framework/ace/mode-d.js similarity index 100% rename from assets/framework/ace/mode-d.js rename to src/etcdkeeper/assets/static/framework/ace/mode-d.js diff --git a/assets/framework/ace/mode-dart.js b/src/etcdkeeper/assets/static/framework/ace/mode-dart.js similarity index 100% rename from assets/framework/ace/mode-dart.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dart.js diff --git a/assets/framework/ace/mode-diff.js b/src/etcdkeeper/assets/static/framework/ace/mode-diff.js similarity index 100% rename from assets/framework/ace/mode-diff.js rename to src/etcdkeeper/assets/static/framework/ace/mode-diff.js diff --git a/assets/framework/ace/mode-django.js b/src/etcdkeeper/assets/static/framework/ace/mode-django.js similarity index 100% rename from assets/framework/ace/mode-django.js rename to src/etcdkeeper/assets/static/framework/ace/mode-django.js diff --git a/assets/framework/ace/mode-dockerfile.js b/src/etcdkeeper/assets/static/framework/ace/mode-dockerfile.js similarity index 100% rename from assets/framework/ace/mode-dockerfile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dockerfile.js diff --git a/assets/framework/ace/mode-dot.js b/src/etcdkeeper/assets/static/framework/ace/mode-dot.js similarity index 100% rename from assets/framework/ace/mode-dot.js rename to src/etcdkeeper/assets/static/framework/ace/mode-dot.js diff --git a/assets/framework/ace/mode-drools.js b/src/etcdkeeper/assets/static/framework/ace/mode-drools.js similarity index 100% rename from assets/framework/ace/mode-drools.js rename to src/etcdkeeper/assets/static/framework/ace/mode-drools.js diff --git a/assets/framework/ace/mode-eiffel.js b/src/etcdkeeper/assets/static/framework/ace/mode-eiffel.js similarity index 100% rename from assets/framework/ace/mode-eiffel.js rename to src/etcdkeeper/assets/static/framework/ace/mode-eiffel.js diff --git a/assets/framework/ace/mode-ejs.js b/src/etcdkeeper/assets/static/framework/ace/mode-ejs.js similarity index 100% rename from assets/framework/ace/mode-ejs.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ejs.js diff --git a/assets/framework/ace/mode-elixir.js b/src/etcdkeeper/assets/static/framework/ace/mode-elixir.js similarity index 100% rename from assets/framework/ace/mode-elixir.js rename to src/etcdkeeper/assets/static/framework/ace/mode-elixir.js diff --git a/assets/framework/ace/mode-elm.js b/src/etcdkeeper/assets/static/framework/ace/mode-elm.js similarity index 100% rename from assets/framework/ace/mode-elm.js rename to src/etcdkeeper/assets/static/framework/ace/mode-elm.js diff --git a/assets/framework/ace/mode-erlang.js b/src/etcdkeeper/assets/static/framework/ace/mode-erlang.js similarity index 100% rename from assets/framework/ace/mode-erlang.js rename to src/etcdkeeper/assets/static/framework/ace/mode-erlang.js diff --git a/assets/framework/ace/mode-forth.js b/src/etcdkeeper/assets/static/framework/ace/mode-forth.js similarity index 100% rename from assets/framework/ace/mode-forth.js rename to src/etcdkeeper/assets/static/framework/ace/mode-forth.js diff --git a/assets/framework/ace/mode-fortran.js b/src/etcdkeeper/assets/static/framework/ace/mode-fortran.js similarity index 100% rename from assets/framework/ace/mode-fortran.js rename to src/etcdkeeper/assets/static/framework/ace/mode-fortran.js diff --git a/assets/framework/ace/mode-ftl.js b/src/etcdkeeper/assets/static/framework/ace/mode-ftl.js similarity index 100% rename from assets/framework/ace/mode-ftl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ftl.js diff --git a/assets/framework/ace/mode-gcode.js b/src/etcdkeeper/assets/static/framework/ace/mode-gcode.js similarity index 100% rename from assets/framework/ace/mode-gcode.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gcode.js diff --git a/assets/framework/ace/mode-gherkin.js b/src/etcdkeeper/assets/static/framework/ace/mode-gherkin.js similarity index 100% rename from assets/framework/ace/mode-gherkin.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gherkin.js diff --git a/assets/framework/ace/mode-gitignore.js b/src/etcdkeeper/assets/static/framework/ace/mode-gitignore.js similarity index 100% rename from assets/framework/ace/mode-gitignore.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gitignore.js diff --git a/assets/framework/ace/mode-glsl.js b/src/etcdkeeper/assets/static/framework/ace/mode-glsl.js similarity index 100% rename from assets/framework/ace/mode-glsl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-glsl.js diff --git a/assets/framework/ace/mode-gobstones.js b/src/etcdkeeper/assets/static/framework/ace/mode-gobstones.js similarity index 100% rename from assets/framework/ace/mode-gobstones.js rename to src/etcdkeeper/assets/static/framework/ace/mode-gobstones.js diff --git a/assets/framework/ace/mode-golang.js b/src/etcdkeeper/assets/static/framework/ace/mode-golang.js similarity index 100% rename from assets/framework/ace/mode-golang.js rename to src/etcdkeeper/assets/static/framework/ace/mode-golang.js diff --git a/assets/framework/ace/mode-groovy.js b/src/etcdkeeper/assets/static/framework/ace/mode-groovy.js similarity index 100% rename from assets/framework/ace/mode-groovy.js rename to src/etcdkeeper/assets/static/framework/ace/mode-groovy.js diff --git a/assets/framework/ace/mode-haml.js b/src/etcdkeeper/assets/static/framework/ace/mode-haml.js similarity index 100% rename from assets/framework/ace/mode-haml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haml.js diff --git a/assets/framework/ace/mode-handlebars.js b/src/etcdkeeper/assets/static/framework/ace/mode-handlebars.js similarity index 100% rename from assets/framework/ace/mode-handlebars.js rename to src/etcdkeeper/assets/static/framework/ace/mode-handlebars.js diff --git a/assets/framework/ace/mode-haskell.js b/src/etcdkeeper/assets/static/framework/ace/mode-haskell.js similarity index 100% rename from assets/framework/ace/mode-haskell.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haskell.js diff --git a/assets/framework/ace/mode-haskell_cabal.js b/src/etcdkeeper/assets/static/framework/ace/mode-haskell_cabal.js similarity index 100% rename from assets/framework/ace/mode-haskell_cabal.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haskell_cabal.js diff --git a/assets/framework/ace/mode-haxe.js b/src/etcdkeeper/assets/static/framework/ace/mode-haxe.js similarity index 100% rename from assets/framework/ace/mode-haxe.js rename to src/etcdkeeper/assets/static/framework/ace/mode-haxe.js diff --git a/assets/framework/ace/mode-hjson.js b/src/etcdkeeper/assets/static/framework/ace/mode-hjson.js similarity index 100% rename from assets/framework/ace/mode-hjson.js rename to src/etcdkeeper/assets/static/framework/ace/mode-hjson.js diff --git a/assets/framework/ace/mode-html.js b/src/etcdkeeper/assets/static/framework/ace/mode-html.js similarity index 100% rename from assets/framework/ace/mode-html.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html.js diff --git a/assets/framework/ace/mode-html_elixir.js b/src/etcdkeeper/assets/static/framework/ace/mode-html_elixir.js similarity index 100% rename from assets/framework/ace/mode-html_elixir.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html_elixir.js diff --git a/assets/framework/ace/mode-html_ruby.js b/src/etcdkeeper/assets/static/framework/ace/mode-html_ruby.js similarity index 100% rename from assets/framework/ace/mode-html_ruby.js rename to src/etcdkeeper/assets/static/framework/ace/mode-html_ruby.js diff --git a/assets/framework/ace/mode-ini.js b/src/etcdkeeper/assets/static/framework/ace/mode-ini.js similarity index 100% rename from assets/framework/ace/mode-ini.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ini.js diff --git a/assets/framework/ace/mode-io.js b/src/etcdkeeper/assets/static/framework/ace/mode-io.js similarity index 100% rename from assets/framework/ace/mode-io.js rename to src/etcdkeeper/assets/static/framework/ace/mode-io.js diff --git a/assets/framework/ace/mode-jack.js b/src/etcdkeeper/assets/static/framework/ace/mode-jack.js similarity index 100% rename from assets/framework/ace/mode-jack.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jack.js diff --git a/assets/framework/ace/mode-jade.js b/src/etcdkeeper/assets/static/framework/ace/mode-jade.js similarity index 100% rename from assets/framework/ace/mode-jade.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jade.js diff --git a/assets/framework/ace/mode-java.js b/src/etcdkeeper/assets/static/framework/ace/mode-java.js similarity index 100% rename from assets/framework/ace/mode-java.js rename to src/etcdkeeper/assets/static/framework/ace/mode-java.js diff --git a/assets/framework/ace/mode-javascript.js b/src/etcdkeeper/assets/static/framework/ace/mode-javascript.js similarity index 100% rename from assets/framework/ace/mode-javascript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-javascript.js diff --git a/assets/framework/ace/mode-json.js b/src/etcdkeeper/assets/static/framework/ace/mode-json.js similarity index 100% rename from assets/framework/ace/mode-json.js rename to src/etcdkeeper/assets/static/framework/ace/mode-json.js diff --git a/assets/framework/ace/mode-jsoniq.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsoniq.js similarity index 100% rename from assets/framework/ace/mode-jsoniq.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsoniq.js diff --git a/assets/framework/ace/mode-jsp.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsp.js similarity index 100% rename from assets/framework/ace/mode-jsp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsp.js diff --git a/assets/framework/ace/mode-jsx.js b/src/etcdkeeper/assets/static/framework/ace/mode-jsx.js similarity index 100% rename from assets/framework/ace/mode-jsx.js rename to src/etcdkeeper/assets/static/framework/ace/mode-jsx.js diff --git a/assets/framework/ace/mode-julia.js b/src/etcdkeeper/assets/static/framework/ace/mode-julia.js similarity index 100% rename from assets/framework/ace/mode-julia.js rename to src/etcdkeeper/assets/static/framework/ace/mode-julia.js diff --git a/assets/framework/ace/mode-kotlin.js b/src/etcdkeeper/assets/static/framework/ace/mode-kotlin.js similarity index 100% rename from assets/framework/ace/mode-kotlin.js rename to src/etcdkeeper/assets/static/framework/ace/mode-kotlin.js diff --git a/assets/framework/ace/mode-latex.js b/src/etcdkeeper/assets/static/framework/ace/mode-latex.js similarity index 100% rename from assets/framework/ace/mode-latex.js rename to src/etcdkeeper/assets/static/framework/ace/mode-latex.js diff --git a/assets/framework/ace/mode-less.js b/src/etcdkeeper/assets/static/framework/ace/mode-less.js similarity index 100% rename from assets/framework/ace/mode-less.js rename to src/etcdkeeper/assets/static/framework/ace/mode-less.js diff --git a/assets/framework/ace/mode-liquid.js b/src/etcdkeeper/assets/static/framework/ace/mode-liquid.js similarity index 100% rename from assets/framework/ace/mode-liquid.js rename to src/etcdkeeper/assets/static/framework/ace/mode-liquid.js diff --git a/assets/framework/ace/mode-lisp.js b/src/etcdkeeper/assets/static/framework/ace/mode-lisp.js similarity index 100% rename from assets/framework/ace/mode-lisp.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lisp.js diff --git a/assets/framework/ace/mode-livescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-livescript.js similarity index 100% rename from assets/framework/ace/mode-livescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-livescript.js diff --git a/assets/framework/ace/mode-logiql.js b/src/etcdkeeper/assets/static/framework/ace/mode-logiql.js similarity index 100% rename from assets/framework/ace/mode-logiql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-logiql.js diff --git a/assets/framework/ace/mode-lsl.js b/src/etcdkeeper/assets/static/framework/ace/mode-lsl.js similarity index 100% rename from assets/framework/ace/mode-lsl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lsl.js diff --git a/assets/framework/ace/mode-lua.js b/src/etcdkeeper/assets/static/framework/ace/mode-lua.js similarity index 100% rename from assets/framework/ace/mode-lua.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lua.js diff --git a/assets/framework/ace/mode-luapage.js b/src/etcdkeeper/assets/static/framework/ace/mode-luapage.js similarity index 100% rename from assets/framework/ace/mode-luapage.js rename to src/etcdkeeper/assets/static/framework/ace/mode-luapage.js diff --git a/assets/framework/ace/mode-lucene.js b/src/etcdkeeper/assets/static/framework/ace/mode-lucene.js similarity index 100% rename from assets/framework/ace/mode-lucene.js rename to src/etcdkeeper/assets/static/framework/ace/mode-lucene.js diff --git a/assets/framework/ace/mode-makefile.js b/src/etcdkeeper/assets/static/framework/ace/mode-makefile.js similarity index 100% rename from assets/framework/ace/mode-makefile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-makefile.js diff --git a/assets/framework/ace/mode-markdown.js b/src/etcdkeeper/assets/static/framework/ace/mode-markdown.js similarity index 100% rename from assets/framework/ace/mode-markdown.js rename to src/etcdkeeper/assets/static/framework/ace/mode-markdown.js diff --git a/assets/framework/ace/mode-mask.js b/src/etcdkeeper/assets/static/framework/ace/mode-mask.js similarity index 100% rename from assets/framework/ace/mode-mask.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mask.js diff --git a/assets/framework/ace/mode-matlab.js b/src/etcdkeeper/assets/static/framework/ace/mode-matlab.js similarity index 100% rename from assets/framework/ace/mode-matlab.js rename to src/etcdkeeper/assets/static/framework/ace/mode-matlab.js diff --git a/assets/framework/ace/mode-maze.js b/src/etcdkeeper/assets/static/framework/ace/mode-maze.js similarity index 100% rename from assets/framework/ace/mode-maze.js rename to src/etcdkeeper/assets/static/framework/ace/mode-maze.js diff --git a/assets/framework/ace/mode-mel.js b/src/etcdkeeper/assets/static/framework/ace/mode-mel.js similarity index 100% rename from assets/framework/ace/mode-mel.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mel.js diff --git a/assets/framework/ace/mode-mushcode.js b/src/etcdkeeper/assets/static/framework/ace/mode-mushcode.js similarity index 100% rename from assets/framework/ace/mode-mushcode.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mushcode.js diff --git a/assets/framework/ace/mode-mysql.js b/src/etcdkeeper/assets/static/framework/ace/mode-mysql.js similarity index 100% rename from assets/framework/ace/mode-mysql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-mysql.js diff --git a/assets/framework/ace/mode-nix.js b/src/etcdkeeper/assets/static/framework/ace/mode-nix.js similarity index 100% rename from assets/framework/ace/mode-nix.js rename to src/etcdkeeper/assets/static/framework/ace/mode-nix.js diff --git a/assets/framework/ace/mode-nsis.js b/src/etcdkeeper/assets/static/framework/ace/mode-nsis.js similarity index 100% rename from assets/framework/ace/mode-nsis.js rename to src/etcdkeeper/assets/static/framework/ace/mode-nsis.js diff --git a/assets/framework/ace/mode-objectivec.js b/src/etcdkeeper/assets/static/framework/ace/mode-objectivec.js similarity index 100% rename from assets/framework/ace/mode-objectivec.js rename to src/etcdkeeper/assets/static/framework/ace/mode-objectivec.js diff --git a/assets/framework/ace/mode-ocaml.js b/src/etcdkeeper/assets/static/framework/ace/mode-ocaml.js similarity index 100% rename from assets/framework/ace/mode-ocaml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ocaml.js diff --git a/assets/framework/ace/mode-pascal.js b/src/etcdkeeper/assets/static/framework/ace/mode-pascal.js similarity index 100% rename from assets/framework/ace/mode-pascal.js rename to src/etcdkeeper/assets/static/framework/ace/mode-pascal.js diff --git a/assets/framework/ace/mode-perl.js b/src/etcdkeeper/assets/static/framework/ace/mode-perl.js similarity index 100% rename from assets/framework/ace/mode-perl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-perl.js diff --git a/assets/framework/ace/mode-pgsql.js b/src/etcdkeeper/assets/static/framework/ace/mode-pgsql.js similarity index 100% rename from assets/framework/ace/mode-pgsql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-pgsql.js diff --git a/assets/framework/ace/mode-php.js b/src/etcdkeeper/assets/static/framework/ace/mode-php.js similarity index 100% rename from assets/framework/ace/mode-php.js rename to src/etcdkeeper/assets/static/framework/ace/mode-php.js diff --git a/assets/framework/ace/mode-plain_text.js b/src/etcdkeeper/assets/static/framework/ace/mode-plain_text.js similarity index 100% rename from assets/framework/ace/mode-plain_text.js rename to src/etcdkeeper/assets/static/framework/ace/mode-plain_text.js diff --git a/assets/framework/ace/mode-powershell.js b/src/etcdkeeper/assets/static/framework/ace/mode-powershell.js similarity index 100% rename from assets/framework/ace/mode-powershell.js rename to src/etcdkeeper/assets/static/framework/ace/mode-powershell.js diff --git a/assets/framework/ace/mode-praat.js b/src/etcdkeeper/assets/static/framework/ace/mode-praat.js similarity index 100% rename from assets/framework/ace/mode-praat.js rename to src/etcdkeeper/assets/static/framework/ace/mode-praat.js diff --git a/assets/framework/ace/mode-prolog.js b/src/etcdkeeper/assets/static/framework/ace/mode-prolog.js similarity index 100% rename from assets/framework/ace/mode-prolog.js rename to src/etcdkeeper/assets/static/framework/ace/mode-prolog.js diff --git a/assets/framework/ace/mode-properties.js b/src/etcdkeeper/assets/static/framework/ace/mode-properties.js similarity index 100% rename from assets/framework/ace/mode-properties.js rename to src/etcdkeeper/assets/static/framework/ace/mode-properties.js diff --git a/assets/framework/ace/mode-protobuf.js b/src/etcdkeeper/assets/static/framework/ace/mode-protobuf.js similarity index 100% rename from assets/framework/ace/mode-protobuf.js rename to src/etcdkeeper/assets/static/framework/ace/mode-protobuf.js diff --git a/assets/framework/ace/mode-python.js b/src/etcdkeeper/assets/static/framework/ace/mode-python.js similarity index 100% rename from assets/framework/ace/mode-python.js rename to src/etcdkeeper/assets/static/framework/ace/mode-python.js diff --git a/assets/framework/ace/mode-r.js b/src/etcdkeeper/assets/static/framework/ace/mode-r.js similarity index 100% rename from assets/framework/ace/mode-r.js rename to src/etcdkeeper/assets/static/framework/ace/mode-r.js diff --git a/assets/framework/ace/mode-razor.js b/src/etcdkeeper/assets/static/framework/ace/mode-razor.js similarity index 100% rename from assets/framework/ace/mode-razor.js rename to src/etcdkeeper/assets/static/framework/ace/mode-razor.js diff --git a/assets/framework/ace/mode-rdoc.js b/src/etcdkeeper/assets/static/framework/ace/mode-rdoc.js similarity index 100% rename from assets/framework/ace/mode-rdoc.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rdoc.js diff --git a/assets/framework/ace/mode-rhtml.js b/src/etcdkeeper/assets/static/framework/ace/mode-rhtml.js similarity index 100% rename from assets/framework/ace/mode-rhtml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rhtml.js diff --git a/assets/framework/ace/mode-rst.js b/src/etcdkeeper/assets/static/framework/ace/mode-rst.js similarity index 100% rename from assets/framework/ace/mode-rst.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rst.js diff --git a/assets/framework/ace/mode-ruby.js b/src/etcdkeeper/assets/static/framework/ace/mode-ruby.js similarity index 100% rename from assets/framework/ace/mode-ruby.js rename to src/etcdkeeper/assets/static/framework/ace/mode-ruby.js diff --git a/assets/framework/ace/mode-rust.js b/src/etcdkeeper/assets/static/framework/ace/mode-rust.js similarity index 100% rename from assets/framework/ace/mode-rust.js rename to src/etcdkeeper/assets/static/framework/ace/mode-rust.js diff --git a/assets/framework/ace/mode-sass.js b/src/etcdkeeper/assets/static/framework/ace/mode-sass.js similarity index 100% rename from assets/framework/ace/mode-sass.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sass.js diff --git a/assets/framework/ace/mode-scad.js b/src/etcdkeeper/assets/static/framework/ace/mode-scad.js similarity index 100% rename from assets/framework/ace/mode-scad.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scad.js diff --git a/assets/framework/ace/mode-scala.js b/src/etcdkeeper/assets/static/framework/ace/mode-scala.js similarity index 100% rename from assets/framework/ace/mode-scala.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scala.js diff --git a/assets/framework/ace/mode-scheme.js b/src/etcdkeeper/assets/static/framework/ace/mode-scheme.js similarity index 100% rename from assets/framework/ace/mode-scheme.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scheme.js diff --git a/assets/framework/ace/mode-scss.js b/src/etcdkeeper/assets/static/framework/ace/mode-scss.js similarity index 100% rename from assets/framework/ace/mode-scss.js rename to src/etcdkeeper/assets/static/framework/ace/mode-scss.js diff --git a/assets/framework/ace/mode-sh.js b/src/etcdkeeper/assets/static/framework/ace/mode-sh.js similarity index 100% rename from assets/framework/ace/mode-sh.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sh.js diff --git a/assets/framework/ace/mode-sjs.js b/src/etcdkeeper/assets/static/framework/ace/mode-sjs.js similarity index 100% rename from assets/framework/ace/mode-sjs.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sjs.js diff --git a/assets/framework/ace/mode-smarty.js b/src/etcdkeeper/assets/static/framework/ace/mode-smarty.js similarity index 100% rename from assets/framework/ace/mode-smarty.js rename to src/etcdkeeper/assets/static/framework/ace/mode-smarty.js diff --git a/assets/framework/ace/mode-snippets.js b/src/etcdkeeper/assets/static/framework/ace/mode-snippets.js similarity index 100% rename from assets/framework/ace/mode-snippets.js rename to src/etcdkeeper/assets/static/framework/ace/mode-snippets.js diff --git a/assets/framework/ace/mode-soy_template.js b/src/etcdkeeper/assets/static/framework/ace/mode-soy_template.js similarity index 100% rename from assets/framework/ace/mode-soy_template.js rename to src/etcdkeeper/assets/static/framework/ace/mode-soy_template.js diff --git a/assets/framework/ace/mode-space.js b/src/etcdkeeper/assets/static/framework/ace/mode-space.js similarity index 100% rename from assets/framework/ace/mode-space.js rename to src/etcdkeeper/assets/static/framework/ace/mode-space.js diff --git a/assets/framework/ace/mode-sql.js b/src/etcdkeeper/assets/static/framework/ace/mode-sql.js similarity index 100% rename from assets/framework/ace/mode-sql.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sql.js diff --git a/assets/framework/ace/mode-sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/mode-sqlserver.js similarity index 100% rename from assets/framework/ace/mode-sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/mode-sqlserver.js diff --git a/assets/framework/ace/mode-stylus.js b/src/etcdkeeper/assets/static/framework/ace/mode-stylus.js similarity index 100% rename from assets/framework/ace/mode-stylus.js rename to src/etcdkeeper/assets/static/framework/ace/mode-stylus.js diff --git a/assets/framework/ace/mode-svg.js b/src/etcdkeeper/assets/static/framework/ace/mode-svg.js similarity index 100% rename from assets/framework/ace/mode-svg.js rename to src/etcdkeeper/assets/static/framework/ace/mode-svg.js diff --git a/assets/framework/ace/mode-swift.js b/src/etcdkeeper/assets/static/framework/ace/mode-swift.js similarity index 100% rename from assets/framework/ace/mode-swift.js rename to src/etcdkeeper/assets/static/framework/ace/mode-swift.js diff --git a/assets/framework/ace/mode-tcl.js b/src/etcdkeeper/assets/static/framework/ace/mode-tcl.js similarity index 100% rename from assets/framework/ace/mode-tcl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tcl.js diff --git a/assets/framework/ace/mode-tex.js b/src/etcdkeeper/assets/static/framework/ace/mode-tex.js similarity index 100% rename from assets/framework/ace/mode-tex.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tex.js diff --git a/assets/framework/ace/mode-text.js b/src/etcdkeeper/assets/static/framework/ace/mode-text.js similarity index 100% rename from assets/framework/ace/mode-text.js rename to src/etcdkeeper/assets/static/framework/ace/mode-text.js diff --git a/assets/framework/ace/mode-textile.js b/src/etcdkeeper/assets/static/framework/ace/mode-textile.js similarity index 100% rename from assets/framework/ace/mode-textile.js rename to src/etcdkeeper/assets/static/framework/ace/mode-textile.js diff --git a/assets/framework/ace/mode-toml.js b/src/etcdkeeper/assets/static/framework/ace/mode-toml.js similarity index 100% rename from assets/framework/ace/mode-toml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-toml.js diff --git a/assets/framework/ace/mode-tsx.js b/src/etcdkeeper/assets/static/framework/ace/mode-tsx.js similarity index 100% rename from assets/framework/ace/mode-tsx.js rename to src/etcdkeeper/assets/static/framework/ace/mode-tsx.js diff --git a/assets/framework/ace/mode-twig.js b/src/etcdkeeper/assets/static/framework/ace/mode-twig.js similarity index 100% rename from assets/framework/ace/mode-twig.js rename to src/etcdkeeper/assets/static/framework/ace/mode-twig.js diff --git a/assets/framework/ace/mode-typescript.js b/src/etcdkeeper/assets/static/framework/ace/mode-typescript.js similarity index 100% rename from assets/framework/ace/mode-typescript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-typescript.js diff --git a/assets/framework/ace/mode-vala.js b/src/etcdkeeper/assets/static/framework/ace/mode-vala.js similarity index 100% rename from assets/framework/ace/mode-vala.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vala.js diff --git a/assets/framework/ace/mode-vbscript.js b/src/etcdkeeper/assets/static/framework/ace/mode-vbscript.js similarity index 100% rename from assets/framework/ace/mode-vbscript.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vbscript.js diff --git a/assets/framework/ace/mode-velocity.js b/src/etcdkeeper/assets/static/framework/ace/mode-velocity.js similarity index 100% rename from assets/framework/ace/mode-velocity.js rename to src/etcdkeeper/assets/static/framework/ace/mode-velocity.js diff --git a/assets/framework/ace/mode-verilog.js b/src/etcdkeeper/assets/static/framework/ace/mode-verilog.js similarity index 100% rename from assets/framework/ace/mode-verilog.js rename to src/etcdkeeper/assets/static/framework/ace/mode-verilog.js diff --git a/assets/framework/ace/mode-vhdl.js b/src/etcdkeeper/assets/static/framework/ace/mode-vhdl.js similarity index 100% rename from assets/framework/ace/mode-vhdl.js rename to src/etcdkeeper/assets/static/framework/ace/mode-vhdl.js diff --git a/assets/framework/ace/mode-wollok.js b/src/etcdkeeper/assets/static/framework/ace/mode-wollok.js similarity index 100% rename from assets/framework/ace/mode-wollok.js rename to src/etcdkeeper/assets/static/framework/ace/mode-wollok.js diff --git a/assets/framework/ace/mode-xml.js b/src/etcdkeeper/assets/static/framework/ace/mode-xml.js similarity index 100% rename from assets/framework/ace/mode-xml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-xml.js diff --git a/assets/framework/ace/mode-xquery.js b/src/etcdkeeper/assets/static/framework/ace/mode-xquery.js similarity index 100% rename from assets/framework/ace/mode-xquery.js rename to src/etcdkeeper/assets/static/framework/ace/mode-xquery.js diff --git a/assets/framework/ace/mode-yaml.js b/src/etcdkeeper/assets/static/framework/ace/mode-yaml.js similarity index 100% rename from assets/framework/ace/mode-yaml.js rename to src/etcdkeeper/assets/static/framework/ace/mode-yaml.js diff --git a/assets/framework/ace/snippets/abap.js b/src/etcdkeeper/assets/static/framework/ace/snippets/abap.js similarity index 100% rename from assets/framework/ace/snippets/abap.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/abap.js diff --git a/assets/framework/ace/snippets/abc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/abc.js similarity index 100% rename from assets/framework/ace/snippets/abc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/abc.js diff --git a/assets/framework/ace/snippets/actionscript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/actionscript.js similarity index 100% rename from assets/framework/ace/snippets/actionscript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/actionscript.js diff --git a/assets/framework/ace/snippets/ada.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ada.js similarity index 100% rename from assets/framework/ace/snippets/ada.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ada.js diff --git a/assets/framework/ace/snippets/apache_conf.js b/src/etcdkeeper/assets/static/framework/ace/snippets/apache_conf.js similarity index 100% rename from assets/framework/ace/snippets/apache_conf.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/apache_conf.js diff --git a/assets/framework/ace/snippets/applescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/applescript.js similarity index 100% rename from assets/framework/ace/snippets/applescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/applescript.js diff --git a/assets/framework/ace/snippets/asciidoc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/asciidoc.js similarity index 100% rename from assets/framework/ace/snippets/asciidoc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/asciidoc.js diff --git a/assets/framework/ace/snippets/assembly_x86.js b/src/etcdkeeper/assets/static/framework/ace/snippets/assembly_x86.js similarity index 100% rename from assets/framework/ace/snippets/assembly_x86.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/assembly_x86.js diff --git a/assets/framework/ace/snippets/autohotkey.js b/src/etcdkeeper/assets/static/framework/ace/snippets/autohotkey.js similarity index 100% rename from assets/framework/ace/snippets/autohotkey.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/autohotkey.js diff --git a/assets/framework/ace/snippets/batchfile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/batchfile.js similarity index 100% rename from assets/framework/ace/snippets/batchfile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/batchfile.js diff --git a/assets/framework/ace/snippets/bro.js b/src/etcdkeeper/assets/static/framework/ace/snippets/bro.js similarity index 100% rename from assets/framework/ace/snippets/bro.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/bro.js diff --git a/assets/framework/ace/snippets/c9search.js b/src/etcdkeeper/assets/static/framework/ace/snippets/c9search.js similarity index 100% rename from assets/framework/ace/snippets/c9search.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/c9search.js diff --git a/assets/framework/ace/snippets/c_cpp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/c_cpp.js similarity index 100% rename from assets/framework/ace/snippets/c_cpp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/c_cpp.js diff --git a/assets/framework/ace/snippets/cirru.js b/src/etcdkeeper/assets/static/framework/ace/snippets/cirru.js similarity index 100% rename from assets/framework/ace/snippets/cirru.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/cirru.js diff --git a/assets/framework/ace/snippets/clojure.js b/src/etcdkeeper/assets/static/framework/ace/snippets/clojure.js similarity index 100% rename from assets/framework/ace/snippets/clojure.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/clojure.js diff --git a/assets/framework/ace/snippets/cobol.js b/src/etcdkeeper/assets/static/framework/ace/snippets/cobol.js similarity index 100% rename from assets/framework/ace/snippets/cobol.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/cobol.js diff --git a/assets/framework/ace/snippets/coffee.js b/src/etcdkeeper/assets/static/framework/ace/snippets/coffee.js similarity index 100% rename from assets/framework/ace/snippets/coffee.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/coffee.js diff --git a/assets/framework/ace/snippets/coldfusion.js b/src/etcdkeeper/assets/static/framework/ace/snippets/coldfusion.js similarity index 100% rename from assets/framework/ace/snippets/coldfusion.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/coldfusion.js diff --git a/assets/framework/ace/snippets/csharp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/csharp.js similarity index 100% rename from assets/framework/ace/snippets/csharp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/csharp.js diff --git a/assets/framework/ace/snippets/css.js b/src/etcdkeeper/assets/static/framework/ace/snippets/css.js similarity index 100% rename from assets/framework/ace/snippets/css.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/css.js diff --git a/assets/framework/ace/snippets/curly.js b/src/etcdkeeper/assets/static/framework/ace/snippets/curly.js similarity index 100% rename from assets/framework/ace/snippets/curly.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/curly.js diff --git a/assets/framework/ace/snippets/d.js b/src/etcdkeeper/assets/static/framework/ace/snippets/d.js similarity index 100% rename from assets/framework/ace/snippets/d.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/d.js diff --git a/assets/framework/ace/snippets/dart.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dart.js similarity index 100% rename from assets/framework/ace/snippets/dart.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dart.js diff --git a/assets/framework/ace/snippets/diff.js b/src/etcdkeeper/assets/static/framework/ace/snippets/diff.js similarity index 100% rename from assets/framework/ace/snippets/diff.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/diff.js diff --git a/assets/framework/ace/snippets/django.js b/src/etcdkeeper/assets/static/framework/ace/snippets/django.js similarity index 100% rename from assets/framework/ace/snippets/django.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/django.js diff --git a/assets/framework/ace/snippets/dockerfile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dockerfile.js similarity index 100% rename from assets/framework/ace/snippets/dockerfile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dockerfile.js diff --git a/assets/framework/ace/snippets/dot.js b/src/etcdkeeper/assets/static/framework/ace/snippets/dot.js similarity index 100% rename from assets/framework/ace/snippets/dot.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/dot.js diff --git a/assets/framework/ace/snippets/drools.js b/src/etcdkeeper/assets/static/framework/ace/snippets/drools.js similarity index 100% rename from assets/framework/ace/snippets/drools.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/drools.js diff --git a/assets/framework/ace/snippets/eiffel.js b/src/etcdkeeper/assets/static/framework/ace/snippets/eiffel.js similarity index 100% rename from assets/framework/ace/snippets/eiffel.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/eiffel.js diff --git a/assets/framework/ace/snippets/ejs.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ejs.js similarity index 100% rename from assets/framework/ace/snippets/ejs.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ejs.js diff --git a/assets/framework/ace/snippets/elixir.js b/src/etcdkeeper/assets/static/framework/ace/snippets/elixir.js similarity index 100% rename from assets/framework/ace/snippets/elixir.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/elixir.js diff --git a/assets/framework/ace/snippets/elm.js b/src/etcdkeeper/assets/static/framework/ace/snippets/elm.js similarity index 100% rename from assets/framework/ace/snippets/elm.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/elm.js diff --git a/assets/framework/ace/snippets/erlang.js b/src/etcdkeeper/assets/static/framework/ace/snippets/erlang.js similarity index 100% rename from assets/framework/ace/snippets/erlang.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/erlang.js diff --git a/assets/framework/ace/snippets/forth.js b/src/etcdkeeper/assets/static/framework/ace/snippets/forth.js similarity index 100% rename from assets/framework/ace/snippets/forth.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/forth.js diff --git a/assets/framework/ace/snippets/fortran.js b/src/etcdkeeper/assets/static/framework/ace/snippets/fortran.js similarity index 100% rename from assets/framework/ace/snippets/fortran.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/fortran.js diff --git a/assets/framework/ace/snippets/ftl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ftl.js similarity index 100% rename from assets/framework/ace/snippets/ftl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ftl.js diff --git a/assets/framework/ace/snippets/gcode.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gcode.js similarity index 100% rename from assets/framework/ace/snippets/gcode.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gcode.js diff --git a/assets/framework/ace/snippets/gherkin.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gherkin.js similarity index 100% rename from assets/framework/ace/snippets/gherkin.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gherkin.js diff --git a/assets/framework/ace/snippets/gitignore.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gitignore.js similarity index 100% rename from assets/framework/ace/snippets/gitignore.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gitignore.js diff --git a/assets/framework/ace/snippets/glsl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/glsl.js similarity index 100% rename from assets/framework/ace/snippets/glsl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/glsl.js diff --git a/assets/framework/ace/snippets/gobstones.js b/src/etcdkeeper/assets/static/framework/ace/snippets/gobstones.js similarity index 100% rename from assets/framework/ace/snippets/gobstones.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/gobstones.js diff --git a/assets/framework/ace/snippets/golang.js b/src/etcdkeeper/assets/static/framework/ace/snippets/golang.js similarity index 100% rename from assets/framework/ace/snippets/golang.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/golang.js diff --git a/assets/framework/ace/snippets/groovy.js b/src/etcdkeeper/assets/static/framework/ace/snippets/groovy.js similarity index 100% rename from assets/framework/ace/snippets/groovy.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/groovy.js diff --git a/assets/framework/ace/snippets/haml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haml.js similarity index 100% rename from assets/framework/ace/snippets/haml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haml.js diff --git a/assets/framework/ace/snippets/handlebars.js b/src/etcdkeeper/assets/static/framework/ace/snippets/handlebars.js similarity index 100% rename from assets/framework/ace/snippets/handlebars.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/handlebars.js diff --git a/assets/framework/ace/snippets/haskell.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haskell.js similarity index 100% rename from assets/framework/ace/snippets/haskell.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haskell.js diff --git a/assets/framework/ace/snippets/haskell_cabal.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haskell_cabal.js similarity index 100% rename from assets/framework/ace/snippets/haskell_cabal.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haskell_cabal.js diff --git a/assets/framework/ace/snippets/haxe.js b/src/etcdkeeper/assets/static/framework/ace/snippets/haxe.js similarity index 100% rename from assets/framework/ace/snippets/haxe.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/haxe.js diff --git a/assets/framework/ace/snippets/hjson.js b/src/etcdkeeper/assets/static/framework/ace/snippets/hjson.js similarity index 100% rename from assets/framework/ace/snippets/hjson.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/hjson.js diff --git a/assets/framework/ace/snippets/html.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html.js similarity index 100% rename from assets/framework/ace/snippets/html.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html.js diff --git a/assets/framework/ace/snippets/html_elixir.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html_elixir.js similarity index 100% rename from assets/framework/ace/snippets/html_elixir.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html_elixir.js diff --git a/assets/framework/ace/snippets/html_ruby.js b/src/etcdkeeper/assets/static/framework/ace/snippets/html_ruby.js similarity index 100% rename from assets/framework/ace/snippets/html_ruby.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/html_ruby.js diff --git a/assets/framework/ace/snippets/ini.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ini.js similarity index 100% rename from assets/framework/ace/snippets/ini.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ini.js diff --git a/assets/framework/ace/snippets/io.js b/src/etcdkeeper/assets/static/framework/ace/snippets/io.js similarity index 100% rename from assets/framework/ace/snippets/io.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/io.js diff --git a/assets/framework/ace/snippets/jack.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jack.js similarity index 100% rename from assets/framework/ace/snippets/jack.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jack.js diff --git a/assets/framework/ace/snippets/jade.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jade.js similarity index 100% rename from assets/framework/ace/snippets/jade.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jade.js diff --git a/assets/framework/ace/snippets/java.js b/src/etcdkeeper/assets/static/framework/ace/snippets/java.js similarity index 100% rename from assets/framework/ace/snippets/java.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/java.js diff --git a/assets/framework/ace/snippets/javascript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/javascript.js similarity index 100% rename from assets/framework/ace/snippets/javascript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/javascript.js diff --git a/assets/framework/ace/snippets/json.js b/src/etcdkeeper/assets/static/framework/ace/snippets/json.js similarity index 100% rename from assets/framework/ace/snippets/json.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/json.js diff --git a/assets/framework/ace/snippets/jsoniq.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsoniq.js similarity index 100% rename from assets/framework/ace/snippets/jsoniq.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsoniq.js diff --git a/assets/framework/ace/snippets/jsp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsp.js similarity index 100% rename from assets/framework/ace/snippets/jsp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsp.js diff --git a/assets/framework/ace/snippets/jsx.js b/src/etcdkeeper/assets/static/framework/ace/snippets/jsx.js similarity index 100% rename from assets/framework/ace/snippets/jsx.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/jsx.js diff --git a/assets/framework/ace/snippets/julia.js b/src/etcdkeeper/assets/static/framework/ace/snippets/julia.js similarity index 100% rename from assets/framework/ace/snippets/julia.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/julia.js diff --git a/assets/framework/ace/snippets/kotlin.js b/src/etcdkeeper/assets/static/framework/ace/snippets/kotlin.js similarity index 100% rename from assets/framework/ace/snippets/kotlin.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/kotlin.js diff --git a/assets/framework/ace/snippets/latex.js b/src/etcdkeeper/assets/static/framework/ace/snippets/latex.js similarity index 100% rename from assets/framework/ace/snippets/latex.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/latex.js diff --git a/assets/framework/ace/snippets/less.js b/src/etcdkeeper/assets/static/framework/ace/snippets/less.js similarity index 100% rename from assets/framework/ace/snippets/less.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/less.js diff --git a/assets/framework/ace/snippets/liquid.js b/src/etcdkeeper/assets/static/framework/ace/snippets/liquid.js similarity index 100% rename from assets/framework/ace/snippets/liquid.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/liquid.js diff --git a/assets/framework/ace/snippets/lisp.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lisp.js similarity index 100% rename from assets/framework/ace/snippets/lisp.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lisp.js diff --git a/assets/framework/ace/snippets/livescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/livescript.js similarity index 100% rename from assets/framework/ace/snippets/livescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/livescript.js diff --git a/assets/framework/ace/snippets/logiql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/logiql.js similarity index 100% rename from assets/framework/ace/snippets/logiql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/logiql.js diff --git a/assets/framework/ace/snippets/lsl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lsl.js similarity index 100% rename from assets/framework/ace/snippets/lsl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lsl.js diff --git a/assets/framework/ace/snippets/lua.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lua.js similarity index 100% rename from assets/framework/ace/snippets/lua.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lua.js diff --git a/assets/framework/ace/snippets/luapage.js b/src/etcdkeeper/assets/static/framework/ace/snippets/luapage.js similarity index 100% rename from assets/framework/ace/snippets/luapage.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/luapage.js diff --git a/assets/framework/ace/snippets/lucene.js b/src/etcdkeeper/assets/static/framework/ace/snippets/lucene.js similarity index 100% rename from assets/framework/ace/snippets/lucene.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/lucene.js diff --git a/assets/framework/ace/snippets/makefile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/makefile.js similarity index 100% rename from assets/framework/ace/snippets/makefile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/makefile.js diff --git a/assets/framework/ace/snippets/markdown.js b/src/etcdkeeper/assets/static/framework/ace/snippets/markdown.js similarity index 100% rename from assets/framework/ace/snippets/markdown.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/markdown.js diff --git a/assets/framework/ace/snippets/mask.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mask.js similarity index 100% rename from assets/framework/ace/snippets/mask.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mask.js diff --git a/assets/framework/ace/snippets/matlab.js b/src/etcdkeeper/assets/static/framework/ace/snippets/matlab.js similarity index 100% rename from assets/framework/ace/snippets/matlab.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/matlab.js diff --git a/assets/framework/ace/snippets/maze.js b/src/etcdkeeper/assets/static/framework/ace/snippets/maze.js similarity index 100% rename from assets/framework/ace/snippets/maze.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/maze.js diff --git a/assets/framework/ace/snippets/mel.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mel.js similarity index 100% rename from assets/framework/ace/snippets/mel.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mel.js diff --git a/assets/framework/ace/snippets/mushcode.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mushcode.js similarity index 100% rename from assets/framework/ace/snippets/mushcode.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mushcode.js diff --git a/assets/framework/ace/snippets/mysql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/mysql.js similarity index 100% rename from assets/framework/ace/snippets/mysql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/mysql.js diff --git a/assets/framework/ace/snippets/nix.js b/src/etcdkeeper/assets/static/framework/ace/snippets/nix.js similarity index 100% rename from assets/framework/ace/snippets/nix.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/nix.js diff --git a/assets/framework/ace/snippets/nsis.js b/src/etcdkeeper/assets/static/framework/ace/snippets/nsis.js similarity index 100% rename from assets/framework/ace/snippets/nsis.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/nsis.js diff --git a/assets/framework/ace/snippets/objectivec.js b/src/etcdkeeper/assets/static/framework/ace/snippets/objectivec.js similarity index 100% rename from assets/framework/ace/snippets/objectivec.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/objectivec.js diff --git a/assets/framework/ace/snippets/ocaml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ocaml.js similarity index 100% rename from assets/framework/ace/snippets/ocaml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ocaml.js diff --git a/assets/framework/ace/snippets/pascal.js b/src/etcdkeeper/assets/static/framework/ace/snippets/pascal.js similarity index 100% rename from assets/framework/ace/snippets/pascal.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/pascal.js diff --git a/assets/framework/ace/snippets/perl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/perl.js similarity index 100% rename from assets/framework/ace/snippets/perl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/perl.js diff --git a/assets/framework/ace/snippets/pgsql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/pgsql.js similarity index 100% rename from assets/framework/ace/snippets/pgsql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/pgsql.js diff --git a/assets/framework/ace/snippets/php.js b/src/etcdkeeper/assets/static/framework/ace/snippets/php.js similarity index 100% rename from assets/framework/ace/snippets/php.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/php.js diff --git a/assets/framework/ace/snippets/plain_text.js b/src/etcdkeeper/assets/static/framework/ace/snippets/plain_text.js similarity index 100% rename from assets/framework/ace/snippets/plain_text.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/plain_text.js diff --git a/assets/framework/ace/snippets/powershell.js b/src/etcdkeeper/assets/static/framework/ace/snippets/powershell.js similarity index 100% rename from assets/framework/ace/snippets/powershell.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/powershell.js diff --git a/assets/framework/ace/snippets/praat.js b/src/etcdkeeper/assets/static/framework/ace/snippets/praat.js similarity index 100% rename from assets/framework/ace/snippets/praat.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/praat.js diff --git a/assets/framework/ace/snippets/prolog.js b/src/etcdkeeper/assets/static/framework/ace/snippets/prolog.js similarity index 100% rename from assets/framework/ace/snippets/prolog.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/prolog.js diff --git a/assets/framework/ace/snippets/properties.js b/src/etcdkeeper/assets/static/framework/ace/snippets/properties.js similarity index 100% rename from assets/framework/ace/snippets/properties.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/properties.js diff --git a/assets/framework/ace/snippets/protobuf.js b/src/etcdkeeper/assets/static/framework/ace/snippets/protobuf.js similarity index 100% rename from assets/framework/ace/snippets/protobuf.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/protobuf.js diff --git a/assets/framework/ace/snippets/python.js b/src/etcdkeeper/assets/static/framework/ace/snippets/python.js similarity index 100% rename from assets/framework/ace/snippets/python.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/python.js diff --git a/assets/framework/ace/snippets/r.js b/src/etcdkeeper/assets/static/framework/ace/snippets/r.js similarity index 100% rename from assets/framework/ace/snippets/r.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/r.js diff --git a/assets/framework/ace/snippets/razor.js b/src/etcdkeeper/assets/static/framework/ace/snippets/razor.js similarity index 100% rename from assets/framework/ace/snippets/razor.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/razor.js diff --git a/assets/framework/ace/snippets/rdoc.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rdoc.js similarity index 100% rename from assets/framework/ace/snippets/rdoc.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rdoc.js diff --git a/assets/framework/ace/snippets/rhtml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rhtml.js similarity index 100% rename from assets/framework/ace/snippets/rhtml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rhtml.js diff --git a/assets/framework/ace/snippets/rst.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rst.js similarity index 100% rename from assets/framework/ace/snippets/rst.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rst.js diff --git a/assets/framework/ace/snippets/ruby.js b/src/etcdkeeper/assets/static/framework/ace/snippets/ruby.js similarity index 100% rename from assets/framework/ace/snippets/ruby.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/ruby.js diff --git a/assets/framework/ace/snippets/rust.js b/src/etcdkeeper/assets/static/framework/ace/snippets/rust.js similarity index 100% rename from assets/framework/ace/snippets/rust.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/rust.js diff --git a/assets/framework/ace/snippets/sass.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sass.js similarity index 100% rename from assets/framework/ace/snippets/sass.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sass.js diff --git a/assets/framework/ace/snippets/scad.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scad.js similarity index 100% rename from assets/framework/ace/snippets/scad.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scad.js diff --git a/assets/framework/ace/snippets/scala.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scala.js similarity index 100% rename from assets/framework/ace/snippets/scala.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scala.js diff --git a/assets/framework/ace/snippets/scheme.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scheme.js similarity index 100% rename from assets/framework/ace/snippets/scheme.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scheme.js diff --git a/assets/framework/ace/snippets/scss.js b/src/etcdkeeper/assets/static/framework/ace/snippets/scss.js similarity index 100% rename from assets/framework/ace/snippets/scss.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/scss.js diff --git a/assets/framework/ace/snippets/sh.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sh.js similarity index 100% rename from assets/framework/ace/snippets/sh.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sh.js diff --git a/assets/framework/ace/snippets/sjs.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sjs.js similarity index 100% rename from assets/framework/ace/snippets/sjs.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sjs.js diff --git a/assets/framework/ace/snippets/smarty.js b/src/etcdkeeper/assets/static/framework/ace/snippets/smarty.js similarity index 100% rename from assets/framework/ace/snippets/smarty.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/smarty.js diff --git a/assets/framework/ace/snippets/snippets.js b/src/etcdkeeper/assets/static/framework/ace/snippets/snippets.js similarity index 100% rename from assets/framework/ace/snippets/snippets.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/snippets.js diff --git a/assets/framework/ace/snippets/soy_template.js b/src/etcdkeeper/assets/static/framework/ace/snippets/soy_template.js similarity index 100% rename from assets/framework/ace/snippets/soy_template.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/soy_template.js diff --git a/assets/framework/ace/snippets/space.js b/src/etcdkeeper/assets/static/framework/ace/snippets/space.js similarity index 100% rename from assets/framework/ace/snippets/space.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/space.js diff --git a/assets/framework/ace/snippets/sql.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sql.js similarity index 100% rename from assets/framework/ace/snippets/sql.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sql.js diff --git a/assets/framework/ace/snippets/sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/snippets/sqlserver.js similarity index 100% rename from assets/framework/ace/snippets/sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/sqlserver.js diff --git a/assets/framework/ace/snippets/stylus.js b/src/etcdkeeper/assets/static/framework/ace/snippets/stylus.js similarity index 100% rename from assets/framework/ace/snippets/stylus.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/stylus.js diff --git a/assets/framework/ace/snippets/svg.js b/src/etcdkeeper/assets/static/framework/ace/snippets/svg.js similarity index 100% rename from assets/framework/ace/snippets/svg.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/svg.js diff --git a/assets/framework/ace/snippets/swift.js b/src/etcdkeeper/assets/static/framework/ace/snippets/swift.js similarity index 100% rename from assets/framework/ace/snippets/swift.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/swift.js diff --git a/assets/framework/ace/snippets/tcl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tcl.js similarity index 100% rename from assets/framework/ace/snippets/tcl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tcl.js diff --git a/assets/framework/ace/snippets/tex.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tex.js similarity index 100% rename from assets/framework/ace/snippets/tex.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tex.js diff --git a/assets/framework/ace/snippets/text.js b/src/etcdkeeper/assets/static/framework/ace/snippets/text.js similarity index 100% rename from assets/framework/ace/snippets/text.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/text.js diff --git a/assets/framework/ace/snippets/textile.js b/src/etcdkeeper/assets/static/framework/ace/snippets/textile.js similarity index 100% rename from assets/framework/ace/snippets/textile.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/textile.js diff --git a/assets/framework/ace/snippets/toml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/toml.js similarity index 100% rename from assets/framework/ace/snippets/toml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/toml.js diff --git a/assets/framework/ace/snippets/tsx.js b/src/etcdkeeper/assets/static/framework/ace/snippets/tsx.js similarity index 100% rename from assets/framework/ace/snippets/tsx.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/tsx.js diff --git a/assets/framework/ace/snippets/twig.js b/src/etcdkeeper/assets/static/framework/ace/snippets/twig.js similarity index 100% rename from assets/framework/ace/snippets/twig.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/twig.js diff --git a/assets/framework/ace/snippets/typescript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/typescript.js similarity index 100% rename from assets/framework/ace/snippets/typescript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/typescript.js diff --git a/assets/framework/ace/snippets/vala.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vala.js similarity index 100% rename from assets/framework/ace/snippets/vala.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vala.js diff --git a/assets/framework/ace/snippets/vbscript.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vbscript.js similarity index 100% rename from assets/framework/ace/snippets/vbscript.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vbscript.js diff --git a/assets/framework/ace/snippets/velocity.js b/src/etcdkeeper/assets/static/framework/ace/snippets/velocity.js similarity index 100% rename from assets/framework/ace/snippets/velocity.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/velocity.js diff --git a/assets/framework/ace/snippets/verilog.js b/src/etcdkeeper/assets/static/framework/ace/snippets/verilog.js similarity index 100% rename from assets/framework/ace/snippets/verilog.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/verilog.js diff --git a/assets/framework/ace/snippets/vhdl.js b/src/etcdkeeper/assets/static/framework/ace/snippets/vhdl.js similarity index 100% rename from assets/framework/ace/snippets/vhdl.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/vhdl.js diff --git a/assets/framework/ace/snippets/wollok.js b/src/etcdkeeper/assets/static/framework/ace/snippets/wollok.js similarity index 100% rename from assets/framework/ace/snippets/wollok.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/wollok.js diff --git a/assets/framework/ace/snippets/xml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/xml.js similarity index 100% rename from assets/framework/ace/snippets/xml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/xml.js diff --git a/assets/framework/ace/snippets/xquery.js b/src/etcdkeeper/assets/static/framework/ace/snippets/xquery.js similarity index 100% rename from assets/framework/ace/snippets/xquery.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/xquery.js diff --git a/assets/framework/ace/snippets/yaml.js b/src/etcdkeeper/assets/static/framework/ace/snippets/yaml.js similarity index 100% rename from assets/framework/ace/snippets/yaml.js rename to src/etcdkeeper/assets/static/framework/ace/snippets/yaml.js diff --git a/assets/framework/ace/theme-ambiance.js b/src/etcdkeeper/assets/static/framework/ace/theme-ambiance.js similarity index 100% rename from assets/framework/ace/theme-ambiance.js rename to src/etcdkeeper/assets/static/framework/ace/theme-ambiance.js diff --git a/assets/framework/ace/theme-chaos.js b/src/etcdkeeper/assets/static/framework/ace/theme-chaos.js similarity index 100% rename from assets/framework/ace/theme-chaos.js rename to src/etcdkeeper/assets/static/framework/ace/theme-chaos.js diff --git a/assets/framework/ace/theme-chrome.js b/src/etcdkeeper/assets/static/framework/ace/theme-chrome.js similarity index 100% rename from assets/framework/ace/theme-chrome.js rename to src/etcdkeeper/assets/static/framework/ace/theme-chrome.js diff --git a/assets/framework/ace/theme-clouds.js b/src/etcdkeeper/assets/static/framework/ace/theme-clouds.js similarity index 100% rename from assets/framework/ace/theme-clouds.js rename to src/etcdkeeper/assets/static/framework/ace/theme-clouds.js diff --git a/assets/framework/ace/theme-clouds_midnight.js b/src/etcdkeeper/assets/static/framework/ace/theme-clouds_midnight.js similarity index 100% rename from assets/framework/ace/theme-clouds_midnight.js rename to src/etcdkeeper/assets/static/framework/ace/theme-clouds_midnight.js diff --git a/assets/framework/ace/theme-cobalt.js b/src/etcdkeeper/assets/static/framework/ace/theme-cobalt.js similarity index 100% rename from assets/framework/ace/theme-cobalt.js rename to src/etcdkeeper/assets/static/framework/ace/theme-cobalt.js diff --git a/assets/framework/ace/theme-crimson_editor.js b/src/etcdkeeper/assets/static/framework/ace/theme-crimson_editor.js similarity index 100% rename from assets/framework/ace/theme-crimson_editor.js rename to src/etcdkeeper/assets/static/framework/ace/theme-crimson_editor.js diff --git a/assets/framework/ace/theme-dawn.js b/src/etcdkeeper/assets/static/framework/ace/theme-dawn.js similarity index 100% rename from assets/framework/ace/theme-dawn.js rename to src/etcdkeeper/assets/static/framework/ace/theme-dawn.js diff --git a/assets/framework/ace/theme-dreamweaver.js b/src/etcdkeeper/assets/static/framework/ace/theme-dreamweaver.js similarity index 100% rename from assets/framework/ace/theme-dreamweaver.js rename to src/etcdkeeper/assets/static/framework/ace/theme-dreamweaver.js diff --git a/assets/framework/ace/theme-eclipse.js b/src/etcdkeeper/assets/static/framework/ace/theme-eclipse.js similarity index 100% rename from assets/framework/ace/theme-eclipse.js rename to src/etcdkeeper/assets/static/framework/ace/theme-eclipse.js diff --git a/assets/framework/ace/theme-github.js b/src/etcdkeeper/assets/static/framework/ace/theme-github.js similarity index 100% rename from assets/framework/ace/theme-github.js rename to src/etcdkeeper/assets/static/framework/ace/theme-github.js diff --git a/assets/framework/ace/theme-idle_fingers.js b/src/etcdkeeper/assets/static/framework/ace/theme-idle_fingers.js similarity index 100% rename from assets/framework/ace/theme-idle_fingers.js rename to src/etcdkeeper/assets/static/framework/ace/theme-idle_fingers.js diff --git a/assets/framework/ace/theme-iplastic.js b/src/etcdkeeper/assets/static/framework/ace/theme-iplastic.js similarity index 100% rename from assets/framework/ace/theme-iplastic.js rename to src/etcdkeeper/assets/static/framework/ace/theme-iplastic.js diff --git a/assets/framework/ace/theme-katzenmilch.js b/src/etcdkeeper/assets/static/framework/ace/theme-katzenmilch.js similarity index 100% rename from assets/framework/ace/theme-katzenmilch.js rename to src/etcdkeeper/assets/static/framework/ace/theme-katzenmilch.js diff --git a/assets/framework/ace/theme-kr_theme.js b/src/etcdkeeper/assets/static/framework/ace/theme-kr_theme.js similarity index 100% rename from assets/framework/ace/theme-kr_theme.js rename to src/etcdkeeper/assets/static/framework/ace/theme-kr_theme.js diff --git a/assets/framework/ace/theme-kuroir.js b/src/etcdkeeper/assets/static/framework/ace/theme-kuroir.js similarity index 100% rename from assets/framework/ace/theme-kuroir.js rename to src/etcdkeeper/assets/static/framework/ace/theme-kuroir.js diff --git a/assets/framework/ace/theme-merbivore.js b/src/etcdkeeper/assets/static/framework/ace/theme-merbivore.js similarity index 100% rename from assets/framework/ace/theme-merbivore.js rename to src/etcdkeeper/assets/static/framework/ace/theme-merbivore.js diff --git a/assets/framework/ace/theme-merbivore_soft.js b/src/etcdkeeper/assets/static/framework/ace/theme-merbivore_soft.js similarity index 100% rename from assets/framework/ace/theme-merbivore_soft.js rename to src/etcdkeeper/assets/static/framework/ace/theme-merbivore_soft.js diff --git a/assets/framework/ace/theme-mono_industrial.js b/src/etcdkeeper/assets/static/framework/ace/theme-mono_industrial.js similarity index 100% rename from assets/framework/ace/theme-mono_industrial.js rename to src/etcdkeeper/assets/static/framework/ace/theme-mono_industrial.js diff --git a/assets/framework/ace/theme-monokai.js b/src/etcdkeeper/assets/static/framework/ace/theme-monokai.js similarity index 100% rename from assets/framework/ace/theme-monokai.js rename to src/etcdkeeper/assets/static/framework/ace/theme-monokai.js diff --git a/assets/framework/ace/theme-pastel_on_dark.js b/src/etcdkeeper/assets/static/framework/ace/theme-pastel_on_dark.js similarity index 100% rename from assets/framework/ace/theme-pastel_on_dark.js rename to src/etcdkeeper/assets/static/framework/ace/theme-pastel_on_dark.js diff --git a/assets/framework/ace/theme-solarized_dark.js b/src/etcdkeeper/assets/static/framework/ace/theme-solarized_dark.js similarity index 100% rename from assets/framework/ace/theme-solarized_dark.js rename to src/etcdkeeper/assets/static/framework/ace/theme-solarized_dark.js diff --git a/assets/framework/ace/theme-solarized_light.js b/src/etcdkeeper/assets/static/framework/ace/theme-solarized_light.js similarity index 100% rename from assets/framework/ace/theme-solarized_light.js rename to src/etcdkeeper/assets/static/framework/ace/theme-solarized_light.js diff --git a/assets/framework/ace/theme-sqlserver.js b/src/etcdkeeper/assets/static/framework/ace/theme-sqlserver.js similarity index 100% rename from assets/framework/ace/theme-sqlserver.js rename to src/etcdkeeper/assets/static/framework/ace/theme-sqlserver.js diff --git a/assets/framework/ace/theme-terminal.js b/src/etcdkeeper/assets/static/framework/ace/theme-terminal.js similarity index 100% rename from assets/framework/ace/theme-terminal.js rename to src/etcdkeeper/assets/static/framework/ace/theme-terminal.js diff --git a/assets/framework/ace/theme-textmate.js b/src/etcdkeeper/assets/static/framework/ace/theme-textmate.js similarity index 100% rename from assets/framework/ace/theme-textmate.js rename to src/etcdkeeper/assets/static/framework/ace/theme-textmate.js diff --git a/assets/framework/ace/theme-tomorrow.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow.js similarity index 100% rename from assets/framework/ace/theme-tomorrow.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow.js diff --git a/assets/framework/ace/theme-tomorrow_night.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night.js diff --git a/assets/framework/ace/theme-tomorrow_night_blue.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_blue.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_blue.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_blue.js diff --git a/assets/framework/ace/theme-tomorrow_night_bright.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_bright.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_bright.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_bright.js diff --git a/assets/framework/ace/theme-tomorrow_night_eighties.js b/src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_eighties.js similarity index 100% rename from assets/framework/ace/theme-tomorrow_night_eighties.js rename to src/etcdkeeper/assets/static/framework/ace/theme-tomorrow_night_eighties.js diff --git a/assets/framework/ace/theme-twilight.js b/src/etcdkeeper/assets/static/framework/ace/theme-twilight.js similarity index 100% rename from assets/framework/ace/theme-twilight.js rename to src/etcdkeeper/assets/static/framework/ace/theme-twilight.js diff --git a/assets/framework/ace/theme-vibrant_ink.js b/src/etcdkeeper/assets/static/framework/ace/theme-vibrant_ink.js similarity index 100% rename from assets/framework/ace/theme-vibrant_ink.js rename to src/etcdkeeper/assets/static/framework/ace/theme-vibrant_ink.js diff --git a/assets/framework/ace/theme-xcode.js b/src/etcdkeeper/assets/static/framework/ace/theme-xcode.js similarity index 100% rename from assets/framework/ace/theme-xcode.js rename to src/etcdkeeper/assets/static/framework/ace/theme-xcode.js diff --git a/assets/framework/ace/worker-coffee.js b/src/etcdkeeper/assets/static/framework/ace/worker-coffee.js similarity index 100% rename from assets/framework/ace/worker-coffee.js rename to src/etcdkeeper/assets/static/framework/ace/worker-coffee.js diff --git a/assets/framework/ace/worker-css.js b/src/etcdkeeper/assets/static/framework/ace/worker-css.js similarity index 100% rename from assets/framework/ace/worker-css.js rename to src/etcdkeeper/assets/static/framework/ace/worker-css.js diff --git a/assets/framework/ace/worker-html.js b/src/etcdkeeper/assets/static/framework/ace/worker-html.js similarity index 100% rename from assets/framework/ace/worker-html.js rename to src/etcdkeeper/assets/static/framework/ace/worker-html.js diff --git a/assets/framework/ace/worker-javascript.js b/src/etcdkeeper/assets/static/framework/ace/worker-javascript.js similarity index 100% rename from assets/framework/ace/worker-javascript.js rename to src/etcdkeeper/assets/static/framework/ace/worker-javascript.js diff --git a/assets/framework/ace/worker-json.js b/src/etcdkeeper/assets/static/framework/ace/worker-json.js similarity index 100% rename from assets/framework/ace/worker-json.js rename to src/etcdkeeper/assets/static/framework/ace/worker-json.js diff --git a/assets/framework/ace/worker-lua.js b/src/etcdkeeper/assets/static/framework/ace/worker-lua.js similarity index 100% rename from assets/framework/ace/worker-lua.js rename to src/etcdkeeper/assets/static/framework/ace/worker-lua.js diff --git a/assets/framework/ace/worker-php.js b/src/etcdkeeper/assets/static/framework/ace/worker-php.js similarity index 100% rename from assets/framework/ace/worker-php.js rename to src/etcdkeeper/assets/static/framework/ace/worker-php.js diff --git a/assets/framework/ace/worker-xml.js b/src/etcdkeeper/assets/static/framework/ace/worker-xml.js similarity index 100% rename from assets/framework/ace/worker-xml.js rename to src/etcdkeeper/assets/static/framework/ace/worker-xml.js diff --git a/assets/framework/ace/worker-xquery.js b/src/etcdkeeper/assets/static/framework/ace/worker-xquery.js similarity index 100% rename from assets/framework/ace/worker-xquery.js rename to src/etcdkeeper/assets/static/framework/ace/worker-xquery.js diff --git a/assets/framework/custom/css/style.css b/src/etcdkeeper/assets/static/framework/custom/css/style.css similarity index 100% rename from assets/framework/custom/css/style.css rename to src/etcdkeeper/assets/static/framework/custom/css/style.css diff --git a/assets/framework/custom/js/common.js b/src/etcdkeeper/assets/static/framework/custom/js/common.js similarity index 100% rename from assets/framework/custom/js/common.js rename to src/etcdkeeper/assets/static/framework/custom/js/common.js diff --git a/assets/framework/easyui/jquery.easyui.min.js b/src/etcdkeeper/assets/static/framework/easyui/jquery.easyui.min.js similarity index 100% rename from assets/framework/easyui/jquery.easyui.min.js rename to src/etcdkeeper/assets/static/framework/easyui/jquery.easyui.min.js diff --git a/assets/framework/easyui/jquery.min.js b/src/etcdkeeper/assets/static/framework/easyui/jquery.min.js similarity index 100% rename from assets/framework/easyui/jquery.min.js rename to src/etcdkeeper/assets/static/framework/easyui/jquery.min.js diff --git a/assets/framework/easyui/locale/easyui-lang-af.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-af.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-af.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-af.js diff --git a/assets/framework/easyui/locale/easyui-lang-am.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-am.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-am.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-am.js diff --git a/assets/framework/easyui/locale/easyui-lang-ar.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ar.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ar.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ar.js diff --git a/assets/framework/easyui/locale/easyui-lang-bg.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-bg.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-bg.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-bg.js diff --git a/assets/framework/easyui/locale/easyui-lang-ca.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ca.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ca.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ca.js diff --git a/assets/framework/easyui/locale/easyui-lang-cs.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cs.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-cs.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cs.js diff --git a/assets/framework/easyui/locale/easyui-lang-cz.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cz.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-cz.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-cz.js diff --git a/assets/framework/easyui/locale/easyui-lang-da.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-da.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-da.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-da.js diff --git a/assets/framework/easyui/locale/easyui-lang-de.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-de.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-de.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-de.js diff --git a/assets/framework/easyui/locale/easyui-lang-el.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-el.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-el.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-el.js diff --git a/assets/framework/easyui/locale/easyui-lang-en.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-en.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-en.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-en.js diff --git a/assets/framework/easyui/locale/easyui-lang-es.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-es.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-es.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-es.js diff --git a/assets/framework/easyui/locale/easyui-lang-fr.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-fr.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-fr.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-fr.js diff --git a/assets/framework/easyui/locale/easyui-lang-it.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-it.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-it.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-it.js diff --git a/assets/framework/easyui/locale/easyui-lang-jp.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-jp.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-jp.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-jp.js diff --git a/assets/framework/easyui/locale/easyui-lang-ko.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ko.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ko.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ko.js diff --git a/assets/framework/easyui/locale/easyui-lang-nl.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-nl.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-nl.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-nl.js diff --git a/assets/framework/easyui/locale/easyui-lang-pl.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pl.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-pl.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pl.js diff --git a/assets/framework/easyui/locale/easyui-lang-pt_BR.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pt_BR.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-pt_BR.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-pt_BR.js diff --git a/assets/framework/easyui/locale/easyui-lang-ru.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ru.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-ru.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-ru.js diff --git a/assets/framework/easyui/locale/easyui-lang-sv_SE.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-sv_SE.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-sv_SE.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-sv_SE.js diff --git a/assets/framework/easyui/locale/easyui-lang-tr.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-tr.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-tr.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-tr.js diff --git a/assets/framework/easyui/locale/easyui-lang-zh_CN.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_CN.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-zh_CN.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_CN.js diff --git a/assets/framework/easyui/locale/easyui-lang-zh_TW.js b/src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_TW.js similarity index 100% rename from assets/framework/easyui/locale/easyui-lang-zh_TW.js rename to src/etcdkeeper/assets/static/framework/easyui/locale/easyui-lang-zh_TW.js diff --git a/assets/framework/easyui/themes/bootstrap/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/easyui.css similarity index 100% rename from assets/framework/easyui/themes/bootstrap/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/easyui.css diff --git a/assets/framework/easyui/themes/bootstrap/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/blank.gif diff --git a/assets/framework/easyui/themes/bootstrap/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/bootstrap/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/bootstrap/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/bootstrap/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/loading.gif diff --git a/assets/framework/easyui/themes/bootstrap/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/messager_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/panel_tools.png diff --git a/assets/framework/easyui/themes/bootstrap/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/bootstrap/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/slider_handle.png diff --git a/assets/framework/easyui/themes/bootstrap/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/bootstrap/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/tree_icons.png diff --git a/assets/framework/easyui/themes/bootstrap/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/bootstrap/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/bootstrap/images/validatebox_warning.png diff --git a/assets/framework/easyui/themes/default/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/default/easyui.css similarity index 100% rename from assets/framework/easyui/themes/default/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/easyui.css diff --git a/assets/framework/easyui/themes/default/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/default/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/default/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/blank.gif diff --git a/assets/framework/easyui/themes/default/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/default/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/default/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/default/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/default/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/default/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/default/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/default/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/default/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/default/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/default/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/loading.gif diff --git a/assets/framework/easyui/themes/default/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/default/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/messager_icons.png diff --git a/assets/framework/easyui/themes/default/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/default/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/default/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/panel_tools.png diff --git a/assets/framework/easyui/themes/default/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/default/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/default/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/default/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/slider_handle.png diff --git a/assets/framework/easyui/themes/default/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/default/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/default/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/default/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/default/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/tree_icons.png diff --git a/assets/framework/easyui/themes/default/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/default/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/default/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/default/images/validatebox_warning.png diff --git a/assets/framework/easyui/themes/icon.css b/src/etcdkeeper/assets/static/framework/easyui/themes/icon.css similarity index 100% rename from assets/framework/easyui/themes/icon.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/icon.css diff --git a/assets/framework/easyui/themes/icons/back.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/back.png similarity index 100% rename from assets/framework/easyui/themes/icons/back.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/back.png diff --git a/assets/framework/easyui/themes/icons/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/blank.gif similarity index 100% rename from assets/framework/easyui/themes/icons/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/blank.gif diff --git a/assets/framework/easyui/themes/icons/cancel.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/cancel.png similarity index 100% rename from assets/framework/easyui/themes/icons/cancel.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/cancel.png diff --git a/assets/framework/easyui/themes/icons/clear.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/clear.png similarity index 100% rename from assets/framework/easyui/themes/icons/clear.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/clear.png diff --git a/assets/framework/easyui/themes/icons/cut.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/cut.png similarity index 100% rename from assets/framework/easyui/themes/icons/cut.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/cut.png diff --git a/assets/framework/easyui/themes/icons/dir.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/dir.png similarity index 100% rename from assets/framework/easyui/themes/icons/dir.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/dir.png diff --git a/assets/framework/easyui/themes/icons/edit_add.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_add.png similarity index 100% rename from assets/framework/easyui/themes/icons/edit_add.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_add.png diff --git a/assets/framework/easyui/themes/icons/edit_remove.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_remove.png similarity index 100% rename from assets/framework/easyui/themes/icons/edit_remove.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/edit_remove.png diff --git a/assets/framework/easyui/themes/icons/export.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/export.png similarity index 100% rename from assets/framework/easyui/themes/icons/export.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/export.png diff --git a/assets/framework/easyui/themes/icons/filesave.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/filesave.png similarity index 100% rename from assets/framework/easyui/themes/icons/filesave.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/filesave.png diff --git a/assets/framework/easyui/themes/icons/filter.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/filter.png similarity index 100% rename from assets/framework/easyui/themes/icons/filter.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/filter.png diff --git a/assets/framework/easyui/themes/icons/help.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/help.png similarity index 100% rename from assets/framework/easyui/themes/icons/help.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/help.png diff --git a/assets/framework/easyui/themes/icons/import.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/import.png similarity index 100% rename from assets/framework/easyui/themes/icons/import.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/import.png diff --git a/assets/framework/easyui/themes/icons/large_chart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_chart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_chart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_chart.png diff --git a/assets/framework/easyui/themes/icons/large_clipart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_clipart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_clipart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_clipart.png diff --git a/assets/framework/easyui/themes/icons/large_picture.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_picture.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_picture.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_picture.png diff --git a/assets/framework/easyui/themes/icons/large_shapes.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_shapes.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_shapes.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_shapes.png diff --git a/assets/framework/easyui/themes/icons/large_smartart.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_smartart.png similarity index 100% rename from assets/framework/easyui/themes/icons/large_smartart.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/large_smartart.png diff --git a/assets/framework/easyui/themes/icons/lock.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/lock.png similarity index 100% rename from assets/framework/easyui/themes/icons/lock.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/lock.png diff --git a/assets/framework/easyui/themes/icons/man.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/man.png similarity index 100% rename from assets/framework/easyui/themes/icons/man.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/man.png diff --git a/assets/framework/easyui/themes/icons/mini_add.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_add.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_add.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_add.png diff --git a/assets/framework/easyui/themes/icons/mini_edit.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_edit.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_edit.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_edit.png diff --git a/assets/framework/easyui/themes/icons/mini_refresh.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_refresh.png similarity index 100% rename from assets/framework/easyui/themes/icons/mini_refresh.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/mini_refresh.png diff --git a/assets/framework/easyui/themes/icons/more.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/more.png similarity index 100% rename from assets/framework/easyui/themes/icons/more.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/more.png diff --git a/assets/framework/easyui/themes/icons/no.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/no.png similarity index 100% rename from assets/framework/easyui/themes/icons/no.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/no.png diff --git a/assets/framework/easyui/themes/icons/ok.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/ok.png similarity index 100% rename from assets/framework/easyui/themes/icons/ok.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/ok.png diff --git a/assets/framework/easyui/themes/icons/path.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/path.png similarity index 100% rename from assets/framework/easyui/themes/icons/path.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/path.png diff --git a/assets/framework/easyui/themes/icons/pencil.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/pencil.png similarity index 100% rename from assets/framework/easyui/themes/icons/pencil.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/pencil.png diff --git a/assets/framework/easyui/themes/icons/print.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/print.png similarity index 100% rename from assets/framework/easyui/themes/icons/print.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/print.png diff --git a/assets/framework/easyui/themes/icons/redo.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/redo.png similarity index 100% rename from assets/framework/easyui/themes/icons/redo.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/redo.png diff --git a/assets/framework/easyui/themes/icons/reload.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/reload.png similarity index 100% rename from assets/framework/easyui/themes/icons/reload.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/reload.png diff --git a/assets/framework/easyui/themes/icons/search.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/search.png similarity index 100% rename from assets/framework/easyui/themes/icons/search.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/search.png diff --git a/assets/framework/easyui/themes/icons/server.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/server.png similarity index 100% rename from assets/framework/easyui/themes/icons/server.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/server.png diff --git a/assets/framework/easyui/themes/icons/sum.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/sum.png similarity index 100% rename from assets/framework/easyui/themes/icons/sum.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/sum.png diff --git a/assets/framework/easyui/themes/icons/text.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/text.png similarity index 100% rename from assets/framework/easyui/themes/icons/text.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/text.png diff --git a/assets/framework/easyui/themes/icons/tip.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/tip.png similarity index 100% rename from assets/framework/easyui/themes/icons/tip.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/tip.png diff --git a/assets/framework/easyui/themes/icons/undo.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/undo.png similarity index 100% rename from assets/framework/easyui/themes/icons/undo.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/undo.png diff --git a/assets/framework/easyui/themes/icons/user.png b/src/etcdkeeper/assets/static/framework/easyui/themes/icons/user.png similarity index 100% rename from assets/framework/easyui/themes/icons/user.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/icons/user.png diff --git a/assets/framework/easyui/themes/metro/easyui.css b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/easyui.css similarity index 100% rename from assets/framework/easyui/themes/metro/easyui.css rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/easyui.css diff --git a/assets/framework/easyui/themes/metro/images/accordion_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/accordion_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/accordion_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/accordion_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/blank.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/blank.gif similarity index 100% rename from assets/framework/easyui/themes/metro/images/blank.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/blank.gif diff --git a/assets/framework/easyui/themes/metro/images/calendar_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/calendar_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/calendar_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/calendar_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/combo_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/combo_arrow.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/combo_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/combo_arrow.png diff --git a/assets/framework/easyui/themes/metro/images/datagrid_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datagrid_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/datagrid_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datagrid_icons.png diff --git a/assets/framework/easyui/themes/metro/images/datebox_arrow.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datebox_arrow.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/datebox_arrow.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/datebox_arrow.png diff --git a/assets/framework/easyui/themes/metro/images/layout_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/layout_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/layout_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/layout_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/linkbutton_bg.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/linkbutton_bg.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/linkbutton_bg.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/linkbutton_bg.png diff --git a/assets/framework/easyui/themes/metro/images/loading.gif b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/loading.gif similarity index 100% rename from assets/framework/easyui/themes/metro/images/loading.gif rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/loading.gif diff --git a/assets/framework/easyui/themes/metro/images/menu_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/menu_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/menu_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/menu_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/messager_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/messager_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/messager_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/messager_icons.png diff --git a/assets/framework/easyui/themes/metro/images/pagination_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/pagination_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/pagination_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/pagination_icons.png diff --git a/assets/framework/easyui/themes/metro/images/panel_tools.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/panel_tools.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/panel_tools.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/panel_tools.png diff --git a/assets/framework/easyui/themes/metro/images/searchbox_button.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/searchbox_button.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/searchbox_button.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/searchbox_button.png diff --git a/assets/framework/easyui/themes/metro/images/slider_handle.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/slider_handle.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/slider_handle.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/slider_handle.png diff --git a/assets/framework/easyui/themes/metro/images/spinner_arrows.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/spinner_arrows.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/spinner_arrows.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/spinner_arrows.png diff --git a/assets/framework/easyui/themes/metro/images/tabs_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tabs_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/tabs_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tabs_icons.png diff --git a/assets/framework/easyui/themes/metro/images/tree_icons.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tree_icons.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/tree_icons.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/tree_icons.png diff --git a/assets/framework/easyui/themes/metro/images/validatebox_warning.png b/src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/validatebox_warning.png similarity index 100% rename from assets/framework/easyui/themes/metro/images/validatebox_warning.png rename to src/etcdkeeper/assets/static/framework/easyui/themes/metro/images/validatebox_warning.png diff --git a/assets/framework/favicon.ico b/src/etcdkeeper/assets/static/framework/favicon.ico similarity index 100% rename from assets/framework/favicon.ico rename to src/etcdkeeper/assets/static/framework/favicon.ico diff --git a/assets/framework/jquery/jquery.json-2.2.js b/src/etcdkeeper/assets/static/framework/jquery/jquery.json-2.2.js similarity index 100% rename from assets/framework/jquery/jquery.json-2.2.js rename to src/etcdkeeper/assets/static/framework/jquery/jquery.json-2.2.js diff --git a/assets/framework/js.cookie-2.1.4.min.js b/src/etcdkeeper/assets/static/framework/js.cookie-2.1.4.min.js similarity index 100% rename from assets/framework/js.cookie-2.1.4.min.js rename to src/etcdkeeper/assets/static/framework/js.cookie-2.1.4.min.js diff --git a/assets/framework/logo.png b/src/etcdkeeper/assets/static/framework/logo.png similarity index 100% rename from assets/framework/logo.png rename to src/etcdkeeper/assets/static/framework/logo.png diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index d74744f..19b9ccf 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -3,16 +3,17 @@ package main import ( "context" "crypto/tls" + "embed" "encoding/json" "etcdkeeper/session" _ "etcdkeeper/session/providers/memory" "flag" "fmt" "io" + "io/fs" "log" "net/http" "os" - "path/filepath" "sort" "strconv" "strings" @@ -48,6 +49,9 @@ type userInfo struct { passwd string } +//go:embed assets/* +var assets embed.FS + func main() { host := flag.String("h", "0.0.0.0", "host name or ip address") port := flag.Int("p", 8080, "port") @@ -82,28 +86,8 @@ func main() { // dirctory mode http.HandleFunc("/v3/getpath", middleware(nothing, getPath)) - wd, err := os.Executable() - if err != nil { - log.Fatal(err) - } - exStat, err := os.Lstat(wd) - if err != nil { - log.Fatal(err) - } - - readWd := wd - // check if executable path is symlink - if exStat.Mode()&os.ModeSymlink != 0 { - // if symlink evaluate it to get real path - readWd, err = filepath.EvalSymlinks(wd) - if err != nil { - log.Printf("failed evaluating executable symlink path (%s).\netcdkeeper will use symlink for loading asset", wd) - } - } - - rootPath := filepath.Dir(readWd) - - // Session management + var err error + // Session managment sessmgr, err = session.NewManager("memory", "_etcdkeeper_session", 86400) if err != nil { log.Fatal(err) @@ -111,10 +95,17 @@ func main() { time.AfterFunc(86400*time.Second, func() { sessmgr.GC() }) - //log.Println(http.Dir(rootPath + "/assets")) - http.Handle("/", http.FileServer(http.Dir(rootPath+"/assets"))) // view static directory + // static directory server + staticFS, err := fs.Sub(assets, "assets/static") + if err != nil { + log.Fatalf("Fail to load static assets resource directory : %v", err) + } + staticHandler := http.FileServer(http.FS(staticFS)) + http.HandleFunc("/", middleware(nothing, staticHandler.ServeHTTP)) + + // listening log.Printf("listening on %s:%d\n", *host, *port) err = http.ListenAndServe(*host+":"+strconv.Itoa(*port), nil) if err != nil { @@ -666,6 +657,7 @@ func get(w http.ResponseWriter, r *http.Request) { sess := sessmgr.SessionStart(w, r) v := sess.Get("uinfo") var uinfo *userInfo + if v != nil { uinfo = v.(*userInfo) cli, _ = newClient(uinfo) From 6a06c967e43fa040a920baf1faf449d8912a3eec Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Wed, 25 Aug 2021 02:39:24 +0200 Subject: [PATCH 09/13] feat: permit golang templating into asset --- Dockerfile | 1 - Makefile | 3 + .../etcdkeeper/index.html | 0 src/etcdkeeper/internal/responseWriter.go | 30 +++++++++ src/etcdkeeper/internal/template.go | 62 +++++++++++++++++++ src/etcdkeeper/main.go | 19 +++++- src/etcdkeeper/session/session.go | 2 +- 7 files changed, 113 insertions(+), 4 deletions(-) rename src/etcdkeeper/assets/{static => templates}/etcdkeeper/index.html (100%) create mode 100644 src/etcdkeeper/internal/responseWriter.go create mode 100644 src/etcdkeeper/internal/template.go diff --git a/Dockerfile b/Dockerfile index 0af7200..875aad5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,6 @@ RUN addgroup -S etcdkeeper && \ WORKDIR /opt/etcdkeeper COPY --from=build --chown=etcdkeeper:etcdkeeper /app/etcdkeeper . -ADD --chown=etcdkeeper:etcdkeeper assets assets EXPOSE ${PORT} USER etcdkeeper diff --git a/Makefile b/Makefile index 8d7e003..d0e34ab 100644 --- a/Makefile +++ b/Makefile @@ -5,3 +5,6 @@ all: lint: cd src/etcdkeeper && golangci-lint run --new-from-rev=HEAD~ + +dev: + cd src/etcdkeeper && go run main.go -h localhost \ No newline at end of file diff --git a/src/etcdkeeper/assets/static/etcdkeeper/index.html b/src/etcdkeeper/assets/templates/etcdkeeper/index.html similarity index 100% rename from src/etcdkeeper/assets/static/etcdkeeper/index.html rename to src/etcdkeeper/assets/templates/etcdkeeper/index.html diff --git a/src/etcdkeeper/internal/responseWriter.go b/src/etcdkeeper/internal/responseWriter.go new file mode 100644 index 0000000..5a76189 --- /dev/null +++ b/src/etcdkeeper/internal/responseWriter.go @@ -0,0 +1,30 @@ +package internal + +import "net/http" + +//CompletableResponseWriter extend http.ResponseWriter with completed state +// a ResponseWriter is Completed when method Write or WriteHeader have been called +type CompletableResponseWriter struct { + http.ResponseWriter + done bool +} + +//New initialize CompletableResponseWriter with non completed http.ResponseWriter +func NewCompletableResponseWriter(w http.ResponseWriter) *CompletableResponseWriter { + return &CompletableResponseWriter{w, false} +} + +func (w *CompletableResponseWriter) WriteHeader(status int) { + w.done = true + w.ResponseWriter.WriteHeader(status) +} + +func (w *CompletableResponseWriter) Write(b []byte) (int, error) { + w.done = true + return w.ResponseWriter.Write(b) +} + +//IsCompleted return true if response writer write or writeheader have been called +func (w *CompletableResponseWriter) IsCompleted() bool { + return w.done +} diff --git a/src/etcdkeeper/internal/template.go b/src/etcdkeeper/internal/template.go new file mode 100644 index 0000000..eaf9176 --- /dev/null +++ b/src/etcdkeeper/internal/template.go @@ -0,0 +1,62 @@ +package internal + +import ( + "io/fs" + "log" + "net/http" + "path" + "path/filepath" + "strings" + "text/template" +) + +type templateHandler struct { + config interface{} + templates *template.Template +} + +func NewTemplateServer(rootDir fs.FS, templateData interface{}) *templateHandler { + + root := template.New("") + err := fs.WalkDir(rootDir, ".", func(currentPath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if !d.IsDir() { + b, err := fs.ReadFile(rootDir, currentPath) + if err != nil { + return err + } + + if len(currentPath) > 0 && !strings.HasPrefix(currentPath, "/") { + currentPath = "/" + currentPath + } + _, err = root.New(currentPath).Parse(string(b)) + if err != nil { + return err + } + log.Printf("template %q parsed\n", currentPath) + } + return nil + }) + + return &templateHandler{templateData, template.Must(root, err)} +} + +// ServeHTTP check and serve template content if exist +func (h *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // this clean '..' '.' and trailing slash '/' + cleanedURL := path.Clean(r.URL.Path) + + ext := path.Ext(cleanedURL) + // if no extension on name this is a folder + if ext == "" { + cleanedURL = filepath.Join(cleanedURL, "/index.html") + } + + err := h.templates.ExecuteTemplate(w, cleanedURL, h.config) + if err == nil { + log.Printf("GET : %s\n", cleanedURL) + } +} diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index 19b9ccf..41e14ef 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "embed" "encoding/json" + "etcdkeeper/internal" "etcdkeeper/session" _ "etcdkeeper/session/providers/memory" "flag" @@ -43,6 +44,9 @@ var ( mu sync.Mutex ) +type etcdkeeperConfig struct { +} + type userInfo struct { host string uname string @@ -61,8 +65,14 @@ func main() { middleware := func(fns ...func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { + // avoid calling superfluous write on ResponseWriter + cw := internal.NewCompletableResponseWriter(w) + for _, fn := range fns { - fn(w, r) + if cw.IsCompleted() { + break + } + fn(cw, r) } } } @@ -102,8 +112,13 @@ func main() { log.Fatalf("Fail to load static assets resource directory : %v", err) } staticHandler := http.FileServer(http.FS(staticFS)) + templateFS, err := fs.Sub(assets, "assets/templates") + if err != nil { + log.Fatalf("Fail to load templates assets resource directory : %v", err) + } + templateHandler := internal.NewTemplateServer(templateFS, &etcdkeeperConfig{}) - http.HandleFunc("/", middleware(nothing, staticHandler.ServeHTTP)) + http.HandleFunc("/", middleware(templateHandler.ServeHTTP, staticHandler.ServeHTTP)) // listening log.Printf("listening on %s:%d\n", *host, *port) diff --git a/src/etcdkeeper/session/session.go b/src/etcdkeeper/session/session.go index 675488f..221cc17 100644 --- a/src/etcdkeeper/session/session.go +++ b/src/etcdkeeper/session/session.go @@ -41,7 +41,7 @@ func Register(name string, provide Provider) { } type Manager struct { - cookieName string //private cookiename + cookieName string // private cookiename lock sync.Mutex // protects session provider Provider maxlifetime int64 From c2d92347f84bc2e36f3cb367c128e81dfc935e30 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Wed, 25 Aug 2021 13:42:09 +0200 Subject: [PATCH 10/13] refactor split main.go function into package etcdkeeper by version v2.go, v3.go file --- src/etcdkeeper/internal/etcdkeeper/config.go | 27 + .../internal/etcdkeeper/etcdkeeper.go | 55 + src/etcdkeeper/internal/etcdkeeper/v2.go | 437 +++++++ src/etcdkeeper/internal/etcdkeeper/v3.go | 543 +++++++++ src/etcdkeeper/main.go | 1040 +---------------- 5 files changed, 1081 insertions(+), 1021 deletions(-) create mode 100644 src/etcdkeeper/internal/etcdkeeper/config.go create mode 100644 src/etcdkeeper/internal/etcdkeeper/etcdkeeper.go create mode 100644 src/etcdkeeper/internal/etcdkeeper/v2.go create mode 100644 src/etcdkeeper/internal/etcdkeeper/v3.go diff --git a/src/etcdkeeper/internal/etcdkeeper/config.go b/src/etcdkeeper/internal/etcdkeeper/config.go new file mode 100644 index 0000000..bfe9e68 --- /dev/null +++ b/src/etcdkeeper/internal/etcdkeeper/config.go @@ -0,0 +1,27 @@ +package etcdkeeper + +import "flag" + +//EtcdkeeperConfig configuration for etcdkeeper object +type EtcdkeeperConfig struct { + separator string + usetls bool + cacert string + cert string + keyfile string + useAuth bool + connectTimeout int + grpcMaxMsgSize int +} + +// ParseFlag use build in flag module to parse command line etcdkeeper configuration +func (conf *EtcdkeeperConfig) ParseFlag() { + flag.StringVar(&conf.separator, "sep", "/", "separator") + flag.BoolVar(&conf.usetls, "usetls", false, "use tls") + flag.StringVar(&conf.cacert, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle (v3)") + flag.StringVar(&conf.cert, "cert", "", "identify secure client using this TLS certificate file (v3)") + flag.StringVar(&conf.keyfile, "key", "", "identify secure client using this TLS key file (v3)") + flag.BoolVar(&conf.useAuth, "auth", false, "use auth") + flag.IntVar(&conf.connectTimeout, "timeout", 5, "ETCD client connect timeout") + flag.IntVar(&conf.grpcMaxMsgSize, "grpcMaxMsgSize", 64*1024*1024, "ETCDv3 grpc client max receive msg size") +} diff --git a/src/etcdkeeper/internal/etcdkeeper/etcdkeeper.go b/src/etcdkeeper/internal/etcdkeeper/etcdkeeper.go new file mode 100644 index 0000000..3e34851 --- /dev/null +++ b/src/etcdkeeper/internal/etcdkeeper/etcdkeeper.go @@ -0,0 +1,55 @@ +package etcdkeeper + +import ( + "etcdkeeper/session" + _ "etcdkeeper/session/providers/memory" + "io" + "log" + "net/http" + "sync" + "time" +) + +type userInfo struct { + host string + uname string + passwd string +} + +type Etcdkeeper struct { + rootUsers map[string]*userInfo // host:rootUser + rootUsersV2 map[string]*userInfo // host:rootUser + + config EtcdkeeperConfig + sessmgr *session.Manager + mu sync.Mutex +} + +// NewEtcdKeeper initialize etdkeeper data structure +func NewEtcdKeeper(config *EtcdkeeperConfig) *Etcdkeeper { + + // Session managment + sessmgr, err := session.NewManager("memory", "_etcdkeeper_session", 86400) + if err != nil { + log.Fatal(err) + } + time.AfterFunc(86400*time.Second, func() { + sessmgr.GC() + }) + + return &Etcdkeeper{ + rootUsers: make(map[string]*userInfo), + rootUsersV2: make(map[string]*userInfo), + + config: *config, + sessmgr: sessmgr, + } +} + +func (ek *Etcdkeeper) GetSeparator(w http.ResponseWriter, _ *http.Request) { + io.WriteString(w, ek.config.separator) +} + +func (ek *Etcdkeeper) size(num int, unit int) (n, rem int) { + return num / unit, num - (num/unit)*unit +} diff --git a/src/etcdkeeper/internal/etcdkeeper/v2.go b/src/etcdkeeper/internal/etcdkeeper/v2.go new file mode 100644 index 0000000..cd8bc18 --- /dev/null +++ b/src/etcdkeeper/internal/etcdkeeper/v2.go @@ -0,0 +1,437 @@ +package etcdkeeper + +import ( + "context" + "encoding/json" + "io" + "log" + "net/http" + "sort" + "strconv" + "strings" + "time" + + "github.com/coreos/etcd/client" +) + +// v2 api +func (ek *Etcdkeeper) ConnectV2(w http.ResponseWriter, r *http.Request) { + ek.mu.Lock() + defer ek.mu.Unlock() + sess := ek.sessmgr.SessionStart(w, r) + host := strings.TrimSpace(r.FormValue("host")) + uname := r.FormValue("uname") + passwd := r.FormValue("passwd") + if !strings.HasPrefix(host, "http") { + host = "http://" + host + } + + if ek.config.useAuth { + _, ok := ek.rootUsersV2[host] + if !ok && uname != "root" { + b, _ := json.Marshal(map[string]interface{}{"status": "root"}) + io.WriteString(w, string(b)) + return + } + if uname == "" || passwd == "" { + b, _ := json.Marshal(map[string]interface{}{"status": "login"}) + io.WriteString(w, string(b)) + return + } + } + + if uinfo, ok := sess.Get("uinfov2").(*userInfo); ok { + if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { + info := ek.getInfoV2(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) + return + } + } + + uinfo := &userInfo{host: host, uname: uname, passwd: passwd} + _, err := ek.newClientV2(uinfo) + if err != nil { + log.Println(r.Method, "v2", "connect fail.") + b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) + io.WriteString(w, string(b)) + return + } + _ = sess.Set("uinfov2", uinfo) + + if ek.config.useAuth { + if uname == "root" { + ek.rootUsersV2[host] = uinfo + } + } else { + ek.rootUsersV2[host] = uinfo + } + log.Println(r.Method, "v2", "connect success.") + info := ek.getInfoV2(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) +} + +func (ek *Etcdkeeper) PutV2(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + value := r.FormValue("value") + ttl := r.FormValue("ttl") + dir := r.FormValue("dir") + log.Println("PUT", "v2", key) + + kapi := client.NewKeysAPI(ek.getClientV2(w, r)) + + var isDir bool + if dir != "" { + isDir, _ = strconv.ParseBool(dir) + } + var err error + data := make(map[string]interface{}) + if ttl != "" { + var sec int64 + sec, err = strconv.ParseInt(ttl, 10, 64) + if err != nil { + log.Println(err.Error()) + } + _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{TTL: time.Duration(sec) * time.Second, Dir: isDir}) + } else { + _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{Dir: isDir}) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp, err := kapi.Get(context.Background(), key, &client.GetOptions{Recursive: true, Sort: true}); err != nil { + data["errorCode"] = err.Error() + } else { + if resp.Node != nil { + node := make(map[string]interface{}) + node["key"] = resp.Node.Key + node["value"] = resp.Node.Value + node["dir"] = resp.Node.Dir + node["ttl"] = resp.Node.TTL + node["createdIndex"] = resp.Node.CreatedIndex + node["modifiedIndex"] = resp.Node.ModifiedIndex + data["node"] = node + } + } + } + + var dataByte []byte + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) GetV2(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + data := make(map[string]interface{}) + log.Println("GET", "v2", key) + + var cli client.Client + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfov2") + var uinfo *userInfo + if v != nil { + uinfo = v.(*userInfo) + cli, _ = ek.newClientV2(uinfo) + kapi := client.NewKeysAPI(cli) + + var permissions [][]string + if r.FormValue("prefix") == "true" { + var e error + permissions, e = ek.getPermissionPrefixV2(uinfo.host, uinfo.uname, key) + if e != nil { + io.WriteString(w, e.Error()) + return + } + } else { + permissions = [][]string{{key, ""}} + } + + var ( + min, max int + ) + if key == ek.config.separator { + min = 1 + } else { + min = len(strings.Split(key, ek.config.separator)) + } + max = min + all := make(map[int][]map[string]interface{}) + if key == ek.config.separator { + all[min] = []map[string]interface{}{{"key": key, "value": "", "dir": true, "nodes": make([]map[string]interface{}, 0)}} + } + for _, p := range permissions { + pKey, pRange := p[0], p[1] + var opt *client.GetOptions + if pRange != "" { + if pRange == "c" { + pKey += ek.config.separator + } + opt = &client.GetOptions{Recursive: true, Sort: true} + } + if resp, err := kapi.Get(context.Background(), pKey, opt); err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp.Node == nil { + data["errorCode"] = 500 + data["message"] = "The node does not exist." + } else { + max = ek.getNode(resp.Node, key, all, min, max) + } + } + } + + //b, _ := json.MarshalIndent(all, "", " ") + //fmt.Println(string(b)) + + // parent-child mapping + for i := max; i > min; i-- { + for _, a := range all[i] { + for _, pa := range all[i-1] { + if i == 2 { // The last is root + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } else { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+ek.config.separator) { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } + } + } + } + } + + for _, n := range all[min] { + if n["key"] == key { + nodesSort(n) + data["node"] = n + break + } + } + } + + var dataByte []byte + var err error + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func nodesSort(node map[string]interface{}) { + if v, ok := node["nodes"]; ok && v != nil { + a := v.([]map[string]interface{}) + if len(a) != 0 { + for i := 0; i < len(a)-1; i++ { + nodesSort(a[i]) + for j := i + 1; j < len(a); j++ { + if a[j]["key"].(string) < a[i]["key"].(string) { + a[i], a[j] = a[j], a[i] + } + } + } + nodesSort(a[len(a)-1]) + } + } +} + +func (ek *Etcdkeeper) DelV2(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + dir := r.FormValue("dir") + log.Println("DELETE", "v2", key) + + kapi := client.NewKeysAPI(ek.getClientV2(w, r)) + + isDir, _ := strconv.ParseBool(dir) + if isDir { + if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive: true, Dir: true}); err != nil { + io.WriteString(w, err.Error()) + return + } + } else { + if _, err := kapi.Delete(context.Background(), key, nil); err != nil { + io.WriteString(w, err.Error()) + return + } + } + + io.WriteString(w, "ok") +} + +func (ek *Etcdkeeper) GetPathV2(w http.ResponseWriter, r *http.Request) { + ek.GetV2(w, r) +} + +func (ek *Etcdkeeper) getClientV2(w http.ResponseWriter, r *http.Request) client.Client { + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfov2") + if v != nil { + uinfo := v.(*userInfo) + c, _ := ek.newClientV2(uinfo) + return c + } + return nil +} + +func (ek *Etcdkeeper) newClientV2(uinfo *userInfo) (client.Client, error) { + cfg := client.Config{ + Endpoints: []string{uinfo.host}, + HeaderTimeoutPerRequest: time.Second * time.Duration(ek.config.connectTimeout), + } + if ek.config.useAuth { + cfg.Username = uinfo.uname + cfg.Password = uinfo.passwd + } + + c, err := client.New(cfg) + if err != nil { + return nil, err + } + return c, nil +} + +func (ek *Etcdkeeper) getPermissionPrefixV2(host, uname, key string) ([][]string, error) { + if !ek.config.useAuth { + return [][]string{{key, "p"}}, nil // No auth return all + } else { + if uname == "root" { + return [][]string{{key, "p"}}, nil + } + + if !strings.HasPrefix(host, "http://") { + host = "http://" + host + } + rootUser := ek.rootUsersV2[host] + rootCli, err := ek.newClientV2(rootUser) + if err != nil { + return nil, err + } + rootUserKapi := client.NewAuthUserAPI(rootCli) + rootRoleKapi := client.NewAuthRoleAPI(rootCli) + + if users, err := rootUserKapi.ListUsers(context.Background()); err != nil { + return nil, err + } else { + // Find user permissions + set := make(map[string]string) + for _, u := range users { + if u == uname { + user, err := rootUserKapi.GetUser(context.Background(), u) + if err != nil { + return nil, err + } + for _, r := range user.Roles { + role, err := rootRoleKapi.GetRole(context.Background(), r) + if err != nil { + return nil, err + } + for _, ks := range role.Permissions.KV.Read { + var k string + if strings.HasSuffix(ks, "*") { + k = ks[:len(ks)-1] + set[k] = "p" + } else if strings.HasSuffix(ks, "/*") { + k = ks[:len(ks)-2] + set[k] = "c" + } else { + if _, ok := set[ks]; !ok { + set[ks] = "" + } + } + } + } + break + } + } + var pers [][]string + var ks []string + for k := range set { + ks = append(ks, k) + } + sort.Strings(ks) + for _, k := range ks { + pers = append(pers, []string{k, set[k]}) + } + return pers, nil + } + } +} + +func (ek *Etcdkeeper) getInfoV2(host string) map[string]string { + if !strings.HasPrefix(host, "http://") { + host = "http://" + host + } + info := make(map[string]string) + uinfo, ok := ek.rootUsersV2[host] + if ok { + rootClient, err := ek.newClientV2(uinfo) + if err != nil { + log.Println(err) + return info + } + ver, err := rootClient.GetVersion(context.Background()) + if err != nil { + log.Fatal(err) + } + memberKapi := client.NewMembersAPI(rootClient) + member, err := memberKapi.Leader(context.Background()) + if err != nil { + log.Fatal(err) + } + info["version"] = ver.Server + info["name"] = member.Name + info["size"] = "unknow" // FIXME: How get? + } + return info +} + +func (ek *Etcdkeeper) getNode(node *client.Node, selKey string, all map[int][]map[string]interface{}, min, max int) int { + keys := strings.Split(node.Key, ek.config.separator) // /foo/bar + if len(keys) < min && strings.HasPrefix(node.Key, selKey) { + return max + } + for i := range keys { // ["", "foo", "bar"] + k := strings.Join(keys[0:i+1], ek.config.separator) + if k == "" { + continue + } + nodeMap := map[string]interface{}{"key": k, "dir": true, "nodes": make([]map[string]interface{}, 0)} + if k == node.Key { + nodeMap["value"] = node.Value + nodeMap["dir"] = node.Dir + nodeMap["ttl"] = node.TTL + nodeMap["createdIndex"] = node.CreatedIndex + nodeMap["modifiedIndex"] = node.ModifiedIndex + } + keylevel := len(strings.Split(k, ek.config.separator)) + if keylevel > max { + max = keylevel + } + + if _, ok := all[keylevel]; !ok { + all[keylevel] = make([]map[string]interface{}, 0) + } + var isExist bool + for _, n := range all[keylevel] { + if n["key"].(string) == k { + isExist = true + } + } + if !isExist { + all[keylevel] = append(all[keylevel], nodeMap) + } + } + + if len(node.Nodes) != 0 { + for _, n := range node.Nodes { + max = ek.getNode(n, selKey, all, min, max) + } + } + return max +} diff --git a/src/etcdkeeper/internal/etcdkeeper/v3.go b/src/etcdkeeper/internal/etcdkeeper/v3.go new file mode 100644 index 0000000..04aebd2 --- /dev/null +++ b/src/etcdkeeper/internal/etcdkeeper/v3.go @@ -0,0 +1,543 @@ +package etcdkeeper + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/pkg/transport" + "google.golang.org/grpc" +) + +// v3 api +func (ek *Etcdkeeper) Connect(w http.ResponseWriter, r *http.Request) { + ek.mu.Lock() + defer ek.mu.Unlock() + sess := ek.sessmgr.SessionStart(w, r) + host := r.FormValue("host") + uname := r.FormValue("uname") + passwd := r.FormValue("passwd") + + if ek.config.useAuth { + if _, ok := ek.rootUsers[host]; !ok && uname != "root" { // no root user + b, _ := json.Marshal(map[string]interface{}{"status": "root"}) + io.WriteString(w, string(b)) + return + } + if uname == "" || passwd == "" { + b, _ := json.Marshal(map[string]interface{}{"status": "login"}) + io.WriteString(w, string(b)) + return + } + } + + if uinfo, ok := sess.Get("uinfo").(*userInfo); ok { + if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { + info := ek.getInfo(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) + return + } + } + + uinfo := &userInfo{host: host, uname: uname, passwd: passwd} + c, err := ek.newClient(uinfo) + if err != nil { + log.Println(r.Method, "v3", "connect fail.") + b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) + io.WriteString(w, string(b)) + return + } + defer c.Close() + _ = sess.Set("uinfo", uinfo) + + if ek.config.useAuth { + if uname == "root" { + ek.rootUsers[host] = uinfo + } + } else { + ek.rootUsers[host] = uinfo + } + log.Println(r.Method, "v3", "connect success.") + info := ek.getInfo(host) + b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) + io.WriteString(w, string(b)) +} + +func (ek *Etcdkeeper) Put(w http.ResponseWriter, r *http.Request) { + cli := ek.getClient(w, r) + defer cli.Close() + key := r.FormValue("key") + value := r.FormValue("value") + ttl := r.FormValue("ttl") + log.Println("PUT", "v3", key) + + var err error + data := make(map[string]interface{}) + if ttl != "" { + var sec int64 + sec, err = strconv.ParseInt(ttl, 10, 64) + if err != nil { + log.Println(err.Error()) + } + var leaseResp *clientv3.LeaseGrantResponse + leaseResp, err = cli.Grant(context.TODO(), sec) + if err == nil && leaseResp != nil { + _, err = cli.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID)) + } + } else { + _, err = cli.Put(context.Background(), key, value) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp, err := cli.Get(context.Background(), key); err != nil { + data["errorCode"] = 500 + data["errorCode"] = err.Error() + } else { + if resp.Count > 0 { + kv := resp.Kvs[0] + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + node["ttl"] = ek.getTTL(cli, kv.Lease) + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + data["node"] = node + } + } + } + + var dataByte []byte + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) Get(w http.ResponseWriter, r *http.Request) { + data := make(map[string]interface{}) + key := r.FormValue("key") + log.Println("GET", "v3", key) + + var cli *clientv3.Client + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + var uinfo *userInfo + + if v != nil { + uinfo = v.(*userInfo) + cli, _ = ek.newClient(uinfo) + defer cli.Close() + + permissions, e := ek.getPermissionPrefix(uinfo.host, uinfo.uname, key) + if e != nil { + io.WriteString(w, e.Error()) + return + } + if r.FormValue("prefix") == "true" { + pnode := make(map[string]interface{}) + pnode["key"] = key + pnode["nodes"] = make([]map[string]interface{}, 0) + for _, p := range permissions { + var ( + resp *clientv3.GetResponse + err error + ) + if p[1] != "" { + prefixKey := p[0] + if p[0] == "/" { + prefixKey = "" + } + resp, err = cli.Get(context.Background(), prefixKey, clientv3.WithPrefix()) + } else { + resp, err = cli.Get(context.Background(), p[0]) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + for _, kv := range resp.Kvs { + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + if key == string(kv.Key) { + node["ttl"] = ek.getTTL(cli, kv.Lease) + } else { + node["ttl"] = 0 + } + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + nodes := pnode["nodes"].([]map[string]interface{}) + pnode["nodes"] = append(nodes, node) + } + } + } + data["node"] = pnode + } else { + if resp, err := cli.Get(context.Background(), key); err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + } else { + if resp.Count > 0 { + kv := resp.Kvs[0] + node := make(map[string]interface{}) + node["key"] = string(kv.Key) + node["value"] = string(kv.Value) + node["dir"] = false + node["ttl"] = ek.getTTL(cli, kv.Lease) + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + data["node"] = node + } else { + data["errorCode"] = 500 + data["message"] = "The node does not exist." + } + } + } + } + + var dataByte []byte + var err error + if dataByte, err = json.Marshal(data); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) GetPath(w http.ResponseWriter, r *http.Request) { + originKey := r.FormValue("key") + log.Println("GET", "v3", originKey) + var ( + data = make(map[string]interface{}) + /* + {1:["/"], 2:["/foo", "/foo2"], 3:["/foo/bar", "/foo2/bar"], 4:["/foo/bar/test"]} + */ + all = make(map[int][]map[string]interface{}) + min int + max int + //prefixKey string + ) + + var cli *clientv3.Client + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + var uinfo *userInfo + if v != nil { + uinfo = v.(*userInfo) + cli, _ = ek.newClient(uinfo) + defer cli.Close() + + permissions, e := ek.getPermissionPrefix(uinfo.host, uinfo.uname, originKey) + if e != nil { + io.WriteString(w, e.Error()) + return + } + + // parent + var ( + presp *clientv3.GetResponse + err error + ) + if originKey != ek.config.separator { + presp, err = cli.Get(context.Background(), originKey) + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + dataByte, _ := json.Marshal(data) + io.WriteString(w, string(dataByte)) + return + } + } + if originKey == ek.config.separator { + min = 1 + //prefixKey = separator + } else { + min = len(strings.Split(originKey, ek.config.separator)) + //prefixKey = originKey + } + max = min + all[min] = []map[string]interface{}{{"key": originKey}} + if presp != nil && presp.Count != 0 { + all[min][0]["value"] = string(presp.Kvs[0].Value) + all[min][0]["ttl"] = ek.getTTL(cli, presp.Kvs[0].Lease) + all[min][0]["createdIndex"] = presp.Kvs[0].CreateRevision + all[min][0]["modifiedIndex"] = presp.Kvs[0].ModRevision + } + all[min][0]["nodes"] = make([]map[string]interface{}, 0) + + for _, p := range permissions { + key, rangeEnd := p[0], p[1] + //child + var resp *clientv3.GetResponse + if rangeEnd != "" { + resp, err = cli.Get(context.Background(), key, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) + } else { + resp, err = cli.Get(context.Background(), key, clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) + } + if err != nil { + data["errorCode"] = 500 + data["message"] = err.Error() + dataByte, _ := json.Marshal(data) + io.WriteString(w, string(dataByte)) + return + } + + for _, kv := range resp.Kvs { + if string(kv.Key) == ek.config.separator { + continue + } + keys := strings.Split(string(kv.Key), ek.config.separator) // /foo/bar + for i := range keys { // ["", "foo", "bar"] + k := strings.Join(keys[0:i+1], ek.config.separator) + if k == "" { + continue + } + node := map[string]interface{}{"key": k} + if node["key"].(string) == string(kv.Key) { + node["value"] = string(kv.Value) + if key == string(kv.Key) { + node["ttl"] = ek.getTTL(cli, kv.Lease) + } else { + node["ttl"] = 0 + } + node["createdIndex"] = kv.CreateRevision + node["modifiedIndex"] = kv.ModRevision + } + level := len(strings.Split(k, ek.config.separator)) + if level > max { + max = level + } + + if _, ok := all[level]; !ok { + all[level] = make([]map[string]interface{}, 0) + } + levelNodes := all[level] + var isExist bool + for _, n := range levelNodes { + if n["key"].(string) == k { + isExist = true + } + } + if !isExist { + node["nodes"] = make([]map[string]interface{}, 0) + all[level] = append(all[level], node) + } + } + } + } + + // parent-child mapping + for i := max; i > min; i-- { + for _, a := range all[i] { + for _, pa := range all[i-1] { + if i == 2 { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } else { + if strings.HasPrefix(a["key"].(string), pa["key"].(string)+ek.config.separator) { + pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) + pa["dir"] = true + } + } + } + } + } + } + data = all[min][0] + if dataByte, err := json.Marshal(map[string]interface{}{"node": data}); err != nil { + io.WriteString(w, err.Error()) + } else { + io.WriteString(w, string(dataByte)) + } +} + +func (ek *Etcdkeeper) Del(w http.ResponseWriter, r *http.Request) { + cli := ek.getClient(w, r) + defer cli.Close() + key := r.FormValue("key") + dir := r.FormValue("dir") + log.Println("DELETE", "v3", key) + + if _, err := cli.Delete(context.Background(), key); err != nil { + io.WriteString(w, err.Error()) + return + } + + if dir == "true" { + if _, err := cli.Delete(context.Background(), key+ek.config.separator, clientv3.WithPrefix()); err != nil { + io.WriteString(w, err.Error()) + return + } + } + io.WriteString(w, "ok") +} + +func (ek *Etcdkeeper) getTTL(cli *clientv3.Client, lease int64) int64 { + resp, err := cli.Lease.TimeToLive(context.Background(), clientv3.LeaseID(lease)) + if err != nil { + return 0 + } + if resp.TTL == -1 { + return 0 + } + return resp.TTL +} + +func (ek *Etcdkeeper) getClient(w http.ResponseWriter, r *http.Request) *clientv3.Client { + sess := ek.sessmgr.SessionStart(w, r) + v := sess.Get("uinfo") + if v != nil { + uinfo := v.(*userInfo) + c, _ := ek.newClient(uinfo) + return c + } + return nil +} + +func (ek *Etcdkeeper) newClient(uinfo *userInfo) (*clientv3.Client, error) { + endpoints := []string{uinfo.host} + var err error + + // use tls if usetls is true + var tlsConfig *tls.Config + if ek.config.usetls { + tlsInfo := transport.TLSInfo{ + CertFile: ek.config.cert, + KeyFile: ek.config.keyfile, + TrustedCAFile: ek.config.cacert, + } + tlsConfig, err = tlsInfo.ClientConfig() + if err != nil { + log.Println(err.Error()) + } + } + + conf := clientv3.Config{ + Endpoints: endpoints, + TLS: tlsConfig, + DialTimeout: time.Second * time.Duration(ek.config.connectTimeout), + DialOptions: []grpc.DialOption{grpc.WithBlock(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ek.config.grpcMaxMsgSize))}, + } + if ek.config.useAuth { + conf.Username = uinfo.uname + conf.Password = uinfo.passwd + } + + var c *clientv3.Client + c, err = clientv3.New(conf) + if err != nil { + return nil, err + } + return c, nil +} + +func (ek *Etcdkeeper) getPermissionPrefix(host, uname, key string) ([][]string, error) { + if !ek.config.useAuth { + return [][]string{{key, "p"}}, nil // No auth return all + } else { + if uname == "root" { + return [][]string{{key, "p"}}, nil + } + rootUser := ek.rootUsers[host] + rootCli, err := ek.newClient(rootUser) + if err != nil { + return nil, err + } + defer rootCli.Close() + + if resp, err := rootCli.UserList(context.Background()); err != nil { + return nil, err + } else { + // Find user permissions + set := make(map[string]string) + for _, u := range resp.Users { + if u == uname { + ur, err := rootCli.UserGet(context.Background(), u) + if err != nil { + return nil, err + } + for _, r := range ur.Roles { + rr, err := rootCli.RoleGet(context.Background(), r) + if err != nil { + return nil, err + } + for _, p := range rr.Perm { + set[string(p.Key)] = string(p.RangeEnd) + } + } + break + } + } + var pers [][]string + for k, v := range set { + pers = append(pers, []string{k, v}) + } + return pers, nil + } + } +} + +func (ek *Etcdkeeper) getInfo(host string) map[string]string { + info := make(map[string]string) + uinfo := ek.rootUsers[host] + rootClient, err := ek.newClient(uinfo) + if err != nil { + log.Println(err) + return info + } + defer rootClient.Close() + + status, err := rootClient.Status(context.Background(), host) + if err != nil { + log.Fatal(err) + } + mems, err := rootClient.MemberList(context.Background()) + if err != nil { + log.Fatal(err) + } + kb := 1024 + mb := kb * 1024 + gb := mb * 1024 + var sizeStr string + for _, m := range mems.Members { + if m.ID == status.Leader { + info["version"] = status.Version + gn, rem1 := ek.size(int(status.DbSize), gb) + mn, rem2 := ek.size(rem1, mb) + kn, bn := ek.size(rem2, kb) + if sizeStr != "" { + sizeStr += " " + } + if gn > 0 { + info["size"] = fmt.Sprintf("%dG", gn) + } else { + if mn > 0 { + info["size"] = fmt.Sprintf("%dM", mn) + } else { + if kn > 0 { + info["size"] = fmt.Sprintf("%dK", kn) + } else { + info["size"] = fmt.Sprintf("%dByte", bn) + } + } + } + info["name"] = m.GetName() + break + } + } + return info +} diff --git a/src/etcdkeeper/main.go b/src/etcdkeeper/main.go index 41e14ef..944c80d 100644 --- a/src/etcdkeeper/main.go +++ b/src/etcdkeeper/main.go @@ -1,67 +1,30 @@ package main import ( - "context" - "crypto/tls" "embed" - "encoding/json" "etcdkeeper/internal" - "etcdkeeper/session" - _ "etcdkeeper/session/providers/memory" + "etcdkeeper/internal/etcdkeeper" "flag" - "fmt" - "io" "io/fs" "log" "net/http" "os" - "sort" "strconv" - "strings" - "sync" - "time" - - "github.com/coreos/etcd/client" - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/pkg/transport" - "google.golang.org/grpc" -) - -var ( - sep = flag.String("sep", "/", "separator") - separator = "" - usetls = flag.Bool("usetls", false, "use tls") - cacert = flag.String("cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle (v3)") - cert = flag.String("cert", "", "identify secure client using this TLS certificate file (v3)") - keyfile = flag.String("key", "", "identify secure client using this TLS key file (v3)") - useAuth = flag.Bool("auth", false, "use auth") - connectTimeout = flag.Int("timeout", 5, "ETCD client connect timeout") - grpcMaxMsgSize = flag.Int("grpcMaxMsgSize", 64*1024*1024, "ETCDv3 grpc client max receive msg size") - rootUsers = make(map[string]*userInfo) // host:rootUser - rootUesrsV2 = make(map[string]*userInfo) // host:rootUser - - sessmgr *session.Manager - mu sync.Mutex ) -type etcdkeeperConfig struct { -} - -type userInfo struct { - host string - uname string - passwd string -} - //go:embed assets/* var assets embed.FS func main() { + etcdkeeperConfig := &etcdkeeper.EtcdkeeperConfig{} + etcdkeeperConfig.ParseFlag() + host := flag.String("h", "0.0.0.0", "host name or ip address") port := flag.Int("p", 8080, "port") flag.CommandLine.Parse(os.Args[1:]) - separator = *sep + + etcdk := etcdkeeper.NewEtcdKeeper(etcdkeeperConfig) middleware := func(fns ...func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { @@ -79,32 +42,22 @@ func main() { // v2 //http.HandleFunc(*name, v2request) - http.HandleFunc("/v2/separator", middleware(nothing, getSeparator)) - http.HandleFunc("/v2/connect", middleware(nothing, connectV2)) - http.HandleFunc("/v2/put", middleware(nothing, putV2)) - http.HandleFunc("/v2/get", middleware(nothing, getV2)) - http.HandleFunc("/v2/delete", middleware(nothing, delV2)) + http.HandleFunc("/v2/separator", middleware(nothing, etcdk.GetSeparator)) + http.HandleFunc("/v2/connect", middleware(nothing, etcdk.ConnectV2)) + http.HandleFunc("/v2/put", middleware(nothing, etcdk.PutV2)) + http.HandleFunc("/v2/get", middleware(nothing, etcdk.GetV2)) + http.HandleFunc("/v2/delete", middleware(nothing, etcdk.DelV2)) // dirctory mode - http.HandleFunc("/v2/getpath", middleware(nothing, getPathV2)) + http.HandleFunc("/v2/getpath", middleware(nothing, etcdk.GetPathV2)) // v3 - http.HandleFunc("/v3/separator", middleware(nothing, getSeparator)) - http.HandleFunc("/v3/connect", middleware(nothing, connect)) - http.HandleFunc("/v3/put", middleware(nothing, put)) - http.HandleFunc("/v3/get", middleware(nothing, get)) - http.HandleFunc("/v3/delete", middleware(nothing, del)) + http.HandleFunc("/v3/separator", middleware(nothing, etcdk.GetSeparator)) + http.HandleFunc("/v3/connect", middleware(nothing, etcdk.Connect)) + http.HandleFunc("/v3/put", middleware(nothing, etcdk.Put)) + http.HandleFunc("/v3/get", middleware(nothing, etcdk.Get)) + http.HandleFunc("/v3/delete", middleware(nothing, etcdk.Del)) // dirctory mode - http.HandleFunc("/v3/getpath", middleware(nothing, getPath)) - - var err error - // Session managment - sessmgr, err = session.NewManager("memory", "_etcdkeeper_session", 86400) - if err != nil { - log.Fatal(err) - } - time.AfterFunc(86400*time.Second, func() { - sessmgr.GC() - }) + http.HandleFunc("/v3/getpath", middleware(nothing, etcdk.GetPath)) // static directory server staticFS, err := fs.Sub(assets, "assets/static") @@ -116,7 +69,7 @@ func main() { if err != nil { log.Fatalf("Fail to load templates assets resource directory : %v", err) } - templateHandler := internal.NewTemplateServer(templateFS, &etcdkeeperConfig{}) + templateHandler := internal.NewTemplateServer(templateFS, etcdkeeperConfig) http.HandleFunc("/", middleware(templateHandler.ServeHTTP, staticHandler.ServeHTTP)) @@ -131,958 +84,3 @@ func main() { func nothing(_ http.ResponseWriter, _ *http.Request) { // Nothing } - -// v2 api -func connectV2(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - sess := sessmgr.SessionStart(w, r) - host := strings.TrimSpace(r.FormValue("host")) - uname := r.FormValue("uname") - passwd := r.FormValue("passwd") - if !strings.HasPrefix(host, "http") { - host = "http://" + host - } - - if *useAuth { - _, ok := rootUesrsV2[host] - if !ok && uname != "root" { - b, _ := json.Marshal(map[string]interface{}{"status": "root"}) - io.WriteString(w, string(b)) - return - } - if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status": "login"}) - io.WriteString(w, string(b)) - return - } - } - - if uinfo, ok := sess.Get("uinfov2").(*userInfo); ok { - if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { - info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) - io.WriteString(w, string(b)) - return - } - } - - uinfo := &userInfo{host: host, uname: uname, passwd: passwd} - _, err := newClientV2(uinfo) - if err != nil { - log.Println(r.Method, "v2", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) - io.WriteString(w, string(b)) - return - } - _ = sess.Set("uinfov2", uinfo) - - if *useAuth { - if uname == "root" { - rootUesrsV2[host] = uinfo - } - } else { - rootUesrsV2[host] = uinfo - } - log.Println(r.Method, "v2", "connect success.") - info := getInfoV2(host) - b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) - io.WriteString(w, string(b)) -} - -func putV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - value := r.FormValue("value") - ttl := r.FormValue("ttl") - dir := r.FormValue("dir") - log.Println("PUT", "v2", key) - - kapi := client.NewKeysAPI(getClientV2(w, r)) - - var isDir bool - if dir != "" { - isDir, _ = strconv.ParseBool(dir) - } - var err error - data := make(map[string]interface{}) - if ttl != "" { - var sec int64 - sec, err = strconv.ParseInt(ttl, 10, 64) - if err != nil { - log.Println(err.Error()) - } - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{TTL: time.Duration(sec) * time.Second, Dir: isDir}) - } else { - _, err = kapi.Set(context.Background(), key, value, &client.SetOptions{Dir: isDir}) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp, err := kapi.Get(context.Background(), key, &client.GetOptions{Recursive: true, Sort: true}); err != nil { - data["errorCode"] = err.Error() - } else { - if resp.Node != nil { - node := make(map[string]interface{}) - node["key"] = resp.Node.Key - node["value"] = resp.Node.Value - node["dir"] = resp.Node.Dir - node["ttl"] = resp.Node.TTL - node["createdIndex"] = resp.Node.CreatedIndex - node["modifiedIndex"] = resp.Node.ModifiedIndex - data["node"] = node - } - } - } - - var dataByte []byte - if dataByte, err = json.Marshal(data); err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func getV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - data := make(map[string]interface{}) - log.Println("GET", "v2", key) - - var cli client.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfov2") - var uinfo *userInfo - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClientV2(uinfo) - kapi := client.NewKeysAPI(cli) - - var permissions [][]string - if r.FormValue("prefix") == "true" { - var e error - permissions, e = getPermissionPrefixV2(uinfo.host, uinfo.uname, key) - if e != nil { - io.WriteString(w, e.Error()) - return - } - } else { - permissions = [][]string{{key, ""}} - } - - var ( - min, max int - ) - if key == separator { - min = 1 - } else { - min = len(strings.Split(key, separator)) - } - max = min - all := make(map[int][]map[string]interface{}) - if key == separator { - all[min] = []map[string]interface{}{{"key": key, "value": "", "dir": true, "nodes": make([]map[string]interface{}, 0)}} - } - for _, p := range permissions { - pKey, pRange := p[0], p[1] - var opt *client.GetOptions - if pRange != "" { - if pRange == "c" { - pKey += separator - } - opt = &client.GetOptions{Recursive: true, Sort: true} - } - if resp, err := kapi.Get(context.Background(), pKey, opt); err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp.Node == nil { - data["errorCode"] = 500 - data["message"] = "The node does not exist." - } else { - max = getNode(resp.Node, key, all, min, max) - } - } - } - - //b, _ := json.MarshalIndent(all, "", " ") - //fmt.Println(string(b)) - - // parent-child mapping - for i := max; i > min; i-- { - for _, a := range all[i] { - for _, pa := range all[i-1] { - if i == 2 { // The last is root - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string)+separator) { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } - } - } - } - } - - for _, n := range all[min] { - if n["key"] == key { - nodesSort(n) - data["node"] = n - break - } - } - } - - var dataByte []byte - var err error - if dataByte, err = json.Marshal(data); err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func nodesSort(node map[string]interface{}) { - if v, ok := node["nodes"]; ok && v != nil { - a := v.([]map[string]interface{}) - if len(a) != 0 { - for i := 0; i < len(a)-1; i++ { - nodesSort(a[i]) - for j := i + 1; j < len(a); j++ { - if a[j]["key"].(string) < a[i]["key"].(string) { - a[i], a[j] = a[j], a[i] - } - } - } - nodesSort(a[len(a)-1]) - } - } -} - -func getNode(node *client.Node, selKey string, all map[int][]map[string]interface{}, min, max int) int { - keys := strings.Split(node.Key, separator) // /foo/bar - if len(keys) < min && strings.HasPrefix(node.Key, selKey) { - return max - } - for i := range keys { // ["", "foo", "bar"] - k := strings.Join(keys[0:i+1], separator) - if k == "" { - continue - } - nodeMap := map[string]interface{}{"key": k, "dir": true, "nodes": make([]map[string]interface{}, 0)} - if k == node.Key { - nodeMap["value"] = node.Value - nodeMap["dir"] = node.Dir - nodeMap["ttl"] = node.TTL - nodeMap["createdIndex"] = node.CreatedIndex - nodeMap["modifiedIndex"] = node.ModifiedIndex - } - keylevel := len(strings.Split(k, separator)) - if keylevel > max { - max = keylevel - } - - if _, ok := all[keylevel]; !ok { - all[keylevel] = make([]map[string]interface{}, 0) - } - var isExist bool - for _, n := range all[keylevel] { - if n["key"].(string) == k { - isExist = true - } - } - if !isExist { - all[keylevel] = append(all[keylevel], nodeMap) - } - } - - if len(node.Nodes) != 0 { - for _, n := range node.Nodes { - max = getNode(n, selKey, all, min, max) - } - } - return max -} - -func delV2(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - dir := r.FormValue("dir") - log.Println("DELETE", "v2", key) - - kapi := client.NewKeysAPI(getClientV2(w, r)) - - isDir, _ := strconv.ParseBool(dir) - if isDir { - if _, err := kapi.Delete(context.Background(), key, &client.DeleteOptions{Recursive: true, Dir: true}); err != nil { - io.WriteString(w, err.Error()) - return - } - } else { - if _, err := kapi.Delete(context.Background(), key, nil); err != nil { - io.WriteString(w, err.Error()) - return - } - } - - io.WriteString(w, "ok") -} - -func getPathV2(w http.ResponseWriter, r *http.Request) { - getV2(w, r) -} - -func getClientV2(w http.ResponseWriter, r *http.Request) client.Client { - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfov2") - if v != nil { - uinfo := v.(*userInfo) - c, _ := newClientV2(uinfo) - return c - } - return nil -} - -func newClientV2(uinfo *userInfo) (client.Client, error) { - cfg := client.Config{ - Endpoints: []string{uinfo.host}, - HeaderTimeoutPerRequest: time.Second * time.Duration(*connectTimeout), - } - if *useAuth { - cfg.Username = uinfo.uname - cfg.Password = uinfo.passwd - } - - c, err := client.New(cfg) - if err != nil { - return nil, err - } - return c, nil -} - -func getPermissionPrefixV2(host, uname, key string) ([][]string, error) { - if !*useAuth { - return [][]string{{key, "p"}}, nil // No auth return all - } else { - if uname == "root" { - return [][]string{{key, "p"}}, nil - } - - if !strings.HasPrefix(host, "http://") { - host = "http://" + host - } - rootUser := rootUesrsV2[host] - rootCli, err := newClientV2(rootUser) - if err != nil { - return nil, err - } - rootUserKapi := client.NewAuthUserAPI(rootCli) - rootRoleKapi := client.NewAuthRoleAPI(rootCli) - - if users, err := rootUserKapi.ListUsers(context.Background()); err != nil { - return nil, err - } else { - // Find user permissions - set := make(map[string]string) - for _, u := range users { - if u == uname { - user, err := rootUserKapi.GetUser(context.Background(), u) - if err != nil { - return nil, err - } - for _, r := range user.Roles { - role, err := rootRoleKapi.GetRole(context.Background(), r) - if err != nil { - return nil, err - } - for _, ks := range role.Permissions.KV.Read { - var k string - if strings.HasSuffix(ks, "*") { - k = ks[:len(ks)-1] - set[k] = "p" - } else if strings.HasSuffix(ks, "/*") { - k = ks[:len(ks)-2] - set[k] = "c" - } else { - if _, ok := set[ks]; !ok { - set[ks] = "" - } - } - } - } - break - } - } - var pers [][]string - var ks []string - for k := range set { - ks = append(ks, k) - } - sort.Strings(ks) - for _, k := range ks { - pers = append(pers, []string{k, set[k]}) - } - return pers, nil - } - } -} - -func getInfoV2(host string) map[string]string { - if !strings.HasPrefix(host, "http://") { - host = "http://" + host - } - info := make(map[string]string) - uinfo, ok := rootUesrsV2[host] - if ok { - rootClient, err := newClientV2(uinfo) - if err != nil { - log.Println(err) - return info - } - ver, err := rootClient.GetVersion(context.Background()) - if err != nil { - log.Fatal(err) - } - memberKapi := client.NewMembersAPI(rootClient) - member, err := memberKapi.Leader(context.Background()) - if err != nil { - log.Fatal(err) - } - info["version"] = ver.Server - info["name"] = member.Name - info["size"] = "unknow" // FIXME: How get? - } - return info -} - -// v3 api -func connect(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - sess := sessmgr.SessionStart(w, r) - host := r.FormValue("host") - uname := r.FormValue("uname") - passwd := r.FormValue("passwd") - - if *useAuth { - if _, ok := rootUsers[host]; !ok && uname != "root" { // no root user - b, _ := json.Marshal(map[string]interface{}{"status": "root"}) - io.WriteString(w, string(b)) - return - } - if uname == "" || passwd == "" { - b, _ := json.Marshal(map[string]interface{}{"status": "login"}) - io.WriteString(w, string(b)) - return - } - } - - if uinfo, ok := sess.Get("uinfo").(*userInfo); ok { - if host == uinfo.host && uname == uinfo.uname && passwd == uinfo.passwd { - info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) - io.WriteString(w, string(b)) - return - } - } - - uinfo := &userInfo{host: host, uname: uname, passwd: passwd} - c, err := newClient(uinfo) - if err != nil { - log.Println(r.Method, "v3", "connect fail.") - b, _ := json.Marshal(map[string]interface{}{"status": "error", "message": err.Error()}) - io.WriteString(w, string(b)) - return - } - defer c.Close() - _ = sess.Set("uinfo", uinfo) - - if *useAuth { - if uname == "root" { - rootUsers[host] = uinfo - } - } else { - rootUsers[host] = uinfo - } - log.Println(r.Method, "v3", "connect success.") - info := getInfo(host) - b, _ := json.Marshal(map[string]interface{}{"status": "running", "info": info}) - io.WriteString(w, string(b)) -} - -func put(w http.ResponseWriter, r *http.Request) { - cli := getClient(w, r) - defer cli.Close() - key := r.FormValue("key") - value := r.FormValue("value") - ttl := r.FormValue("ttl") - log.Println("PUT", "v3", key) - - var err error - data := make(map[string]interface{}) - if ttl != "" { - var sec int64 - sec, err = strconv.ParseInt(ttl, 10, 64) - if err != nil { - log.Println(err.Error()) - } - var leaseResp *clientv3.LeaseGrantResponse - leaseResp, err = cli.Grant(context.TODO(), sec) - if err == nil && leaseResp != nil { - _, err = cli.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID)) - } - } else { - _, err = cli.Put(context.Background(), key, value) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp, err := cli.Get(context.Background(), key); err != nil { - data["errorCode"] = 500 - data["errorCode"] = err.Error() - } else { - if resp.Count > 0 { - kv := resp.Kvs[0] - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - node["ttl"] = getTTL(cli, kv.Lease) - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - data["node"] = node - } - } - } - - var dataByte []byte - if dataByte, err = json.Marshal(data); err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func get(w http.ResponseWriter, r *http.Request) { - data := make(map[string]interface{}) - key := r.FormValue("key") - log.Println("GET", "v3", key) - - var cli *clientv3.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - var uinfo *userInfo - - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClient(uinfo) - defer cli.Close() - - permissions, e := getPermissionPrefix(uinfo.host, uinfo.uname, key) - if e != nil { - io.WriteString(w, e.Error()) - return - } - if r.FormValue("prefix") == "true" { - pnode := make(map[string]interface{}) - pnode["key"] = key - pnode["nodes"] = make([]map[string]interface{}, 0) - for _, p := range permissions { - var ( - resp *clientv3.GetResponse - err error - ) - if p[1] != "" { - prefixKey := p[0] - if p[0] == "/" { - prefixKey = "" - } - resp, err = cli.Get(context.Background(), prefixKey, clientv3.WithPrefix()) - } else { - resp, err = cli.Get(context.Background(), p[0]) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - for _, kv := range resp.Kvs { - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - if key == string(kv.Key) { - node["ttl"] = getTTL(cli, kv.Lease) - } else { - node["ttl"] = 0 - } - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - nodes := pnode["nodes"].([]map[string]interface{}) - pnode["nodes"] = append(nodes, node) - } - } - } - data["node"] = pnode - } else { - if resp, err := cli.Get(context.Background(), key); err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - } else { - if resp.Count > 0 { - kv := resp.Kvs[0] - node := make(map[string]interface{}) - node["key"] = string(kv.Key) - node["value"] = string(kv.Value) - node["dir"] = false - node["ttl"] = getTTL(cli, kv.Lease) - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - data["node"] = node - } else { - data["errorCode"] = 500 - data["message"] = "The node does not exist." - } - } - } - } - - var dataByte []byte - var err error - if dataByte, err = json.Marshal(data); err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func getPath(w http.ResponseWriter, r *http.Request) { - originKey := r.FormValue("key") - log.Println("GET", "v3", originKey) - var ( - data = make(map[string]interface{}) - /* - {1:["/"], 2:["/foo", "/foo2"], 3:["/foo/bar", "/foo2/bar"], 4:["/foo/bar/test"]} - */ - all = make(map[int][]map[string]interface{}) - min int - max int - //prefixKey string - ) - - var cli *clientv3.Client - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - var uinfo *userInfo - if v != nil { - uinfo = v.(*userInfo) - cli, _ = newClient(uinfo) - defer cli.Close() - - permissions, e := getPermissionPrefix(uinfo.host, uinfo.uname, originKey) - if e != nil { - io.WriteString(w, e.Error()) - return - } - - // parent - var ( - presp *clientv3.GetResponse - err error - ) - if originKey != separator { - presp, err = cli.Get(context.Background(), originKey) - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - dataByte, _ := json.Marshal(data) - io.WriteString(w, string(dataByte)) - return - } - } - if originKey == separator { - min = 1 - //prefixKey = separator - } else { - min = len(strings.Split(originKey, separator)) - //prefixKey = originKey - } - max = min - all[min] = []map[string]interface{}{{"key": originKey}} - if presp != nil && presp.Count != 0 { - all[min][0]["value"] = string(presp.Kvs[0].Value) - all[min][0]["ttl"] = getTTL(cli, presp.Kvs[0].Lease) - all[min][0]["createdIndex"] = presp.Kvs[0].CreateRevision - all[min][0]["modifiedIndex"] = presp.Kvs[0].ModRevision - } - all[min][0]["nodes"] = make([]map[string]interface{}, 0) - - for _, p := range permissions { - key, rangeEnd := p[0], p[1] - //child - var resp *clientv3.GetResponse - if rangeEnd != "" { - resp, err = cli.Get(context.Background(), key, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) - } else { - resp, err = cli.Get(context.Background(), key, clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) - } - if err != nil { - data["errorCode"] = 500 - data["message"] = err.Error() - dataByte, _ := json.Marshal(data) - io.WriteString(w, string(dataByte)) - return - } - - for _, kv := range resp.Kvs { - if string(kv.Key) == separator { - continue - } - keys := strings.Split(string(kv.Key), separator) // /foo/bar - for i := range keys { // ["", "foo", "bar"] - k := strings.Join(keys[0:i+1], separator) - if k == "" { - continue - } - node := map[string]interface{}{"key": k} - if node["key"].(string) == string(kv.Key) { - node["value"] = string(kv.Value) - if key == string(kv.Key) { - node["ttl"] = getTTL(cli, kv.Lease) - } else { - node["ttl"] = 0 - } - node["createdIndex"] = kv.CreateRevision - node["modifiedIndex"] = kv.ModRevision - } - level := len(strings.Split(k, separator)) - if level > max { - max = level - } - - if _, ok := all[level]; !ok { - all[level] = make([]map[string]interface{}, 0) - } - levelNodes := all[level] - var isExist bool - for _, n := range levelNodes { - if n["key"].(string) == k { - isExist = true - } - } - if !isExist { - node["nodes"] = make([]map[string]interface{}, 0) - all[level] = append(all[level], node) - } - } - } - } - - // parent-child mapping - for i := max; i > min; i-- { - for _, a := range all[i] { - for _, pa := range all[i-1] { - if i == 2 { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } else { - if strings.HasPrefix(a["key"].(string), pa["key"].(string)+separator) { - pa["nodes"] = append(pa["nodes"].([]map[string]interface{}), a) - pa["dir"] = true - } - } - } - } - } - } - data = all[min][0] - if dataByte, err := json.Marshal(map[string]interface{}{"node": data}); err != nil { - io.WriteString(w, err.Error()) - } else { - io.WriteString(w, string(dataByte)) - } -} - -func del(w http.ResponseWriter, r *http.Request) { - cli := getClient(w, r) - defer cli.Close() - key := r.FormValue("key") - dir := r.FormValue("dir") - log.Println("DELETE", "v3", key) - - if _, err := cli.Delete(context.Background(), key); err != nil { - io.WriteString(w, err.Error()) - return - } - - if dir == "true" { - if _, err := cli.Delete(context.Background(), key+separator, clientv3.WithPrefix()); err != nil { - io.WriteString(w, err.Error()) - return - } - } - io.WriteString(w, "ok") -} - -func getTTL(cli *clientv3.Client, lease int64) int64 { - resp, err := cli.Lease.TimeToLive(context.Background(), clientv3.LeaseID(lease)) - if err != nil { - return 0 - } - if resp.TTL == -1 { - return 0 - } - return resp.TTL -} - -func getSeparator(w http.ResponseWriter, _ *http.Request) { - io.WriteString(w, separator) -} - -func getClient(w http.ResponseWriter, r *http.Request) *clientv3.Client { - sess := sessmgr.SessionStart(w, r) - v := sess.Get("uinfo") - if v != nil { - uinfo := v.(*userInfo) - c, _ := newClient(uinfo) - return c - } - return nil -} - -func newClient(uinfo *userInfo) (*clientv3.Client, error) { - endpoints := []string{uinfo.host} - var err error - - // use tls if usetls is true - var tlsConfig *tls.Config - if *usetls { - tlsInfo := transport.TLSInfo{ - CertFile: *cert, - KeyFile: *keyfile, - TrustedCAFile: *cacert, - } - tlsConfig, err = tlsInfo.ClientConfig() - if err != nil { - log.Println(err.Error()) - } - } - - conf := clientv3.Config{ - Endpoints: endpoints, - TLS: tlsConfig, - DialTimeout: time.Second * time.Duration(*connectTimeout), - DialOptions: []grpc.DialOption{grpc.WithBlock(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*grpcMaxMsgSize))}, - } - if *useAuth { - conf.Username = uinfo.uname - conf.Password = uinfo.passwd - } - - var c *clientv3.Client - c, err = clientv3.New(conf) - if err != nil { - return nil, err - } - return c, nil -} - -func getPermissionPrefix(host, uname, key string) ([][]string, error) { - if !*useAuth { - return [][]string{{key, "p"}}, nil // No auth return all - } else { - if uname == "root" { - return [][]string{{key, "p"}}, nil - } - rootUser := rootUsers[host] - rootCli, err := newClient(rootUser) - if err != nil { - return nil, err - } - defer rootCli.Close() - - if resp, err := rootCli.UserList(context.Background()); err != nil { - return nil, err - } else { - // Find user permissions - set := make(map[string]string) - for _, u := range resp.Users { - if u == uname { - ur, err := rootCli.UserGet(context.Background(), u) - if err != nil { - return nil, err - } - for _, r := range ur.Roles { - rr, err := rootCli.RoleGet(context.Background(), r) - if err != nil { - return nil, err - } - for _, p := range rr.Perm { - set[string(p.Key)] = string(p.RangeEnd) - } - } - break - } - } - var pers [][]string - for k, v := range set { - pers = append(pers, []string{k, v}) - } - return pers, nil - } - } -} - -func getInfo(host string) map[string]string { - info := make(map[string]string) - uinfo := rootUsers[host] - rootClient, err := newClient(uinfo) - if err != nil { - log.Println(err) - return info - } - defer rootClient.Close() - - status, err := rootClient.Status(context.Background(), host) - if err != nil { - log.Fatal(err) - } - mems, err := rootClient.MemberList(context.Background()) - if err != nil { - log.Fatal(err) - } - kb := 1024 - mb := kb * 1024 - gb := mb * 1024 - var sizeStr string - for _, m := range mems.Members { - if m.ID == status.Leader { - info["version"] = status.Version - gn, rem1 := size(int(status.DbSize), gb) - mn, rem2 := size(rem1, mb) - kn, bn := size(rem2, kb) - if sizeStr != "" { - sizeStr += " " - } - if gn > 0 { - info["size"] = fmt.Sprintf("%dG", gn) - } else { - if mn > 0 { - info["size"] = fmt.Sprintf("%dM", mn) - } else { - if kn > 0 { - info["size"] = fmt.Sprintf("%dK", kn) - } else { - info["size"] = fmt.Sprintf("%dByte", bn) - } - } - } - info["name"] = m.GetName() - break - } - } - return info -} - -func size(num int, unit int) (n, rem int) { - return num / unit, num - (num/unit)*unit -} From a744a9c46c4434523bd135d9a48ecea1ab5dc014 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Wed, 25 Aug 2021 16:19:31 +0200 Subject: [PATCH 11/13] feat: add template into index.html for default property --- .../assets/templates/etcdkeeper/index.html | 12 ++++++------ src/etcdkeeper/internal/etcdkeeper/config.go | 19 +++++++++++++++++-- src/etcdkeeper/main.go | 4 ++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/etcdkeeper/assets/templates/etcdkeeper/index.html b/src/etcdkeeper/assets/templates/etcdkeeper/index.html index fbd7f5f..47f4cfe 100644 --- a/src/etcdkeeper/assets/templates/etcdkeeper/index.html +++ b/src/etcdkeeper/assets/templates/etcdkeeper/index.html @@ -120,7 +120,7 @@

ETCD Keeper 0.7.6 -
+
text
toml
ini
@@ -146,18 +146,18 @@

Date: Wed, 25 Aug 2021 22:31:55 +0200 Subject: [PATCH 12/13] Add github workflow to create release and build/push docker image to github package --- .github/workflows/release.yml | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d5227aa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: release + +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + github_release: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + # Duplication with docker.yml workflow cause the previous automatic release don't trigger a release created event + docker: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Cache Docker layers + uses: actions/cache@v2 + id: cache + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - + name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - + name: Login to docker registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and Push docker image + id: docker_build + uses: docker/build-push-action@v2.7.0 + with: + push: true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: "type=local,dest=/tmp/.buildx-cache" + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + - + name: Clear + if: always() + run: | + rm -f ${HOME}/.docker/config.json From 73edf5481cb64cdfd3a83b0fa91d4f466ba09c06 Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Thu, 26 Aug 2021 03:52:51 +0200 Subject: [PATCH 13/13] add goreleaser to github workflow release asset --- .github/workflows/release.yml | 41 ++++++++++++++++++++++++++--------- .gitignore | 3 ++- .goreleaser.yml | 36 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 .goreleaser.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5227aa..75fe935 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,18 +14,39 @@ jobs: name: Create Release runs-on: ubuntu-latest steps: - - name: Checkout code + - + name: Checkout code uses: actions/checkout@v2 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false + fetch-depth: 0 + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + - + name: Cache Go modules + uses: actions/cache@v1 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - + name: Tests + run: | + cd ./src/etcdkeeper + go mod tidy + go test -v ./... + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + if: success() && startsWith(github.ref, 'refs/tags/') + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Duplication with docker.yml workflow cause the previous automatic release don't trigger a release created event docker: diff --git a/.gitignore b/.gitignore index 0060276..a52a788 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ bin # Package Files *.war -*.ear \ No newline at end of file +*.ear +dist/ diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..232aba3 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,36 @@ +project_name: etcdkeeper + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + ignore: + - goos: windows + goarch: arm64 + dir: ./src/etcdkeeper +archives: + - replacements: + 386: i386 + amd64: x86_64 + format: zip + name_template: "{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}_{{ .Arch }}" + wrap_in_directory: true + files: + - LICENSE + - README.md +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-snapshot" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:'