github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/crossmodel/listformatter.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package crossmodel 5 6 import ( 7 "fmt" 8 "io" 9 "sort" 10 "strings" 11 12 "github.com/juju/ansiterm" 13 "github.com/juju/errors" 14 15 "github.com/juju/juju/cmd/output" 16 "github.com/juju/juju/core/relation" 17 ) 18 19 // formatListSummary returns a tabular summary of remote application offers or 20 // errors out if parameter is not of expected type. 21 func formatListSummary(writer io.Writer, value interface{}) error { 22 offers, ok := value.(offeredApplications) 23 if !ok { 24 return errors.Errorf("expected value of type %T, got %T", offers, value) 25 } 26 return formatListEndpointsSummary(writer, offers) 27 } 28 29 type offerItems []ListOfferItem 30 31 // formatListEndpointsSummary returns a tabular summary of listed applications' endpoints. 32 func formatListEndpointsSummary(writer io.Writer, offers offeredApplications) error { 33 tw := output.TabWriter(writer) 34 w := output.Wrapper{tw} 35 36 // Sort offers by source then application name. 37 allOffers := offerItems{} 38 for _, offer := range offers { 39 allOffers = append(allOffers, offer) 40 } 41 sort.Sort(allOffers) 42 43 w.Println("Offer", "Application", "Charm", "Connected", "Store", "URL", "Endpoint", "Interface", "Role") 44 for _, offer := range allOffers { 45 // Sort endpoints alphabetically. 46 endpoints := []string{} 47 for endpoint := range offer.Endpoints { 48 endpoints = append(endpoints, endpoint) 49 } 50 sort.Strings(endpoints) 51 52 for i, endpointName := range endpoints { 53 endpoint := offer.Endpoints[endpointName] 54 if i == 0 { 55 // As there is some information about offer and its endpoints, 56 // only display offer information once when the first endpoint is displayed. 57 totalConnectedCount := len(offer.Connections) 58 activeConnectedCount := 0 59 for _, conn := range offer.Connections { 60 if conn.Status.Current == relation.Joined.String() { 61 activeConnectedCount++ 62 } 63 } 64 w.Println(offer.OfferName, offer.ApplicationName, offer.CharmURL, 65 fmt.Sprintf("%v/%v", activeConnectedCount, totalConnectedCount), 66 offer.Source, offer.OfferURL, endpointName, endpoint.Interface, endpoint.Role) 67 continue 68 } 69 // Subsequent lines only need to display endpoint information. 70 // This will display less noise. 71 w.Println("", "", "", "", "", "", endpointName, endpoint.Interface, endpoint.Role) 72 } 73 } 74 tw.Flush() 75 return nil 76 } 77 78 func (o offerItems) Len() int { return len(o) } 79 func (o offerItems) Swap(i, j int) { o[i], o[j] = o[j], o[i] } 80 func (o offerItems) Less(i, j int) bool { 81 return o[i].OfferName < o[j].OfferName 82 } 83 84 func formatListTabular(writer io.Writer, value interface{}) error { 85 offers, ok := value.(offeredApplications) 86 if !ok { 87 return errors.Errorf("expected value of type %T, got %T", offers, value) 88 } 89 return formatListEndpointsTabular(writer, offers) 90 } 91 92 // formatListEndpointsTabular returns a tabular summary of listed applications' endpoints. 93 func formatListEndpointsTabular(writer io.Writer, offers offeredApplications) error { 94 tw := output.TabWriter(writer) 95 w := output.Wrapper{tw} 96 97 // Sort offers by source then application name. 98 allOffers := offerItems{} 99 for _, offer := range offers { 100 allOffers = append(allOffers, offer) 101 } 102 sort.Sort(allOffers) 103 104 w.Println("Offer", "User", "Relation id", "Status", "Endpoint", "Interface", "Role", "Ingress subnets") 105 for _, offer := range allOffers { 106 // Sort endpoints alphabetically. 107 endpoints := []string{} 108 for endpoint := range offer.Endpoints { 109 endpoints = append(endpoints, endpoint) 110 } 111 sort.Strings(endpoints) 112 113 // Sort connections by relation id and username. 114 sort.Sort(byUserRelationId(offer.Connections)) 115 116 // If there are no connections, print am empty row. 117 if len(offer.Connections) == 0 { 118 w.Println(offer.OfferName, "-", "", "", "", "", "", "") 119 } 120 121 for i, conn := range offer.Connections { 122 if i == 0 { 123 w.Print(offer.OfferName) 124 } else { 125 w.Print("") 126 } 127 endpoints := make(map[string]RemoteEndpoint) 128 for alias, ep := range offer.Endpoints { 129 aliasedEp := ep 130 aliasedEp.Name = alias 131 endpoints[ep.Name] = ep 132 } 133 connEp := endpoints[conn.Endpoint] 134 w.Print(conn.Username, conn.RelationId) 135 w.PrintColor(RelationStatusColor(relation.Status(conn.Status.Current)), conn.Status.Current) 136 w.Println(connEp.Name, connEp.Interface, connEp.Role, strings.Join(conn.IngressSubnets, ",")) 137 } 138 } 139 tw.Flush() 140 return nil 141 } 142 143 // RelationStatusColor returns a context used to print the status with the relevant color. 144 func RelationStatusColor(status relation.Status) *ansiterm.Context { 145 switch status { 146 case relation.Joined: 147 return output.GoodHighlight 148 case relation.Suspended: 149 return output.WarningHighlight 150 case relation.Broken, relation.Error: 151 return output.ErrorHighlight 152 } 153 return nil 154 } 155 156 type byUserRelationId []offerConnectionDetails 157 158 func (b byUserRelationId) Len() int { 159 return len(b) 160 } 161 162 func (b byUserRelationId) Less(i, j int) bool { 163 if b[i].Username == b[j].Username { 164 return b[i].RelationId < b[j].RelationId 165 } 166 return b[i].Username < b[j].Username 167 } 168 169 func (b byUserRelationId) Swap(i, j int) { 170 b[i], b[j] = b[j], b[i] 171 }