scctl/pkg/plugin/diagnose/diagnose.go (139 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package diagnose import ( "bytes" "context" "fmt" "github.com/apache/servicecomb-service-center/client" "github.com/apache/servicecomb-service-center/pkg/dump" "github.com/apache/servicecomb-service-center/scctl/etcd" "github.com/apache/servicecomb-service-center/scctl/pkg/cmd" "github.com/spf13/cobra" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" ) const ( service = "service" instance = "instance" ) const ( greater = iota mismatch less ) var typeMap = map[string]string{ service: "/cse-sr/ms/files/", instance: "/cse-sr/inst/files/", } type etcdResponse map[string][]*mvccpb.KeyValue func CommandFunc(_ *cobra.Command, _ []string) { // initialize sc/etcd clients scClient, err := client.NewSCClient(cmd.ScClientConfig) if err != nil { cmd.StopAndExit(cmd.ExitError, err) } etcdClient, err := etcd.NewEtcdClient(EtcdClientConfig) if err != nil { cmd.StopAndExit(cmd.ExitError, err) } defer etcdClient.Close() // query etcd etcdResp, err := getEtcdResponse(context.Background(), etcdClient) if err != nil { cmd.StopAndExit(cmd.ExitError, err) } // query sc cache, scErr := scClient.GetScCache(context.Background()) if scErr != nil { cmd.StopAndExit(cmd.ExitError, scErr) } // diagnose go... details, err := diagnose(cache, etcdResp) if err != nil { fmt.Println(details) // stdout cmd.StopAndExit(cmd.ExitError, err) // stderr } } func getEtcdResponse(ctx context.Context, etcdClient *clientv3.Client) (etcdResponse, error) { etcdResp := make(etcdResponse) for t, prefix := range typeMap { if err := setResponse(ctx, etcdClient, t, prefix, etcdResp); err != nil { return nil, err } } return etcdResp, nil } func setResponse(ctx context.Context, etcdClient *clientv3.Client, key, prefix string, etcdResp etcdResponse) error { resp, err := etcdClient.Get(ctx, prefix, clientv3.WithPrefix()) if err != nil { return err } etcdResp[key] = resp.Kvs return nil } func diagnose(cache *dump.Cache, etcdResp etcdResponse) (details string, err error) { var ( service = ServiceCompareHolder{Cache: cache.Microservices, Kvs: etcdResp[service]} instance = InstanceCompareHolder{Cache: cache.Instances, Kvs: etcdResp[instance]} ) sr := service.Compare() ir := instance.Compare() var ( b bytes.Buffer full bytes.Buffer ) writeResult(&b, &full, sr, ir) if b.Len() > 0 { return full.String(), fmt.Errorf("error: %s", b.String()) } return "", nil } func writeResult(b *bytes.Buffer, full *bytes.Buffer, rss ...*CompareResult) { g, m, l := make(map[string][]string), make(map[string][]string), make(map[string][]string) for _, rs := range rss { for t, arr := range rs.Results { switch t { case greater: g[rs.Name] = arr case mismatch: m[rs.Name] = arr case less: l[rs.Name] = arr } } } i := 0 if s := len(g); s > 0 { i++ header := fmt.Sprintf("%d. found in cache but not in etcd ", i) b.WriteString(header) full.WriteString(header) writeBody(full, g) } if s := len(m); s > 0 { i++ header := fmt.Sprintf("%d. found different between cache and etcd ", i) b.WriteString(header) full.WriteString(header) writeBody(full, m) } if s := len(l); s > 0 { i++ header := fmt.Sprintf("%d. found in etcd but not in cache ", i) b.WriteString(header) full.WriteString(header) writeBody(full, l) } if l := b.Len(); l > 0 { b.Truncate(l - 1) full.Truncate(full.Len() - 1) } } func writeBody(b *bytes.Buffer, r map[string][]string) { b.WriteString("\b, details:\n") for t, v := range r { writeSection(b, t) b.WriteString(fmt.Sprint(v)) b.WriteRune('\n') } } func writeSection(b *bytes.Buffer, t string) { b.WriteString(" ") b.WriteString(t) b.WriteString(": ") }