github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/cli_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/x509" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "runtime" 11 "testing" 12 13 cliconfig "github.com/docker/cli/cli/config" 14 "github.com/docker/cli/cli/config/configfile" 15 "github.com/docker/cli/cli/flags" 16 clitypes "github.com/docker/cli/types" 17 "github.com/docker/docker/api" 18 "github.com/docker/docker/api/types" 19 "github.com/docker/docker/client" 20 "github.com/pkg/errors" 21 "gotest.tools/assert" 22 is "gotest.tools/assert/cmp" 23 "gotest.tools/env" 24 "gotest.tools/fs" 25 ) 26 27 func TestNewAPIClientFromFlags(t *testing.T) { 28 host := "unix://path" 29 if runtime.GOOS == "windows" { 30 host = "npipe://./" 31 } 32 opts := &flags.CommonOptions{Hosts: []string{host}} 33 configFile := &configfile.ConfigFile{ 34 HTTPHeaders: map[string]string{ 35 "My-Header": "Custom-Value", 36 }, 37 } 38 apiclient, err := NewAPIClientFromFlags(opts, configFile) 39 assert.NilError(t, err) 40 assert.Check(t, is.Equal(host, apiclient.DaemonHost())) 41 42 expectedHeaders := map[string]string{ 43 "My-Header": "Custom-Value", 44 "User-Agent": UserAgent(), 45 } 46 assert.Check(t, is.DeepEqual(expectedHeaders, apiclient.(*client.Client).CustomHTTPHeaders())) 47 assert.Check(t, is.Equal(api.DefaultVersion, apiclient.ClientVersion())) 48 } 49 50 func TestNewAPIClientFromFlagsForDefaultSchema(t *testing.T) { 51 host := ":2375" 52 opts := &flags.CommonOptions{Hosts: []string{host}} 53 configFile := &configfile.ConfigFile{ 54 HTTPHeaders: map[string]string{ 55 "My-Header": "Custom-Value", 56 }, 57 } 58 apiclient, err := NewAPIClientFromFlags(opts, configFile) 59 assert.NilError(t, err) 60 assert.Check(t, is.Equal("tcp://localhost"+host, apiclient.DaemonHost())) 61 62 expectedHeaders := map[string]string{ 63 "My-Header": "Custom-Value", 64 "User-Agent": UserAgent(), 65 } 66 assert.Check(t, is.DeepEqual(expectedHeaders, apiclient.(*client.Client).CustomHTTPHeaders())) 67 assert.Check(t, is.Equal(api.DefaultVersion, apiclient.ClientVersion())) 68 } 69 70 func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { 71 customVersion := "v3.3.3" 72 defer env.Patch(t, "DOCKER_API_VERSION", customVersion)() 73 defer env.Patch(t, "DOCKER_HOST", ":2375")() 74 75 opts := &flags.CommonOptions{} 76 configFile := &configfile.ConfigFile{} 77 apiclient, err := NewAPIClientFromFlags(opts, configFile) 78 assert.NilError(t, err) 79 assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion())) 80 } 81 82 type fakeClient struct { 83 client.Client 84 pingFunc func() (types.Ping, error) 85 version string 86 negotiated bool 87 } 88 89 func (c *fakeClient) Ping(_ context.Context) (types.Ping, error) { 90 return c.pingFunc() 91 } 92 93 func (c *fakeClient) ClientVersion() string { 94 return c.version 95 } 96 97 func (c *fakeClient) NegotiateAPIVersionPing(types.Ping) { 98 c.negotiated = true 99 } 100 101 func TestInitializeFromClient(t *testing.T) { 102 defaultVersion := "v1.55" 103 104 var testcases = []struct { 105 doc string 106 pingFunc func() (types.Ping, error) 107 expectedServer ServerInfo 108 negotiated bool 109 }{ 110 { 111 doc: "successful ping", 112 pingFunc: func() (types.Ping, error) { 113 return types.Ping{Experimental: true, OSType: "linux", APIVersion: "v1.30"}, nil 114 }, 115 expectedServer: ServerInfo{HasExperimental: true, OSType: "linux"}, 116 negotiated: true, 117 }, 118 { 119 doc: "failed ping, no API version", 120 pingFunc: func() (types.Ping, error) { 121 return types.Ping{}, errors.New("failed") 122 }, 123 expectedServer: ServerInfo{HasExperimental: true}, 124 }, 125 { 126 doc: "failed ping, with API version", 127 pingFunc: func() (types.Ping, error) { 128 return types.Ping{APIVersion: "v1.33"}, errors.New("failed") 129 }, 130 expectedServer: ServerInfo{HasExperimental: true}, 131 negotiated: true, 132 }, 133 } 134 135 for _, testcase := range testcases { 136 t.Run(testcase.doc, func(t *testing.T) { 137 apiclient := &fakeClient{ 138 pingFunc: testcase.pingFunc, 139 version: defaultVersion, 140 } 141 142 cli := &DockerCli{client: apiclient} 143 cli.initializeFromClient() 144 assert.Check(t, is.DeepEqual(testcase.expectedServer, cli.serverInfo)) 145 assert.Check(t, is.Equal(testcase.negotiated, apiclient.negotiated)) 146 }) 147 } 148 } 149 150 func TestExperimentalCLI(t *testing.T) { 151 defaultVersion := "v1.55" 152 153 var testcases = []struct { 154 doc string 155 configfile string 156 expectedExperimentalCLI bool 157 }{ 158 { 159 doc: "default", 160 configfile: `{}`, 161 expectedExperimentalCLI: false, 162 }, 163 { 164 doc: "experimental", 165 configfile: `{ 166 "experimental": "enabled" 167 }`, 168 expectedExperimentalCLI: true, 169 }, 170 } 171 172 for _, testcase := range testcases { 173 t.Run(testcase.doc, func(t *testing.T) { 174 dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile)) 175 defer dir.Remove() 176 apiclient := &fakeClient{ 177 version: defaultVersion, 178 pingFunc: func() (types.Ping, error) { 179 return types.Ping{Experimental: true, OSType: "linux", APIVersion: defaultVersion}, nil 180 }, 181 } 182 183 cli := &DockerCli{client: apiclient, err: os.Stderr} 184 cliconfig.SetDir(dir.Path()) 185 err := cli.Initialize(flags.NewClientOptions()) 186 assert.NilError(t, err) 187 assert.Check(t, is.Equal(testcase.expectedExperimentalCLI, cli.ClientInfo().HasExperimental)) 188 }) 189 } 190 } 191 192 func TestGetClientWithPassword(t *testing.T) { 193 expected := "password" 194 195 var testcases = []struct { 196 doc string 197 password string 198 retrieverErr error 199 retrieverGiveup bool 200 newClientErr error 201 expectedErr string 202 }{ 203 { 204 doc: "successful connect", 205 password: expected, 206 }, 207 { 208 doc: "password retriever exhausted", 209 retrieverGiveup: true, 210 retrieverErr: errors.New("failed"), 211 expectedErr: "private key is encrypted, but could not get passphrase", 212 }, 213 { 214 doc: "password retriever error", 215 retrieverErr: errors.New("failed"), 216 expectedErr: "failed", 217 }, 218 { 219 doc: "newClient error", 220 newClientErr: errors.New("failed to connect"), 221 expectedErr: "failed to connect", 222 }, 223 } 224 225 for _, testcase := range testcases { 226 t.Run(testcase.doc, func(t *testing.T) { 227 passRetriever := func(_, _ string, _ bool, attempts int) (passphrase string, giveup bool, err error) { 228 // Always return an invalid pass first to test iteration 229 switch attempts { 230 case 0: 231 return "something else", false, nil 232 default: 233 return testcase.password, testcase.retrieverGiveup, testcase.retrieverErr 234 } 235 } 236 237 newClient := func(currentPassword string) (client.APIClient, error) { 238 if testcase.newClientErr != nil { 239 return nil, testcase.newClientErr 240 } 241 if currentPassword == expected { 242 return &client.Client{}, nil 243 } 244 return &client.Client{}, x509.IncorrectPasswordError 245 } 246 247 _, err := getClientWithPassword(passRetriever, newClient) 248 if testcase.expectedErr != "" { 249 assert.ErrorContains(t, err, testcase.expectedErr) 250 return 251 } 252 253 assert.NilError(t, err) 254 }) 255 } 256 } 257 258 func TestNewDockerCliAndOperators(t *testing.T) { 259 // Test default operations and also overriding default ones 260 cli, err := NewDockerCli( 261 WithContentTrust(true), 262 WithContainerizedClient(func(string) (clitypes.ContainerizedClient, error) { return nil, nil }), 263 ) 264 assert.NilError(t, err) 265 // Check streams are initialized 266 assert.Check(t, cli.In() != nil) 267 assert.Check(t, cli.Out() != nil) 268 assert.Check(t, cli.Err() != nil) 269 assert.Equal(t, cli.ContentTrustEnabled(), true) 270 client, err := cli.NewContainerizedEngineClient("") 271 assert.NilError(t, err) 272 assert.Equal(t, client, nil) 273 274 // Apply can modify a dockerCli after construction 275 inbuf := bytes.NewBuffer([]byte("input")) 276 outbuf := bytes.NewBuffer(nil) 277 errbuf := bytes.NewBuffer(nil) 278 cli.Apply( 279 WithInputStream(ioutil.NopCloser(inbuf)), 280 WithOutputStream(outbuf), 281 WithErrorStream(errbuf), 282 ) 283 // Check input stream 284 inputStream, err := ioutil.ReadAll(cli.In()) 285 assert.NilError(t, err) 286 assert.Equal(t, string(inputStream), "input") 287 // Check output stream 288 fmt.Fprintf(cli.Out(), "output") 289 outputStream, err := ioutil.ReadAll(outbuf) 290 assert.NilError(t, err) 291 assert.Equal(t, string(outputStream), "output") 292 // Check error stream 293 fmt.Fprintf(cli.Err(), "error") 294 errStream, err := ioutil.ReadAll(errbuf) 295 assert.NilError(t, err) 296 assert.Equal(t, string(errStream), "error") 297 } 298 299 func TestInitializeShouldAlwaysCreateTheContextStore(t *testing.T) { 300 cli, err := NewDockerCli() 301 assert.NilError(t, err) 302 assert.NilError(t, cli.Initialize(flags.NewClientOptions(), WithInitializeClient(func(cli *DockerCli) (client.APIClient, error) { 303 return client.NewClientWithOpts() 304 }))) 305 assert.Check(t, cli.ContextStore() != nil) 306 }