package main import ( "encoding/json" "errors" "fmt" "sort" "strconv" "strings" "time" "github.com/ipfs/ipfs-cluster/api" peer "github.com/libp2p/go-libp2p-core/peer" humanize "github.com/dustin/go-humanize" ) type addedOutputQuiet struct { *api.AddedOutput quiet bool } func jsonFormatObject(resp interface{}) { switch r := resp.(type) { case nil: return case []*addedOutputQuiet: // print original objects as in JSON it makes // no sense to have a human "quiet" output var actual []*api.AddedOutput for _, s := range r { actual = append(actual, s.AddedOutput) } jsonFormatPrint(actual) default: jsonFormatPrint(resp) } } func jsonFormatPrint(obj interface{}) { j, err := json.MarshalIndent(obj, "", " ") checkErr("generating json output", err) fmt.Printf("%s\n", j) } func textFormatObject(resp interface{}) { switch r := resp.(type) { case nil: return case string: fmt.Println(resp) case *api.ID: textFormatPrintID(r) case *api.GlobalPinInfo: textFormatPrintGPInfo(r) case *api.Pin: textFormatPrintPin(r) case *api.AddedOutput: textFormatPrintAddedOutput(r) case *addedOutputQuiet: textFormatPrintAddedOutputQuiet(r) case *api.Version: textFormatPrintVersion(r) case *api.Error: textFormatPrintError(r) case *api.Metric: textFormatPrintMetric(r) case []*api.ID: for _, item := range r { textFormatObject(item) } case []*api.GlobalPinInfo: for _, item := range r { textFormatObject(item) } case []*api.Pin: for _, item := range r { textFormatObject(item) } case []*api.AddedOutput: for _, item := range r { textFormatObject(item) } case []*addedOutputQuiet: for _, item := range r { textFormatObject(item) } case []*api.Metric: for _, item := range r { textFormatObject(item) } case *api.GlobalRepoGC: textFormatPrintGlobalRepoGC(r) case []string: for _, item := range r { textFormatObject(item) } case map[string]api.Alert: for i := range resp.(map[string]api.Alert) { fmt.Printf("peer is down: %s\n", i) } default: checkErr("", errors.New("unsupported type returned")) } } func textFormatPrintID(obj *api.ID) { if obj.Error != "" { fmt.Printf("%s | ERROR: %s\n", obj.ID.Pretty(), obj.Error) return } fmt.Printf( "%s | %s | Sees %d other peers\n", obj.ID.Pretty(), obj.Peername, len(obj.ClusterPeers)-1, ) addrs := make(sort.StringSlice, 0, len(obj.Addresses)) for _, a := range obj.Addresses { addrs = append(addrs, a.String()) } addrs.Sort() fmt.Println(" > Addresses:") for _, a := range addrs { fmt.Printf(" - %s\n", a) } if obj.IPFS.Error != "" { fmt.Printf(" > IPFS ERROR: %s\n", obj.IPFS.Error) return } ipfsAddrs := make(sort.StringSlice, 0, len(obj.Addresses)) for _, a := range obj.IPFS.Addresses { ipfsAddrs = append(ipfsAddrs, a.String()) } ipfsAddrs.Sort() fmt.Printf(" > IPFS: %s\n", obj.IPFS.ID.Pretty()) for _, a := range ipfsAddrs { fmt.Printf(" - %s\n", a) } } func textFormatPrintGPInfo(obj *api.GlobalPinInfo) { var b strings.Builder peers := make([]string, 0, len(obj.PeerMap)) for k := range obj.PeerMap { peers = append(peers, k) } sort.Strings(peers) fmt.Fprintf(&b, "%s", obj.Cid) if obj.Name != "" { fmt.Fprintf(&b, " | %s", obj.Name) } b.WriteString(":\n") for _, k := range peers { v := obj.PeerMap[k] if len(v.PeerName) > 0 { fmt.Fprintf(&b, " > %-20s : %s", v.PeerName, strings.ToUpper(v.Status.String())) } else { fmt.Fprintf(&b, " > %-20s : %s", k, strings.ToUpper(v.Status.String())) } if v.Error != "" { fmt.Fprintf(&b, ": %s", v.Error) } txt, _ := v.TS.MarshalText() fmt.Fprintf(&b, " | %s\n", txt) } fmt.Print(b.String()) } func textFormatPrintVersion(obj *api.Version) { fmt.Println(obj.Version) } func textFormatPrintPin(obj *api.Pin) { t := strings.ToUpper(obj.Type.String()) if obj.Mode == api.PinModeDirect { t = t + "-DIRECT" } fmt.Printf("%s | %s | %s | ", obj.Cid, obj.Name, t) if obj.ReplicationFactorMin < 0 { fmt.Printf("Repl. Factor: -1 | Allocations: [everywhere]") } else { sortAlloc := api.PeersToStrings(obj.Allocations) sort.Strings(sortAlloc) fmt.Printf("Repl. Factor: %d--%d | Allocations: %s", obj.ReplicationFactorMin, obj.ReplicationFactorMax, sortAlloc) } var recStr string switch obj.MaxDepth { case 0: recStr = "Direct" case -1: recStr = "Recursive" default: recStr = fmt.Sprintf("Recursive-%d", obj.MaxDepth) } fmt.Printf(" | %s", recStr) fmt.Printf(" | Metadata:") if len(obj.Metadata) == 0 { fmt.Printf(" no") } else { fmt.Printf(" yes") } expireAt := "Exp: ∞" if !obj.ExpireAt.IsZero() { expireAt = humanize.Time(obj.ExpireAt) } fmt.Printf(" | %s\n", expireAt) } func textFormatPrintAddedOutput(obj *api.AddedOutput) { fmt.Printf("added %s %s\n", obj.Cid, obj.Name) } func textFormatPrintAddedOutputQuiet(obj *addedOutputQuiet) { if obj.quiet { fmt.Printf("%s\n", obj.AddedOutput.Cid) } else { textFormatPrintAddedOutput(obj.AddedOutput) } } func textFormatPrintMetric(obj *api.Metric) { if obj.Name == "freespace" { u, err := strconv.ParseUint(obj.Value, 10, 64) checkErr("parsing to uint64", err) fmt.Printf("%s | freespace: %s | Expires in: %s\n", peer.Encode(obj.Peer), humanize.Bytes(u), humanize.Time(time.Unix(0, obj.Expire))) return } fmt.Printf("%s | %s | Expires in: %s\n", peer.Encode(obj.Peer), obj.Name, humanize.Time(time.Unix(0, obj.Expire))) } func textFormatPrintGlobalRepoGC(obj *api.GlobalRepoGC) { peers := make(sort.StringSlice, 0, len(obj.PeerMap)) for peer := range obj.PeerMap { peers = append(peers, peer) } peers.Sort() for _, peer := range peers { item := obj.PeerMap[peer] // If peer name is set, use it instead of peer ID. if len(item.Peername) > 0 { peer = item.Peername } if item.Error != "" { fmt.Printf("%-15s | ERROR: %s\n", peer, item.Error) } else { fmt.Printf("%-15s\n", peer) } fmt.Printf(" > CIDs:\n") for _, key := range item.Keys { if key.Error != "" { // key.Key will be empty fmt.Printf(" - ERROR: %s\n", key.Error) continue } fmt.Printf(" - %s\n", key.Key) } } } func textFormatPrintError(obj *api.Error) { fmt.Printf("An error occurred:\n") fmt.Printf(" Code: %d\n", obj.Code) fmt.Printf(" Message: %s\n", obj.Message) } func trackerStatusAllString() string { var strs []string for _, st := range api.TrackerStatusAll() { strs = append(strs, " - "+st.String()) } sort.Strings(strs) return strings.Join(strs, "\n") }