github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_interface.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "io" 26 "sort" 27 "text/tabwriter" 28 29 "github.com/jessevdk/go-flags" 30 31 "github.com/snapcore/snapd/client" 32 "github.com/snapcore/snapd/i18n" 33 ) 34 35 type cmdInterface struct { 36 clientMixin 37 ShowAttrs bool `long:"attrs"` 38 ShowAll bool `long:"all"` 39 Positionals struct { 40 Interface interfaceName `skip-help:"true"` 41 } `positional-args:"true"` 42 } 43 44 var shortInterfaceHelp = i18n.G("Show details of snap interfaces") 45 var longInterfaceHelp = i18n.G(` 46 The interface command shows details of snap interfaces. 47 48 If no interface name is provided, a list of interface names with at least 49 one connection is shown, or a list of all interfaces if --all is provided. 50 `) 51 52 func init() { 53 addCommand("interface", shortInterfaceHelp, longInterfaceHelp, func() flags.Commander { 54 return &cmdInterface{} 55 }, map[string]string{ 56 // TRANSLATORS: This should not start with a lowercase letter. 57 "attrs": i18n.G("Show interface attributes"), 58 // TRANSLATORS: This should not start with a lowercase letter. 59 "all": i18n.G("Include unused interfaces"), 60 }, []argDesc{{ 61 // TRANSLATORS: This needs to begin with < and end with > 62 name: i18n.G("<interface>"), 63 // TRANSLATORS: This should not start with a lowercase letter. 64 desc: i18n.G("Show details of a specific interface"), 65 }}) 66 } 67 68 func (x *cmdInterface) Execute(args []string) error { 69 if len(args) > 0 { 70 return ErrExtraArgs 71 } 72 73 if x.Positionals.Interface != "" { 74 // Show one interface in detail. 75 name := string(x.Positionals.Interface) 76 ifaces, err := x.client.Interfaces(&client.InterfaceOptions{ 77 Names: []string{name}, 78 Doc: true, 79 Plugs: true, 80 Slots: true, 81 }) 82 if err != nil { 83 return err 84 } 85 if len(ifaces) == 0 { 86 return fmt.Errorf(i18n.G("no such interface")) 87 } 88 x.showOneInterface(ifaces[0]) 89 } else { 90 // Show an overview of available interfaces. 91 ifaces, err := x.client.Interfaces(&client.InterfaceOptions{ 92 Connected: !x.ShowAll, 93 }) 94 if err != nil { 95 return err 96 } 97 if len(ifaces) == 0 { 98 if x.ShowAll { 99 return fmt.Errorf(i18n.G("no interfaces found")) 100 } 101 return fmt.Errorf(i18n.G("no interfaces currently connected")) 102 } 103 x.showManyInterfaces(ifaces) 104 } 105 return nil 106 } 107 108 func (x *cmdInterface) showOneInterface(iface *client.Interface) { 109 w := tabwriter.NewWriter(Stdout, 2, 2, 1, ' ', 0) 110 defer w.Flush() 111 112 fmt.Fprintf(w, "name:\t%s\n", iface.Name) 113 if iface.Summary != "" { 114 fmt.Fprintf(w, "summary:\t%s\n", iface.Summary) 115 } 116 if iface.DocURL != "" { 117 fmt.Fprintf(w, "documentation:\t%s\n", iface.DocURL) 118 } 119 if len(iface.Plugs) > 0 { 120 fmt.Fprintf(w, "plugs:\n") 121 for _, plug := range iface.Plugs { 122 var labelPart string 123 if plug.Label != "" { 124 labelPart = fmt.Sprintf(" (%s)", plug.Label) 125 } 126 if plug.Name == iface.Name { 127 fmt.Fprintf(w, " - %s%s", plug.Snap, labelPart) 128 } else { 129 fmt.Fprintf(w, ` - %s:%s%s`, plug.Snap, plug.Name, labelPart) 130 } 131 // Print a colon which will make the snap:plug element a key-value 132 // yaml object so that we can write the attributes. 133 if len(plug.Attrs) > 0 && x.ShowAttrs { 134 fmt.Fprintf(w, ":\n") 135 x.showAttrs(w, plug.Attrs, " ") 136 } else { 137 fmt.Fprintf(w, "\n") 138 } 139 } 140 } 141 if len(iface.Slots) > 0 { 142 fmt.Fprintf(w, "slots:\n") 143 for _, slot := range iface.Slots { 144 var labelPart string 145 if slot.Label != "" { 146 labelPart = fmt.Sprintf(" (%s)", slot.Label) 147 } 148 if slot.Name == iface.Name { 149 fmt.Fprintf(w, " - %s%s", slot.Snap, labelPart) 150 } else { 151 fmt.Fprintf(w, ` - %s:%s%s`, slot.Snap, slot.Name, labelPart) 152 } 153 // Print a colon which will make the snap:slot element a key-value 154 // yaml object so that we can write the attributes. 155 if len(slot.Attrs) > 0 && x.ShowAttrs { 156 fmt.Fprintf(w, ":\n") 157 x.showAttrs(w, slot.Attrs, " ") 158 } else { 159 fmt.Fprintf(w, "\n") 160 } 161 } 162 } 163 } 164 165 func (x *cmdInterface) showManyInterfaces(infos []*client.Interface) { 166 w := tabWriter() 167 defer w.Flush() 168 fmt.Fprintln(w, i18n.G("Name\tSummary")) 169 for _, iface := range infos { 170 fmt.Fprintf(w, "%s\t%s\n", iface.Name, iface.Summary) 171 } 172 } 173 174 func (x *cmdInterface) showAttrs(w io.Writer, attrs map[string]interface{}, indent string) { 175 if len(attrs) == 0 { 176 return 177 } 178 names := make([]string, 0, len(attrs)) 179 for name := range attrs { 180 names = append(names, name) 181 } 182 sort.Strings(names) 183 for _, name := range names { 184 value := attrs[name] 185 switch value.(type) { 186 case string, bool, json.Number: 187 fmt.Fprintf(w, "%s %s:\t%v\n", indent, name, value) 188 } 189 } 190 }