44 "context"
55 "fmt"
66 "io"
7+ "io/ioutil"
8+ "regexp"
79 "sort"
810 "strings"
911
@@ -64,13 +66,6 @@ func NewInfoCommand(dockerCli command.Cli) *cobra.Command {
6466func runInfo (cmd * cobra.Command , dockerCli command.Cli , opts * infoOptions ) error {
6567 var info info
6668
67- ctx := context .Background ()
68- if dinfo , err := dockerCli .Client ().Info (ctx ); err == nil {
69- info .Info = & dinfo
70- } else {
71- info .ServerErrors = append (info .ServerErrors , err .Error ())
72- }
73-
7469 info .ClientInfo = & clientInfo {
7570 Context : dockerCli .CurrentContext (),
7671 Debug : debug .IsEnabled (),
@@ -81,12 +76,60 @@ func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error
8176 info .ClientErrors = append (info .ClientErrors , err .Error ())
8277 }
8378
79+ if needsServerInfo (opts .format , info ) {
80+ ctx := context .Background ()
81+ if dinfo , err := dockerCli .Client ().Info (ctx ); err == nil {
82+ info .Info = & dinfo
83+ } else {
84+ info .ServerErrors = append (info .ServerErrors , err .Error ())
85+ }
86+ }
87+
8488 if opts .format == "" {
8589 return prettyPrintInfo (dockerCli , info )
8690 }
8791 return formatInfo (dockerCli , info , opts .format )
8892}
8993
94+ // placeHolders does a rudimentary match for possible placeholders in a
95+ // template, matching a '.', followed by an letter (a-z/A-Z).
96+ var placeHolders = regexp .MustCompile (`\.[a-zA-Z]` )
97+
98+ // needsServerInfo detects if the given template uses any server information.
99+ // If only client-side information is used in the template, we can skip
100+ // connecting to the daemon. This allows (e.g.) to only get cli-plugin
101+ // information, without also making a (potentially expensive) API call.
102+ func needsServerInfo (template string , info info ) bool {
103+ if len (template ) == 0 || placeHolders .FindString (template ) == "" {
104+ // The template is empty, or does not contain formatting fields
105+ // (e.g. `table` or `raw` or `{{ json .}}`). Assume we need server-side
106+ // information to render it.
107+ return true
108+ }
109+
110+ // A template is provided and has at least one field set.
111+ tmpl , err := templates .NewParse ("" , template )
112+ if err != nil {
113+ // ignore parsing errors here, and let regular code handle them
114+ return true
115+ }
116+
117+ type sparseInfo struct {
118+ ClientInfo * clientInfo `json:",omitempty"`
119+ ClientErrors []string `json:",omitempty"`
120+ }
121+
122+ // This constructs an "info" object that only has the client-side fields.
123+ err = tmpl .Execute (ioutil .Discard , sparseInfo {
124+ ClientInfo : info .ClientInfo ,
125+ ClientErrors : info .ClientErrors ,
126+ })
127+ // If executing the template failed, it means the template needs
128+ // server-side information as well. If it succeeded without server-side
129+ // information, we don't need to make API calls to collect that information.
130+ return err != nil
131+ }
132+
90133func prettyPrintInfo (dockerCli command.Cli , info info ) error {
91134 fmt .Fprintln (dockerCli .Out (), "Client:" )
92135 if info .ClientInfo != nil {
0 commit comments