package main import ( "encoding/json" "errors" "fmt" "reflect" "sort" "strings" "time" "github.com/ipfs-cluster/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{}) { print := func(o interface{}) { j, err := json.MarshalIndent(o, "", " ") checkErr("generating json output", err) fmt.Printf("%s\n", j) } switch r := obj.(type) { case chan api.Pin: for o := range r { print(o) } case chan api.GlobalPinInfo: for o := range r { print(o) } case chan api.ID: for o := range r { print(o) } default: print(obj) } } 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.Alert: textFormatPrintAlert(r) case chan api.ID: for item := range r { textFormatObject(item) } case chan api.GlobalPinInfo: for item := range r { textFormatObject(item) } case chan 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 []api.Alert: for _, item := range r { textFormatObject(item) } default: checkErr("", errors.New("unsupported type returned"+reflect.TypeOf(r).String())) } } 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", txt) fmt.Fprintf(&b, " | Attempts: %d", v.AttemptCount) fmt.Fprintf(&b, " | Priority: %t", v.PriorityPin) fmt.Fprintf(&b, "\n") } 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.IsPinEverywhere() { 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 := "∞" if !obj.ExpireAt.IsZero() { expireAt = obj.ExpireAt.Format("2006-01-02 15:04:05") } fmt.Printf(" | Exp: %s", expireAt) added := "unknown" if !obj.Timestamp.IsZero() { added = obj.Timestamp.Format("2006-01-02 15:04:05") } fmt.Printf(" | Added: %s\n", added) } 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) { v := obj.Value if obj.Name == "freespace" && obj.Weight > 0 { v = humanize.Bytes(uint64(obj.Weight)) } fmt.Printf("%s | %s: %s | Expires in: %s\n", peer.Encode(obj.Peer), obj.Name, v, humanize.Time(time.Unix(0, obj.Expire))) } func textFormatPrintAlert(obj api.Alert) { fmt.Printf("%s: %s. Expired at: %s. Triggered at: %s\n", obj.Peer, obj.Name, humanize.Time(time.Unix(0, obj.Expire)), humanize.Time(obj.TriggeredAt), ) } 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") }