github.com/thajeztah/cli@v0.0.0-20240223162942-dc6bfac81a8b/cmd/docker/builder_test.go (about)

     1  // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
     2  //go:build go1.19
     3  
     4  package main
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/docker/cli/cli/command"
    14  	"github.com/docker/cli/cli/context/store"
    15  	"github.com/docker/cli/cli/flags"
    16  	"github.com/docker/cli/internal/test/output"
    17  	"github.com/docker/docker/api/types"
    18  	"github.com/docker/docker/client"
    19  	"gotest.tools/v3/assert"
    20  	"gotest.tools/v3/fs"
    21  )
    22  
    23  var pluginFilename = "docker-buildx"
    24  
    25  func TestBuildWithBuilder(t *testing.T) {
    26  	testcases := []struct {
    27  		name         string
    28  		context      string
    29  		builder      string
    30  		alias        bool
    31  		expectedEnvs []string
    32  	}{
    33  		{
    34  			name:         "default",
    35  			context:      "default",
    36  			alias:        false,
    37  			expectedEnvs: []string{"BUILDX_BUILDER=default"},
    38  		},
    39  		{
    40  			name:         "custom context",
    41  			context:      "foo",
    42  			alias:        false,
    43  			expectedEnvs: []string{"BUILDX_BUILDER=foo"},
    44  		},
    45  		{
    46  			name:         "custom builder name",
    47  			builder:      "mybuilder",
    48  			alias:        false,
    49  			expectedEnvs: nil,
    50  		},
    51  		{
    52  			name:         "buildx install",
    53  			alias:        true,
    54  			expectedEnvs: nil,
    55  		},
    56  	}
    57  
    58  	dir := fs.NewDir(t, t.Name(),
    59  		fs.WithFile(pluginFilename, `#!/bin/sh
    60  echo '{"SchemaVersion":"0.1.0","Vendor":"Docker Inc.","Version":"v0.6.3","ShortDescription":"Build with BuildKit"}'`, fs.WithMode(0o777)),
    61  	)
    62  	defer dir.Remove()
    63  
    64  	for _, tt := range testcases {
    65  		tt := tt
    66  		t.Run(tt.name, func(t *testing.T) {
    67  			if tt.builder != "" {
    68  				t.Setenv("BUILDX_BUILDER", tt.builder)
    69  			}
    70  
    71  			var b bytes.Buffer
    72  			dockerCli, err := command.NewDockerCli(
    73  				command.WithAPIClient(&fakeClient{}),
    74  				command.WithInputStream(discard),
    75  				command.WithCombinedStreams(&b),
    76  			)
    77  			assert.NilError(t, err)
    78  			assert.NilError(t, dockerCli.Initialize(flags.NewClientOptions()))
    79  
    80  			if tt.context != "" {
    81  				if tt.context != command.DefaultContextName {
    82  					assert.NilError(t, dockerCli.ContextStore().CreateOrUpdate(store.Metadata{
    83  						Name: tt.context,
    84  						Endpoints: map[string]any{
    85  							"docker": map[string]any{
    86  								"host": "unix://" + filepath.Join(t.TempDir(), "docker.sock"),
    87  							},
    88  						},
    89  					}))
    90  				}
    91  				opts := flags.NewClientOptions()
    92  				opts.Context = tt.context
    93  				assert.NilError(t, dockerCli.Initialize(opts))
    94  			}
    95  
    96  			dockerCli.ConfigFile().CLIPluginsExtraDirs = []string{dir.Path()}
    97  			if tt.alias {
    98  				dockerCli.ConfigFile().Aliases = map[string]string{"builder": "buildx"}
    99  			}
   100  
   101  			tcmd := newDockerCommand(dockerCli)
   102  			tcmd.SetArgs([]string{"build", "."})
   103  
   104  			cmd, args, err := tcmd.HandleGlobalFlags()
   105  			assert.NilError(t, err)
   106  
   107  			var envs []string
   108  			args, os.Args, envs, err = processBuilder(dockerCli, cmd, args, os.Args)
   109  			assert.NilError(t, err)
   110  			assert.DeepEqual(t, []string{builderDefaultPlugin, "build", "."}, args)
   111  			if tt.expectedEnvs != nil {
   112  				assert.DeepEqual(t, tt.expectedEnvs, envs)
   113  			} else {
   114  				assert.Check(t, len(envs) == 0)
   115  			}
   116  		})
   117  	}
   118  }
   119  
   120  type fakeClient struct {
   121  	client.Client
   122  }
   123  
   124  func (c *fakeClient) Ping(_ context.Context) (types.Ping, error) {
   125  	return types.Ping{OSType: "linux"}, nil
   126  }
   127  
   128  func TestBuildkitDisabled(t *testing.T) {
   129  	t.Setenv("DOCKER_BUILDKIT", "0")
   130  
   131  	dir := fs.NewDir(t, t.Name(),
   132  		fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
   133  	)
   134  	defer dir.Remove()
   135  
   136  	b := bytes.NewBuffer(nil)
   137  
   138  	dockerCli, err := command.NewDockerCli(
   139  		command.WithAPIClient(&fakeClient{}),
   140  		command.WithInputStream(discard),
   141  		command.WithCombinedStreams(b),
   142  	)
   143  	assert.NilError(t, err)
   144  	assert.NilError(t, dockerCli.Initialize(flags.NewClientOptions()))
   145  	dockerCli.ConfigFile().CLIPluginsExtraDirs = []string{dir.Path()}
   146  
   147  	tcmd := newDockerCommand(dockerCli)
   148  	tcmd.SetArgs([]string{"build", "."})
   149  
   150  	cmd, args, err := tcmd.HandleGlobalFlags()
   151  	assert.NilError(t, err)
   152  
   153  	var envs []string
   154  	args, os.Args, envs, err = processBuilder(dockerCli, cmd, args, os.Args)
   155  	assert.NilError(t, err)
   156  	assert.DeepEqual(t, []string{"build", "."}, args)
   157  	assert.Check(t, len(envs) == 0)
   158  
   159  	output.Assert(t, b.String(), map[int]func(string) error{
   160  		0: output.Suffix("DEPRECATED: The legacy builder is deprecated and will be removed in a future release."),
   161  		1: output.Suffix("BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0"),
   162  	})
   163  }
   164  
   165  func TestBuilderBroken(t *testing.T) {
   166  	dir := fs.NewDir(t, t.Name(),
   167  		fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
   168  	)
   169  	defer dir.Remove()
   170  
   171  	b := bytes.NewBuffer(nil)
   172  
   173  	dockerCli, err := command.NewDockerCli(
   174  		command.WithAPIClient(&fakeClient{}),
   175  		command.WithInputStream(discard),
   176  		command.WithCombinedStreams(b),
   177  	)
   178  	assert.NilError(t, err)
   179  	assert.NilError(t, dockerCli.Initialize(flags.NewClientOptions()))
   180  	dockerCli.ConfigFile().CLIPluginsExtraDirs = []string{dir.Path()}
   181  
   182  	tcmd := newDockerCommand(dockerCli)
   183  	tcmd.SetArgs([]string{"build", "."})
   184  
   185  	cmd, args, err := tcmd.HandleGlobalFlags()
   186  	assert.NilError(t, err)
   187  
   188  	var envs []string
   189  	args, os.Args, envs, err = processBuilder(dockerCli, cmd, args, os.Args)
   190  	assert.NilError(t, err)
   191  	assert.DeepEqual(t, []string{"build", "."}, args)
   192  	assert.Check(t, len(envs) == 0)
   193  
   194  	output.Assert(t, b.String(), map[int]func(string) error{
   195  		0: output.Prefix("failed to fetch metadata:"),
   196  		2: output.Suffix("DEPRECATED: The legacy builder is deprecated and will be removed in a future release."),
   197  	})
   198  }
   199  
   200  func TestBuilderBrokenEnforced(t *testing.T) {
   201  	t.Setenv("DOCKER_BUILDKIT", "1")
   202  
   203  	dir := fs.NewDir(t, t.Name(),
   204  		fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
   205  	)
   206  	defer dir.Remove()
   207  
   208  	b := bytes.NewBuffer(nil)
   209  
   210  	dockerCli, err := command.NewDockerCli(
   211  		command.WithAPIClient(&fakeClient{}),
   212  		command.WithInputStream(discard),
   213  		command.WithCombinedStreams(b),
   214  	)
   215  	assert.NilError(t, err)
   216  	assert.NilError(t, dockerCli.Initialize(flags.NewClientOptions()))
   217  	dockerCli.ConfigFile().CLIPluginsExtraDirs = []string{dir.Path()}
   218  
   219  	tcmd := newDockerCommand(dockerCli)
   220  	tcmd.SetArgs([]string{"build", "."})
   221  
   222  	cmd, args, err := tcmd.HandleGlobalFlags()
   223  	assert.NilError(t, err)
   224  
   225  	var envs []string
   226  	args, os.Args, envs, err = processBuilder(dockerCli, cmd, args, os.Args)
   227  	assert.DeepEqual(t, []string{"build", "."}, args)
   228  	assert.Check(t, len(envs) == 0)
   229  
   230  	output.Assert(t, err.Error(), map[int]func(string) error{
   231  		0: output.Prefix("failed to fetch metadata:"),
   232  		2: output.Suffix("ERROR: BuildKit is enabled but the buildx component is missing or broken."),
   233  	})
   234  }
   235  
   236  func TestHasBuilderName(t *testing.T) {
   237  	cases := []struct {
   238  		name     string
   239  		args     []string
   240  		envs     []string
   241  		expected bool
   242  	}{
   243  		{
   244  			name:     "no args",
   245  			args:     []string{"docker", "build", "."},
   246  			envs:     []string{"FOO=bar"},
   247  			expected: false,
   248  		},
   249  		{
   250  			name:     "env var",
   251  			args:     []string{"docker", "build", "."},
   252  			envs:     []string{"BUILDX_BUILDER=foo"},
   253  			expected: true,
   254  		},
   255  		{
   256  			name:     "empty env var",
   257  			args:     []string{"docker", "build", "."},
   258  			envs:     []string{"BUILDX_BUILDER="},
   259  			expected: false,
   260  		},
   261  		{
   262  			name:     "flag",
   263  			args:     []string{"docker", "build", "--builder", "foo", "."},
   264  			envs:     []string{"FOO=bar"},
   265  			expected: true,
   266  		},
   267  		{
   268  			name:     "both",
   269  			args:     []string{"docker", "build", "--builder", "foo", "."},
   270  			envs:     []string{"BUILDX_BUILDER=foo"},
   271  			expected: true,
   272  		},
   273  	}
   274  	for _, tt := range cases {
   275  		tt := tt
   276  		t.Run(tt.name, func(t *testing.T) {
   277  			assert.Equal(t, tt.expected, hasBuilderName(tt.args, tt.envs))
   278  		})
   279  	}
   280  }