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  }