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  }