RedemptionC opened a new issue #2045:
URL: https://github.com/apache/apisix-dashboard/issues/2045


   ## context
   this is a subtask of a project from open summer 
2021,[here](https://summer.iscas.ac.cn/#/org/prodetail/210050252?lang=en) for 
full detail.
   dashboard related:
   >Add a new command line tool to the Apache APISIX Dashboard. When the tool 
is executed, the latest data will be obtained from the ETCD in real time, and 
compared with the local cache item by item to verify whether there is data out 
of sync.
   
   ## what we really need to do for this task(IMO)
   I used the dashboard and read the source code,I found that what we need to 
do is to check whether items in cache(sync.Map) are the same as those in ETCD
   And we need to add a cobra command to do such things
   
   ## possible solution
   **use the migrate/export API**
   this API will return all items in cache,we can get those info from it,and 
then traverse them,compare them to corresponding items in ETCD 
   and here is a draft implementation(which only checks the consumers):
   ```go
   package store
   
   import (
        "context"
        "encoding/json"
        "fmt"
        "github.com/apisix/manager-api/internal/conf"
        "github.com/apisix/manager-api/internal/core/entity"
        "github.com/apisix/manager-api/internal/core/storage"
        "github.com/apisix/manager-api/internal/log"
        "io/ioutil"
        "net/http"
   )
   
   type DataSet struct {
        Counsumers    []*entity.Consumer      `json:"Counsumers,omitempty"`
        Routes        []*entity.Route         `json:"Routes,omitempty"`
        Services      []*entity.Service       `json:"Services,omitempty"`
        SSLs          []*entity.SSL           `json:"SSLs,omitempty"`
        Upstreams     []*entity.Upstream      `json:"Upstreams,omitempty"`
        Scripts       []*entity.Script        `json:"Scripts,omitempty"`
        GlobalPlugins []*entity.GlobalPlugins `json:"GlobalPlugins,omitempty"`
        PluginConfigs []*entity.PluginConfig  `json:"PluginConfigs,omitempty"`
   }
   
   func AreEqualJSON(s1, s2 string) (bool, error) {
        fmt.Printf("compare:\ncache: %s\nand\netcd: %s\n", s1, s2)
        var o1 interface{}
        var o2 interface{}
   
        var err error
        err = json.Unmarshal([]byte(s1), &o1)
        if err != nil {
                return false, fmt.Errorf("Error mashalling string 1 :: %s", 
err.Error())
        }
        err = json.Unmarshal([]byte(s2), &o2)
        if err != nil {
                return false, fmt.Errorf("Error mashalling string 2 :: %s", 
err.Error())
        }
   
        return reflect.DeepEqual(o1, o2), nil
   }
   
   func CacheVerify() {
        //fmt.Println("in func cacheVerify")
        rsp, err := 
http.Get("http://localhost:9000/apisix/admin/migrate/export";)
        if err != nil {
                fmt.Println("get result from migrate/export failed")
                return
        }
        data, err := ioutil.ReadAll(rsp.Body)
        if err != nil {
                fmt.Println(err)
        }
        fmt.Printf("len of data is %d\n", len(data))
        var responseObj DataSet
        err = json.Unmarshal(data[:len(data)-4], &responseObj)
        if err != nil {
                fmt.Println(err)
                return
        }
        //fmt.Println(responseObj)
        conf.InitConf()
        consumers := responseObj.Counsumers
        for _, v := range consumers {
                s, err := json.Marshal(v)
                if err != nil {
                        fmt.Printf("json marsharl failed : %v\n", err)
                        continue
                }
                cacheValue := string(s)
                key := fmt.Sprintf("/apisix/consumers/%s", v.Username)
                if err := storage.InitETCDClient(conf.ETCDConfig); err != nil {
                        log.Errorf("init etcd client fail: %w", err)
                        return
                }
                val, err := storage.GenEtcdStorage().Get(context.TODO(), key)
                if err != nil {
                        fmt.Printf("etcd get failed %v \n", err)
                        continue
                }
                etcdValue := val
                cmp, err := AreEqualJSON(cacheValue, etcdValue)
                if err != nil {
                        fmt.Printf("compare json failed %v\n", err)
                }
                fmt.Printf("cache value and etcd value for key(%s) are the same 
: %v\n", key, cmp)
   
        }
   
   }
   
   ```
   here is the output from cli:
   **it seems to be working well**
   ```
   ❯ go run main.go cache-verify
   len of data is 1735
   compare:
   cache: 
{"username":"dashboard","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627746151,"update_time":1627746151}
   and
   etcd: 
{"username":"dashboard","plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627746151,"create_time":1627746151}
   cache value and etcd value for key(/apisix/consumers/dashboard) are the same 
: true
   compare:
   cache: 
{"username":"dumb","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627906781,"update_time":1627906781}
   and
   etcd: 
{"plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627906781,"create_time":1627906781,"username":"dumb"}
   cache value and etcd value for key(/apisix/consumers/dumb) are the same : 
true
   compare:
   cache: 
{"username":"john","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627731085,"update_time":1627738858}
   and
   etcd: 
{"username":"john","plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627738858,"create_time":1627731085}
   cache value and etcd value for key(/apisix/consumers/john) are the same : 
true
   compare:
   cache: 
{"username":"red","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627738877,"update_time":1627738933}
   and
   etcd: 
{"username":"red","plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627738933,"create_time":1627738877}
   cache value and etcd value for key(/apisix/consumers/red) are the same : true
   compare:
   cache: 
{"username":"test","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627908039,"update_time":1627908039}
   and
   etcd: 
{"plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627908039,"create_time":1627908039,"username":"test"}
   cache value and etcd value for key(/apisix/consumers/test) are the same : 
true
   compare:
   cache: 
{"username":"andy","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627739045,"update_time":1627744978}
   and
   etcd: 
{"username":"andy","plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627744978,"create_time":1627739045}
   cache value and etcd value for key(/apisix/consumers/andy) are the same : 
true
   compare:
   cache: 
{"username":"apisix","plugins":{"key-auth":{"key":"key-of-john"}},"create_time":1627746172,"update_time":1627746172}
   and
   etcd: 
{"username":"apisix","plugins":{"key-auth":{"key":"key-of-john"}},"update_time":1627746172,"create_time":1627746172}
   cache value and etcd value for key(/apisix/consumers/apisix) are the same : 
true
   
   ```
   ##  thoughts on another "possible" solution
   I once thought another solution also reasonable:
   we don't rely on running dashboard,we just initialize everything like 
function manageapi() did
   then we traverse items in cache,compare them to corresponding items in ETCD
   **but I recently found this one  not ressonable: when we init everything,we 
read items from etcd to cache,if we compare them to items in ETCD,how could 
them be different?**
   
   ## things confuse me 
   * I'm not really familiar with this tool's use case,can it rely on a 
existing running dashboard(manager API),i.e.,first we run manager API,then we 
run this tool?
   * is the proposed solution reasonable ?
   * when I code the draft implementation,I found that I need to traverse the 
data of a struct(the dataSet),is there a way to do this,instead of hard-coded 
style?
   * is there better way to do this?
   
   Any advice will be greatly appreciated!
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to