github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/context/create_test.go (about)

     1  package context
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/docker/cli/cli/command"
    10  	"github.com/docker/cli/cli/config/configfile"
    11  	"github.com/docker/cli/cli/context/docker"
    12  	"github.com/docker/cli/cli/context/kubernetes"
    13  	"github.com/docker/cli/cli/context/store"
    14  	"github.com/docker/cli/internal/test"
    15  	"gotest.tools/v3/assert"
    16  	"gotest.tools/v3/env"
    17  )
    18  
    19  func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) (*test.FakeCli, func()) {
    20  	dir, err := ioutil.TempDir("", t.Name())
    21  	assert.NilError(t, err)
    22  	storeConfig := store.NewConfig(
    23  		func() interface{} { return &command.DockerContext{} },
    24  		store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
    25  		store.EndpointTypeGetter(kubernetes.KubernetesEndpoint, func() interface{} { return &kubernetes.EndpointMeta{} }),
    26  	)
    27  	store := &command.ContextStoreWithDefault{
    28  		Store: store.New(dir, storeConfig),
    29  		Resolver: func() (*command.DefaultContext, error) {
    30  			return &command.DefaultContext{
    31  				Meta: store.Metadata{
    32  					Endpoints: map[string]interface{}{
    33  						docker.DockerEndpoint: docker.EndpointMeta{
    34  							Host: "unix:///var/run/docker.sock",
    35  						},
    36  					},
    37  					Metadata: command.DockerContext{
    38  						Description:       "",
    39  						StackOrchestrator: command.OrchestratorSwarm,
    40  					},
    41  					Name: command.DefaultContextName,
    42  				},
    43  				TLS: store.ContextTLSData{},
    44  			}, nil
    45  		},
    46  	}
    47  	cleanup := func() {
    48  		os.RemoveAll(dir)
    49  	}
    50  	result := test.NewFakeCli(nil, opts...)
    51  	for _, o := range opts {
    52  		o(result)
    53  	}
    54  	result.SetContextStore(store)
    55  	return result, cleanup
    56  }
    57  
    58  func withCliConfig(configFile *configfile.ConfigFile) func(*test.FakeCli) {
    59  	return func(m *test.FakeCli) {
    60  		m.SetConfigFile(configFile)
    61  	}
    62  }
    63  
    64  func TestCreateInvalids(t *testing.T) {
    65  	cli, cleanup := makeFakeCli(t)
    66  	defer cleanup()
    67  	assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"}))
    68  	tests := []struct {
    69  		options     CreateOptions
    70  		expecterErr string
    71  	}{
    72  		{
    73  			expecterErr: `context name cannot be empty`,
    74  		},
    75  		{
    76  			options: CreateOptions{
    77  				Name: "default",
    78  			},
    79  			expecterErr: `"default" is a reserved context name`,
    80  		},
    81  		{
    82  			options: CreateOptions{
    83  				Name: " ",
    84  			},
    85  			expecterErr: `context name " " is invalid`,
    86  		},
    87  		{
    88  			options: CreateOptions{
    89  				Name: "existing-context",
    90  			},
    91  			expecterErr: `context "existing-context" already exists`,
    92  		},
    93  		{
    94  			options: CreateOptions{
    95  				Name: "invalid-docker-host",
    96  				Docker: map[string]string{
    97  					keyHost: "some///invalid/host",
    98  				},
    99  			},
   100  			expecterErr: `unable to parse docker host`,
   101  		},
   102  		{
   103  			options: CreateOptions{
   104  				Name:                     "invalid-orchestrator",
   105  				DefaultStackOrchestrator: "invalid",
   106  			},
   107  			expecterErr: `specified orchestrator "invalid" is invalid, please use either kubernetes, swarm or all`,
   108  		},
   109  		{
   110  			options: CreateOptions{
   111  				Name:                     "orchestrator-kubernetes-no-endpoint",
   112  				DefaultStackOrchestrator: "kubernetes",
   113  				Docker:                   map[string]string{},
   114  			},
   115  			expecterErr: `cannot specify orchestrator "kubernetes" without configuring a Kubernetes endpoint`,
   116  		},
   117  		{
   118  			options: CreateOptions{
   119  				Name:                     "orchestrator-all-no-endpoint",
   120  				DefaultStackOrchestrator: "all",
   121  				Docker:                   map[string]string{},
   122  			},
   123  			expecterErr: `cannot specify orchestrator "all" without configuring a Kubernetes endpoint`,
   124  		},
   125  	}
   126  	for _, tc := range tests {
   127  		tc := tc
   128  		t.Run(tc.options.Name, func(t *testing.T) {
   129  			err := RunCreate(cli, &tc.options)
   130  			assert.ErrorContains(t, err, tc.expecterErr)
   131  		})
   132  	}
   133  }
   134  
   135  func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
   136  	assert.Equal(t, n+"\n", cli.OutBuffer().String())
   137  	assert.Equal(t, fmt.Sprintf("Successfully created context %q\n", n), cli.ErrBuffer().String())
   138  }
   139  
   140  func TestCreateOrchestratorSwarm(t *testing.T) {
   141  	cli, cleanup := makeFakeCli(t)
   142  	defer cleanup()
   143  
   144  	err := RunCreate(cli, &CreateOptions{
   145  		Name:                     "test",
   146  		DefaultStackOrchestrator: "swarm",
   147  		Docker:                   map[string]string{},
   148  	})
   149  	assert.NilError(t, err)
   150  	assertContextCreateLogging(t, cli, "test")
   151  }
   152  
   153  func TestCreateOrchestratorEmpty(t *testing.T) {
   154  	cli, cleanup := makeFakeCli(t)
   155  	defer cleanup()
   156  
   157  	err := RunCreate(cli, &CreateOptions{
   158  		Name:   "test",
   159  		Docker: map[string]string{},
   160  	})
   161  	assert.NilError(t, err)
   162  	assertContextCreateLogging(t, cli, "test")
   163  }
   164  
   165  func validateTestKubeEndpoint(t *testing.T, s store.Reader, name string) {
   166  	t.Helper()
   167  	ctxMetadata, err := s.GetMetadata(name)
   168  	assert.NilError(t, err)
   169  	kubeMeta := ctxMetadata.Endpoints[kubernetes.KubernetesEndpoint].(kubernetes.EndpointMeta)
   170  	kubeEP, err := kubeMeta.WithTLSData(s, name)
   171  	assert.NilError(t, err)
   172  	assert.Equal(t, "https://someserver", kubeEP.Host)
   173  	assert.Equal(t, "the-ca", string(kubeEP.TLSData.CA))
   174  	assert.Equal(t, "the-cert", string(kubeEP.TLSData.Cert))
   175  	assert.Equal(t, "the-key", string(kubeEP.TLSData.Key))
   176  }
   177  
   178  func createTestContextWithKube(t *testing.T, cli command.Cli) {
   179  	t.Helper()
   180  	revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
   181  	defer revert()
   182  
   183  	err := RunCreate(cli, &CreateOptions{
   184  		Name:                     "test",
   185  		DefaultStackOrchestrator: "all",
   186  		Kubernetes: map[string]string{
   187  			keyFrom: "default",
   188  		},
   189  		Docker: map[string]string{},
   190  	})
   191  	assert.NilError(t, err)
   192  }
   193  
   194  func TestCreateOrchestratorAllKubernetesEndpointFromCurrent(t *testing.T) {
   195  	cli, cleanup := makeFakeCli(t)
   196  	defer cleanup()
   197  	createTestContextWithKube(t, cli)
   198  	assertContextCreateLogging(t, cli, "test")
   199  	validateTestKubeEndpoint(t, cli.ContextStore(), "test")
   200  }
   201  
   202  func TestCreateFromContext(t *testing.T) {
   203  	cases := []struct {
   204  		name                 string
   205  		description          string
   206  		orchestrator         string
   207  		expectedDescription  string
   208  		docker               map[string]string
   209  		kubernetes           map[string]string
   210  		expectedOrchestrator command.Orchestrator
   211  	}{
   212  		{
   213  			name:                 "no-override",
   214  			expectedDescription:  "original description",
   215  			expectedOrchestrator: command.OrchestratorSwarm,
   216  		},
   217  		{
   218  			name:                 "override-description",
   219  			description:          "new description",
   220  			expectedDescription:  "new description",
   221  			expectedOrchestrator: command.OrchestratorSwarm,
   222  		},
   223  		{
   224  			name:                 "override-orchestrator",
   225  			orchestrator:         "kubernetes",
   226  			expectedDescription:  "original description",
   227  			expectedOrchestrator: command.OrchestratorKubernetes,
   228  		},
   229  	}
   230  
   231  	cli, cleanup := makeFakeCli(t)
   232  	defer cleanup()
   233  	revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
   234  	defer revert()
   235  	cli.ResetOutputBuffers()
   236  	assert.NilError(t, RunCreate(cli, &CreateOptions{
   237  		Name:        "original",
   238  		Description: "original description",
   239  		Docker: map[string]string{
   240  			keyHost: "tcp://42.42.42.42:2375",
   241  		},
   242  		Kubernetes: map[string]string{
   243  			keyFrom: "default",
   244  		},
   245  		DefaultStackOrchestrator: "swarm",
   246  	}))
   247  	assertContextCreateLogging(t, cli, "original")
   248  
   249  	cli.ResetOutputBuffers()
   250  	assert.NilError(t, RunCreate(cli, &CreateOptions{
   251  		Name:        "dummy",
   252  		Description: "dummy description",
   253  		Docker: map[string]string{
   254  			keyHost: "tcp://24.24.24.24:2375",
   255  		},
   256  		Kubernetes: map[string]string{
   257  			keyFrom: "default",
   258  		},
   259  		DefaultStackOrchestrator: "swarm",
   260  	}))
   261  	assertContextCreateLogging(t, cli, "dummy")
   262  
   263  	cli.SetCurrentContext("dummy")
   264  
   265  	for _, c := range cases {
   266  		c := c
   267  		t.Run(c.name, func(t *testing.T) {
   268  			cli.ResetOutputBuffers()
   269  			err := RunCreate(cli, &CreateOptions{
   270  				From:                     "original",
   271  				Name:                     c.name,
   272  				Description:              c.description,
   273  				DefaultStackOrchestrator: c.orchestrator,
   274  				Docker:                   c.docker,
   275  				Kubernetes:               c.kubernetes,
   276  			})
   277  			assert.NilError(t, err)
   278  			assertContextCreateLogging(t, cli, c.name)
   279  			newContext, err := cli.ContextStore().GetMetadata(c.name)
   280  			assert.NilError(t, err)
   281  			newContextTyped, err := command.GetDockerContext(newContext)
   282  			assert.NilError(t, err)
   283  			dockerEndpoint, err := docker.EndpointFromContext(newContext)
   284  			assert.NilError(t, err)
   285  			kubeEndpoint := kubernetes.EndpointFromContext(newContext)
   286  			assert.Check(t, kubeEndpoint != nil)
   287  			assert.Equal(t, newContextTyped.Description, c.expectedDescription)
   288  			assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
   289  			assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
   290  			assert.Equal(t, kubeEndpoint.Host, "https://someserver")
   291  		})
   292  	}
   293  }
   294  
   295  func TestCreateFromCurrent(t *testing.T) {
   296  	cases := []struct {
   297  		name                 string
   298  		description          string
   299  		orchestrator         string
   300  		expectedDescription  string
   301  		expectedOrchestrator command.Orchestrator
   302  	}{
   303  		{
   304  			name:                 "no-override",
   305  			expectedDescription:  "original description",
   306  			expectedOrchestrator: command.OrchestratorSwarm,
   307  		},
   308  		{
   309  			name:                 "override-description",
   310  			description:          "new description",
   311  			expectedDescription:  "new description",
   312  			expectedOrchestrator: command.OrchestratorSwarm,
   313  		},
   314  		{
   315  			name:                 "override-orchestrator",
   316  			orchestrator:         "kubernetes",
   317  			expectedDescription:  "original description",
   318  			expectedOrchestrator: command.OrchestratorKubernetes,
   319  		},
   320  	}
   321  
   322  	cli, cleanup := makeFakeCli(t)
   323  	defer cleanup()
   324  	revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
   325  	defer revert()
   326  	cli.ResetOutputBuffers()
   327  	assert.NilError(t, RunCreate(cli, &CreateOptions{
   328  		Name:        "original",
   329  		Description: "original description",
   330  		Docker: map[string]string{
   331  			keyHost: "tcp://42.42.42.42:2375",
   332  		},
   333  		Kubernetes: map[string]string{
   334  			keyFrom: "default",
   335  		},
   336  		DefaultStackOrchestrator: "swarm",
   337  	}))
   338  	assertContextCreateLogging(t, cli, "original")
   339  
   340  	cli.SetCurrentContext("original")
   341  
   342  	for _, c := range cases {
   343  		c := c
   344  		t.Run(c.name, func(t *testing.T) {
   345  			cli.ResetOutputBuffers()
   346  			err := RunCreate(cli, &CreateOptions{
   347  				Name:                     c.name,
   348  				Description:              c.description,
   349  				DefaultStackOrchestrator: c.orchestrator,
   350  			})
   351  			assert.NilError(t, err)
   352  			assertContextCreateLogging(t, cli, c.name)
   353  			newContext, err := cli.ContextStore().GetMetadata(c.name)
   354  			assert.NilError(t, err)
   355  			newContextTyped, err := command.GetDockerContext(newContext)
   356  			assert.NilError(t, err)
   357  			dockerEndpoint, err := docker.EndpointFromContext(newContext)
   358  			assert.NilError(t, err)
   359  			kubeEndpoint := kubernetes.EndpointFromContext(newContext)
   360  			assert.Check(t, kubeEndpoint != nil)
   361  			assert.Equal(t, newContextTyped.Description, c.expectedDescription)
   362  			assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
   363  			assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
   364  			assert.Equal(t, kubeEndpoint.Host, "https://someserver")
   365  		})
   366  	}
   367  }