github.com/metasources/buildx@v0.0.0-20230418141019-7aa1459cedea/test/cli/packages_cmd_test.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"testing"
     7  )
     8  
     9  func TestPackagesCmdFlags(t *testing.T) {
    10  	hiddenPackagesImage := "docker-archive:" + getFixtureImage(t, "image-hidden-packages")
    11  	coverageImage := "docker-archive:" + getFixtureImage(t, "image-pkg-coverage")
    12  	nodeBinaryImage := "docker-archive:" + getFixtureImage(t, "image-node-binary")
    13  	//badBinariesImage := "docker-archive:" + getFixtureImage(t, "image-bad-binaries")
    14  	tmp := t.TempDir() + "/"
    15  
    16  	tests := []struct {
    17  		name       string
    18  		args       []string
    19  		env        map[string]string
    20  		assertions []traitAssertion
    21  	}{
    22  		{
    23  			name: "no-args-shows-help",
    24  			args: []string{"packages"},
    25  			assertions: []traitAssertion{
    26  				assertInOutput("an image/directory argument is required"),              // specific error that should be shown
    27  				assertInOutput("Generate a packaged-based Software Bill Of Materials"), // excerpt from help description
    28  				assertFailingReturnCode,
    29  			},
    30  		},
    31  		{
    32  			name: "json-output-flag",
    33  			args: []string{"packages", "-o", "json", coverageImage},
    34  			assertions: []traitAssertion{
    35  				assertJsonReport,
    36  				assertSuccessfulReturnCode,
    37  			},
    38  		},
    39  		{
    40  			name: "multiple-output-flags",
    41  			args: []string{"packages", "-o", "table", "-o", "json=" + tmp + ".tmp/multiple-output-flag-test.json", coverageImage},
    42  			assertions: []traitAssertion{
    43  				assertTableReport,
    44  				assertFileExists(tmp + ".tmp/multiple-output-flag-test.json"),
    45  				assertSuccessfulReturnCode,
    46  			},
    47  		},
    48  		// I haven't been able to reproduce locally yet, but in CI this has proven to be unstable:
    49  		// For the same commit:
    50  		//   pass: https://github.com/metasources/buildx/runs/4611344142?check_suite_focus=true
    51  		//   fail: https://github.com/metasources/buildx/runs/4611343586?check_suite_focus=true
    52  		// For the meantime this test will be commented out, but should be added back in as soon as possible.
    53  		//
    54  		//{
    55  		//	name: "regression-survive-bad-binaries",
    56  		//	// this image has all sorts of rich binaries from the clang-13 test suite that should do pretty bad things
    57  		//	// to the go cataloger binary path. We should NEVER let a panic stop the cataloging process for these
    58  		//	// specific cases.
    59  		//
    60  		//	// this is more of an integration test, however, to assert the output we want to see from the application
    61  		//	// a CLI test is much easier.
    62  		//	args: []string{"packages", "-vv", badBinariesImage},
    63  		//	assertions: []traitAssertion{
    64  		//		assertInOutput("could not parse possible go binary"),
    65  		//		assertSuccessfulReturnCode,
    66  		//	},
    67  		//},
    68  		{
    69  			name: "output-env-binding",
    70  			env: map[string]string{
    71  				"BUILDX_OUTPUT": "json",
    72  			},
    73  			args: []string{"packages", coverageImage},
    74  			assertions: []traitAssertion{
    75  				assertJsonReport,
    76  				assertSuccessfulReturnCode,
    77  			},
    78  		},
    79  		{
    80  			name: "table-output-flag",
    81  			args: []string{"packages", "-o", "table", coverageImage},
    82  			assertions: []traitAssertion{
    83  				assertTableReport,
    84  				assertSuccessfulReturnCode,
    85  			},
    86  		},
    87  		{
    88  			name: "default-output-flag",
    89  			args: []string{"packages", coverageImage},
    90  			assertions: []traitAssertion{
    91  				assertTableReport,
    92  				assertSuccessfulReturnCode,
    93  			},
    94  		},
    95  		{
    96  			name: "squashed-scope-flag",
    97  			args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
    98  			assertions: []traitAssertion{
    99  				assertPackageCount(37),
   100  				assertSuccessfulReturnCode,
   101  			},
   102  		},
   103  		{
   104  			name: "squashed-scope-flag-hidden-packages",
   105  			args: []string{"packages", "-o", "json", "-s", "squashed", hiddenPackagesImage},
   106  			assertions: []traitAssertion{
   107  				assertPackageCount(163),
   108  				assertNotInOutput("vsftpd"), // hidden package
   109  				assertSuccessfulReturnCode,
   110  			},
   111  		},
   112  		{
   113  			name: "all-layers-scope-flag",
   114  			args: []string{"packages", "-o", "json", "-s", "all-layers", hiddenPackagesImage},
   115  			assertions: []traitAssertion{
   116  				assertPackageCount(164), // packages are now deduplicated for this case
   117  				assertInOutput("all-layers"),
   118  				assertInOutput("vsftpd"), // hidden package
   119  				assertSuccessfulReturnCode,
   120  			},
   121  		},
   122  		{
   123  			name: "all-layers-scope-flag-by-env",
   124  			args: []string{"packages", "-o", "json", hiddenPackagesImage},
   125  			env: map[string]string{
   126  				"BUILDX_PACKAGE_CATALOGER_SCOPE": "all-layers",
   127  			},
   128  			assertions: []traitAssertion{
   129  				assertPackageCount(164), // packages are now deduplicated for this case
   130  				assertInOutput("all-layers"),
   131  				assertInOutput("vsftpd"), // hidden package
   132  				assertSuccessfulReturnCode,
   133  			},
   134  		},
   135  		{
   136  			// we want to make certain that buildx can catalog a single go binary and get a SBOM report that is not empty
   137  			name: "catalog-single-go-binary",
   138  			args: []string{"packages", "-o", "json", getBuildxBinaryLocation(t)},
   139  			assertions: []traitAssertion{
   140  				assertJsonReport,
   141  				assertStdoutLengthGreaterThan(1000),
   142  				assertSuccessfulReturnCode,
   143  			},
   144  		},
   145  		{
   146  			name: "catalog-node-js-binary",
   147  			args: []string{"packages", "-o", "json", nodeBinaryImage},
   148  			assertions: []traitAssertion{
   149  				assertJsonReport,
   150  				assertInOutput("node.js"),
   151  				assertSuccessfulReturnCode,
   152  			},
   153  		},
   154  		{
   155  			name: "responds-to-package-cataloger-search-options",
   156  			args: []string{"packages", "-vv"},
   157  			env: map[string]string{
   158  				"BUILDX_PACKAGE_SEARCH_UNINDEXED_ARCHIVES": "true",
   159  				"BUILDX_PACKAGE_SEARCH_INDEXED_ARCHIVES":   "false",
   160  			},
   161  			assertions: []traitAssertion{
   162  				// the application config in the log matches that of what we expect to have been configured. Note:
   163  				// we are not testing further wiring of this option, only that the config responds to
   164  				// package-cataloger-level options.
   165  				assertInOutput("search-unindexed-archives: true"),
   166  				assertInOutput("search-indexed-archives: false"),
   167  			},
   168  		},
   169  		{
   170  			name: "platform-option-wired-up",
   171  			args: []string{"packages", "--platform", "arm64", "-o", "json", "registry:busybox:1.31"},
   172  			assertions: []traitAssertion{
   173  				assertInOutput("sha256:1ee006886991ad4689838d3a288e0dd3fd29b70e276622f16b67a8922831a853"), // linux/arm64 image digest
   174  				assertSuccessfulReturnCode,
   175  			},
   176  		},
   177  		{
   178  			name: "json-file-flag",
   179  			args: []string{"packages", "-o", "json", "--file", filepath.Join(tmp, "output-1.json"), coverageImage},
   180  			assertions: []traitAssertion{
   181  				assertSuccessfulReturnCode,
   182  				assertFileOutput(t, filepath.Join(tmp, "output-1.json"),
   183  					assertJsonReport,
   184  				),
   185  			},
   186  		},
   187  		{
   188  			name: "json-output-flag-to-file",
   189  			args: []string{"packages", "-o", fmt.Sprintf("json=%s", filepath.Join(tmp, "output-2.json")), coverageImage},
   190  			assertions: []traitAssertion{
   191  				assertSuccessfulReturnCode,
   192  				assertFileOutput(t, filepath.Join(tmp, "output-2.json"),
   193  					assertJsonReport,
   194  				),
   195  			},
   196  		},
   197  		{
   198  			name: "catalogers-option",
   199  			// This will detect enable python-index-cataloger, python-package-cataloger and ruby-gemspec cataloger
   200  			args: []string{"packages", "-o", "json", "--catalogers", "python,ruby-gemspec", coverageImage},
   201  			assertions: []traitAssertion{
   202  				assertPackageCount(13),
   203  				assertSuccessfulReturnCode,
   204  			},
   205  		},
   206  		{
   207  			name: "override-default-parallelism",
   208  			args: []string{"packages", "-vvv", "-o", "json", coverageImage},
   209  			env: map[string]string{
   210  				"BUILDX_PARALLELISM": "2",
   211  			},
   212  			assertions: []traitAssertion{
   213  				// the application config in the log matches that of what we expect to have been configured.
   214  				assertInOutput("parallelism: 2"),
   215  				assertInOutput("parallelism=2"),
   216  				assertPackageCount(37),
   217  				assertSuccessfulReturnCode,
   218  			},
   219  		},
   220  		{
   221  			name: "default-parallelism",
   222  			args: []string{"packages", "-vvv", "-o", "json", coverageImage},
   223  			assertions: []traitAssertion{
   224  				// the application config in the log matches that of what we expect to have been configured.
   225  				assertInOutput("parallelism: 1"),
   226  				assertInOutput("parallelism=1"),
   227  				assertPackageCount(37),
   228  				assertSuccessfulReturnCode,
   229  			},
   230  		},
   231  		{
   232  			name: "password and key not in config output",
   233  			args: []string{"packages", "-vvv", "-o", "json", coverageImage},
   234  			env: map[string]string{
   235  				"BUILDX_ATTEST_PASSWORD": "secret_password",
   236  				"BUILDX_ATTEST_KEY":      "secret_key_path",
   237  			},
   238  			assertions: []traitAssertion{
   239  				assertNotInOutput("secret_password"),
   240  				assertNotInOutput("secret_key_path"),
   241  				assertPackageCount(37),
   242  				assertSuccessfulReturnCode,
   243  			},
   244  		},
   245  	}
   246  
   247  	for _, test := range tests {
   248  		t.Run(test.name, func(t *testing.T) {
   249  			cmd, stdout, stderr := runBuildx(t, test.env, test.args...)
   250  			for _, traitFn := range test.assertions {
   251  				traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
   252  			}
   253  			logOutputOnFailure(t, cmd, stdout, stderr)
   254  		})
   255  	}
   256  }
   257  
   258  func TestRegistryAuth(t *testing.T) {
   259  	host := "localhost:17"
   260  	image := fmt.Sprintf("%s/something:latest", host)
   261  	args := []string{"packages", "-vv", fmt.Sprintf("registry:%s", image)}
   262  
   263  	tests := []struct {
   264  		name       string
   265  		args       []string
   266  		env        map[string]string
   267  		assertions []traitAssertion
   268  	}{
   269  		{
   270  			name: "fallback to keychain",
   271  			args: args,
   272  			assertions: []traitAssertion{
   273  				assertInOutput("source=OciRegistry"),
   274  				assertInOutput(image),
   275  				assertInOutput("no registry credentials configured, using the default keychain"),
   276  			},
   277  		},
   278  		{
   279  			name: "use creds",
   280  			args: args,
   281  			env: map[string]string{
   282  				"BUILDX_REGISTRY_AUTH_AUTHORITY": host,
   283  				"BUILDX_REGISTRY_AUTH_USERNAME":  "username",
   284  				"BUILDX_REGISTRY_AUTH_PASSWORD":  "password",
   285  			},
   286  			assertions: []traitAssertion{
   287  				assertInOutput("source=OciRegistry"),
   288  				assertInOutput(image),
   289  				assertInOutput(fmt.Sprintf(`using basic auth for registry "%s"`, host)),
   290  			},
   291  		},
   292  		{
   293  			name: "use token",
   294  			args: args,
   295  			env: map[string]string{
   296  				"BUILDX_REGISTRY_AUTH_AUTHORITY": host,
   297  				"BUILDX_REGISTRY_AUTH_TOKEN":     "token",
   298  			},
   299  			assertions: []traitAssertion{
   300  				assertInOutput("source=OciRegistry"),
   301  				assertInOutput(image),
   302  				assertInOutput(fmt.Sprintf(`using token for registry "%s"`, host)),
   303  			},
   304  		},
   305  		{
   306  			name: "not enough info fallsback to keychain",
   307  			args: args,
   308  			env: map[string]string{
   309  				"BUILDX_REGISTRY_AUTH_AUTHORITY": host,
   310  			},
   311  			assertions: []traitAssertion{
   312  				assertInOutput("source=OciRegistry"),
   313  				assertInOutput(image),
   314  				assertInOutput(`no registry credentials configured, using the default keychain`),
   315  			},
   316  		},
   317  		{
   318  			name: "allows insecure http flag",
   319  			args: args,
   320  			env: map[string]string{
   321  				"BUILDX_REGISTRY_INSECURE_USE_HTTP": "true",
   322  			},
   323  			assertions: []traitAssertion{
   324  				assertInOutput("insecure-use-http: true"),
   325  			},
   326  		},
   327  	}
   328  
   329  	for _, test := range tests {
   330  		t.Run(test.name, func(t *testing.T) {
   331  			cmd, stdout, stderr := runBuildx(t, test.env, test.args...)
   332  			for _, traitAssertionFn := range test.assertions {
   333  				traitAssertionFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
   334  			}
   335  			logOutputOnFailure(t, cmd, stdout, stderr)
   336  		})
   337  	}
   338  }