github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/blob/blob_minio_test.go (about)

     1  package blob
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/goreleaser/goreleaser/internal/artifact"
    13  	"github.com/goreleaser/goreleaser/internal/testctx"
    14  	"github.com/goreleaser/goreleaser/internal/testlib"
    15  	"github.com/goreleaser/goreleaser/pkg/config"
    16  	"github.com/goreleaser/goreleaser/pkg/context"
    17  	"github.com/ory/dockertest/v3"
    18  	"github.com/ory/dockertest/v3/docker"
    19  	"github.com/stretchr/testify/require"
    20  	"gocloud.dev/blob"
    21  )
    22  
    23  const (
    24  	minioUser     = "minio"
    25  	minioPwd      = "miniostorage"
    26  	containerName = "goreleaserTestMinio"
    27  )
    28  
    29  var listen string
    30  
    31  func TestMain(m *testing.M) {
    32  	prepareEnv()
    33  
    34  	requireNoErr := func(err error) {
    35  		if err != nil {
    36  			log.Fatal(err)
    37  		}
    38  	}
    39  
    40  	pool := testlib.MustDockerPool(log.Default())
    41  	testlib.MustKillContainer(log.Default(), containerName)
    42  
    43  	resource, err := pool.RunWithOptions(&dockertest.RunOptions{
    44  		Name:       containerName,
    45  		Repository: "minio/minio",
    46  		Tag:        "latest",
    47  		Env: []string{
    48  			"MINIO_ROOT_USER=" + minioUser,
    49  			"MINIO_ROOT_PASSWORD=" + minioPwd,
    50  		},
    51  		ExposedPorts: []string{"9000", "9001"},
    52  		Cmd:          []string{"server", "/data", "--console-address", ":9001"},
    53  	}, func(hc *docker.HostConfig) {
    54  		hc.AutoRemove = true
    55  	})
    56  	requireNoErr(err)
    57  	requireNoErr(pool.Retry(func() error {
    58  		_, err := http.Get(fmt.Sprintf("http://localhost:%s/minio/health/ready", resource.GetPort("9000/tcp")))
    59  		return err
    60  	}))
    61  	listen = "localhost:" + resource.GetPort("9000/tcp")
    62  
    63  	code := m.Run()
    64  
    65  	requireNoErr(pool.Purge(resource))
    66  	os.Exit(code)
    67  }
    68  
    69  func TestMinioUpload(t *testing.T) {
    70  	name := "basic"
    71  	folder := t.TempDir()
    72  	srcpath := filepath.Join(folder, "source.tar.gz")
    73  	tgzpath := filepath.Join(folder, "bin.tar.gz")
    74  	debpath := filepath.Join(folder, "bin.deb")
    75  	checkpath := filepath.Join(folder, "check.txt")
    76  	metapath := filepath.Join(folder, "metadata.json")
    77  	sigpath := filepath.Join(folder, "f.sig")
    78  	certpath := filepath.Join(folder, "f.pem")
    79  	require.NoError(t, os.WriteFile(checkpath, []byte("fake checksums"), 0o744))
    80  	require.NoError(t, os.WriteFile(metapath, []byte(`{"fake":true}`), 0o744))
    81  	require.NoError(t, os.WriteFile(srcpath, []byte("fake\nsrc"), 0o744))
    82  	require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744))
    83  	require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744))
    84  	require.NoError(t, os.WriteFile(sigpath, []byte("fake\nsig"), 0o744))
    85  	require.NoError(t, os.WriteFile(certpath, []byte("fake\ncert"), 0o744))
    86  	ctx := testctx.NewWithCfg(config.Project{
    87  		Dist:        folder,
    88  		ProjectName: "testupload",
    89  		Blobs: []config.Blob{
    90  			{
    91  				Provider:           "s3",
    92  				Bucket:             name,
    93  				Region:             "us-east",
    94  				Endpoint:           "http://" + listen,
    95  				IDs:                []string{"foo", "bar"},
    96  				CacheControl:       []string{"max-age=9999"},
    97  				ContentDisposition: "inline",
    98  				IncludeMeta:        true,
    99  				ExtraFiles: []config.ExtraFile{
   100  					{
   101  						Glob: "./testdata/*.golden",
   102  					},
   103  				},
   104  			},
   105  		},
   106  	}, testctx.WithCurrentTag("v1.0.0"))
   107  	ctx.Artifacts.Add(&artifact.Artifact{
   108  		Type: artifact.Metadata,
   109  		Name: "metadata.json",
   110  		Path: metapath,
   111  	})
   112  	ctx.Artifacts.Add(&artifact.Artifact{
   113  		Type: artifact.Checksum,
   114  		Name: "checksum.txt",
   115  		Path: checkpath,
   116  	})
   117  	ctx.Artifacts.Add(&artifact.Artifact{
   118  		Type: artifact.Signature,
   119  		Name: "checksum.txt.sig",
   120  		Path: sigpath,
   121  		Extra: map[string]interface{}{
   122  			artifact.ExtraID: "foo",
   123  		},
   124  	})
   125  	ctx.Artifacts.Add(&artifact.Artifact{
   126  		Type: artifact.Certificate,
   127  		Name: "checksum.pem",
   128  		Path: certpath,
   129  		Extra: map[string]interface{}{
   130  			artifact.ExtraID: "foo",
   131  		},
   132  	})
   133  	ctx.Artifacts.Add(&artifact.Artifact{
   134  		Type: artifact.UploadableSourceArchive,
   135  		Name: "source.tar.gz",
   136  		Path: srcpath,
   137  		Extra: map[string]interface{}{
   138  			artifact.ExtraFormat: "tar.gz",
   139  		},
   140  	})
   141  	ctx.Artifacts.Add(&artifact.Artifact{
   142  		Type: artifact.UploadableArchive,
   143  		Name: "bin.tar.gz",
   144  		Path: tgzpath,
   145  		Extra: map[string]interface{}{
   146  			artifact.ExtraID: "foo",
   147  		},
   148  	})
   149  	ctx.Artifacts.Add(&artifact.Artifact{
   150  		Type: artifact.LinuxPackage,
   151  		Name: "bin.deb",
   152  		Path: debpath,
   153  		Extra: map[string]interface{}{
   154  			artifact.ExtraID: "bar",
   155  		},
   156  	})
   157  
   158  	setupBucket(t, testlib.MustDockerPool(t), name)
   159  	require.NoError(t, Pipe{}.Default(ctx))
   160  	require.NoError(t, Pipe{}.Publish(ctx))
   161  
   162  	require.Subset(t, getFiles(t, ctx, ctx.Config.Blobs[0]), []string{
   163  		"testupload/v1.0.0/bin.deb",
   164  		"testupload/v1.0.0/bin.tar.gz",
   165  		"testupload/v1.0.0/metadata.json",
   166  		"testupload/v1.0.0/checksum.txt",
   167  		"testupload/v1.0.0/checksum.txt.sig",
   168  		"testupload/v1.0.0/checksum.pem",
   169  		"testupload/v1.0.0/source.tar.gz",
   170  		"testupload/v1.0.0/file.golden",
   171  	})
   172  }
   173  
   174  func TestMinioUploadCustomBucketID(t *testing.T) {
   175  	name := "fromenv"
   176  	folder := t.TempDir()
   177  	tgzpath := filepath.Join(folder, "bin.tar.gz")
   178  	debpath := filepath.Join(folder, "bin.deb")
   179  	require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744))
   180  	require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744))
   181  	// Set custom BUCKET_ID env variable.
   182  	t.Setenv("BUCKET_ID", name)
   183  	ctx := testctx.NewWithCfg(config.Project{
   184  		Dist:        folder,
   185  		ProjectName: "testupload",
   186  		Blobs: []config.Blob{
   187  			{
   188  				Provider: "s3",
   189  				Bucket:   "{{.Env.BUCKET_ID}}",
   190  				Endpoint: "http://" + listen,
   191  			},
   192  		},
   193  	}, testctx.WithCurrentTag("v1.0.0"))
   194  	ctx.Artifacts.Add(&artifact.Artifact{
   195  		Type: artifact.UploadableArchive,
   196  		Name: "bin.tar.gz",
   197  		Path: tgzpath,
   198  	})
   199  	ctx.Artifacts.Add(&artifact.Artifact{
   200  		Type: artifact.LinuxPackage,
   201  		Name: "bin.deb",
   202  		Path: debpath,
   203  	})
   204  
   205  	setupBucket(t, testlib.MustDockerPool(t), name)
   206  	require.NoError(t, Pipe{}.Default(ctx))
   207  	require.NoError(t, Pipe{}.Publish(ctx))
   208  }
   209  
   210  func TestMinioUploadRootFolder(t *testing.T) {
   211  	name := "rootdir"
   212  	folder := t.TempDir()
   213  	tgzpath := filepath.Join(folder, "bin.tar.gz")
   214  	debpath := filepath.Join(folder, "bin.deb")
   215  	require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744))
   216  	require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744))
   217  	ctx := testctx.NewWithCfg(config.Project{
   218  		Dist:        folder,
   219  		ProjectName: "testupload",
   220  		Blobs: []config.Blob{
   221  			{
   222  				Provider: "s3",
   223  				Bucket:   name,
   224  				Folder:   "/",
   225  				Endpoint: "http://" + listen,
   226  			},
   227  		},
   228  	}, testctx.WithCurrentTag("v1.0.0"))
   229  	ctx.Artifacts.Add(&artifact.Artifact{
   230  		Type: artifact.UploadableArchive,
   231  		Name: "bin.tar.gz",
   232  		Path: tgzpath,
   233  	})
   234  	ctx.Artifacts.Add(&artifact.Artifact{
   235  		Type: artifact.LinuxPackage,
   236  		Name: "bin.deb",
   237  		Path: debpath,
   238  	})
   239  
   240  	setupBucket(t, testlib.MustDockerPool(t), name)
   241  	require.NoError(t, Pipe{}.Default(ctx))
   242  	require.NoError(t, Pipe{}.Publish(ctx))
   243  }
   244  
   245  func TestMinioUploadInvalidCustomBucketID(t *testing.T) {
   246  	folder := t.TempDir()
   247  	tgzpath := filepath.Join(folder, "bin.tar.gz")
   248  	debpath := filepath.Join(folder, "bin.deb")
   249  	require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744))
   250  	require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744))
   251  	ctx := testctx.NewWithCfg(config.Project{
   252  		Dist:        folder,
   253  		ProjectName: "testupload",
   254  		Blobs: []config.Blob{
   255  			{
   256  				Provider: "s3",
   257  				Bucket:   "{{.Bad}}",
   258  				Endpoint: "http://" + listen,
   259  			},
   260  		},
   261  	}, testctx.WithCurrentTag("v1.1.0"))
   262  	ctx.Artifacts.Add(&artifact.Artifact{
   263  		Type: artifact.UploadableArchive,
   264  		Name: "bin.tar.gz",
   265  		Path: tgzpath,
   266  	})
   267  	ctx.Artifacts.Add(&artifact.Artifact{
   268  		Type: artifact.LinuxPackage,
   269  		Name: "bin.deb",
   270  		Path: debpath,
   271  	})
   272  
   273  	require.NoError(t, Pipe{}.Default(ctx))
   274  	require.Error(t, Pipe{}.Publish(ctx))
   275  }
   276  
   277  func TestMinioUploadSkip(t *testing.T) {
   278  	name := "basic"
   279  	folder := t.TempDir()
   280  	debpath := filepath.Join(folder, "bin.deb")
   281  	tgzpath := filepath.Join(folder, "bin.tar.gz")
   282  	require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744))
   283  	require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744))
   284  
   285  	buildCtx := func(uploadID string) *context.Context {
   286  		ctx := testctx.NewWithCfg(
   287  			config.Project{
   288  				Dist:        folder,
   289  				ProjectName: "testupload",
   290  				Blobs: []config.Blob{
   291  					{
   292  						Provider: "s3",
   293  						Bucket:   name,
   294  						Region:   "us-east",
   295  						Endpoint: "http://" + listen,
   296  						IDs:      []string{"foo"},
   297  						Disable:  `{{ eq .Env.UPLOAD_ID "foo" }}`,
   298  					},
   299  					{
   300  						Provider: "s3",
   301  						Bucket:   name,
   302  						Region:   "us-east",
   303  						Endpoint: "http://" + listen,
   304  						Disable:  `{{ eq .Env.UPLOAD_ID "bar" }}`,
   305  						IDs:      []string{"bar"},
   306  					},
   307  				},
   308  			},
   309  			testctx.WithCurrentTag("v1.0.0"),
   310  			testctx.WithEnv(map[string]string{
   311  				"UPLOAD_ID": uploadID,
   312  			}),
   313  		)
   314  		ctx.Artifacts.Add(&artifact.Artifact{
   315  			Type: artifact.UploadableArchive,
   316  			Name: "bin.tar.gz",
   317  			Path: tgzpath,
   318  			Extra: map[string]interface{}{
   319  				artifact.ExtraID: "foo",
   320  			},
   321  		})
   322  		ctx.Artifacts.Add(&artifact.Artifact{
   323  			Type: artifact.LinuxPackage,
   324  			Name: "bin.deb",
   325  			Path: debpath,
   326  			Extra: map[string]interface{}{
   327  				artifact.ExtraID: "bar",
   328  			},
   329  		})
   330  		return ctx
   331  	}
   332  
   333  	setupBucket(t, testlib.MustDockerPool(t), name)
   334  
   335  	t.Run("upload only foo", func(t *testing.T) {
   336  		ctx := buildCtx("foo")
   337  		require.NoError(t, Pipe{}.Default(ctx))
   338  		testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   339  		require.Subset(t, getFiles(t, ctx, ctx.Config.Blobs[0]), []string{
   340  			"testupload/v1.0.0/bin.deb",
   341  		})
   342  	})
   343  
   344  	t.Run("upload only bar", func(t *testing.T) {
   345  		ctx := buildCtx("bar")
   346  		require.NoError(t, Pipe{}.Default(ctx))
   347  		testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   348  		require.Subset(t, getFiles(t, ctx, ctx.Config.Blobs[0]), []string{
   349  			"testupload/v1.0.0/bin.tar.gz",
   350  		})
   351  	})
   352  
   353  	t.Run("invalid tmpl", func(t *testing.T) {
   354  		ctx := buildCtx("none")
   355  		ctx.Config.Blobs = []config.Blob{{
   356  			Provider: "s3",
   357  			Bucket:   name,
   358  			Endpoint: "http://" + listen,
   359  			Disable:  `{{ .Env.NOME }}`,
   360  		}}
   361  		require.NoError(t, Pipe{}.Default(ctx))
   362  		testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
   363  	})
   364  }
   365  
   366  func prepareEnv() {
   367  	os.Setenv("AWS_ACCESS_KEY_ID", minioUser)
   368  	os.Setenv("AWS_SECRET_ACCESS_KEY", minioPwd)
   369  	os.Setenv("AWS_REGION", "us-east-1")
   370  }
   371  
   372  func setupBucket(tb testing.TB, pool *dockertest.Pool, name string) {
   373  	tb.Helper()
   374  
   375  	res, err := pool.RunWithOptions(&dockertest.RunOptions{
   376  		Repository: "minio/mc",
   377  		Links:      []string{containerName},
   378  		Env:        []string{fmt.Sprintf("MC_HOST_local=http://%s:%s@%s:9000", minioUser, minioPwd, containerName)},
   379  		Cmd:        []string{"mb", "local/" + name},
   380  	}, func(hc *docker.HostConfig) {
   381  		hc.AutoRemove = true
   382  	})
   383  	require.NoError(tb, err)
   384  	require.NoError(tb, pool.Retry(func() error {
   385  		if _, ok := pool.ContainerByName(res.Container.Name); ok {
   386  			return fmt.Errorf("still running: %s", res.Container.Name)
   387  		}
   388  		return nil
   389  	}))
   390  }
   391  
   392  func getFiles(t *testing.T, ctx *context.Context, cfg config.Blob) []string {
   393  	t.Helper()
   394  	url, err := urlFor(ctx, cfg)
   395  	require.NoError(t, err)
   396  	conn, err := blob.OpenBucket(ctx, url)
   397  	require.NoError(t, err)
   398  	defer conn.Close()
   399  	iter := conn.List(nil)
   400  	var files []string
   401  	for {
   402  		file, err := iter.Next(ctx)
   403  		if err != nil && err == io.EOF {
   404  			break
   405  		}
   406  		require.NoError(t, err)
   407  		files = append(files, file.Key)
   408  	}
   409  	return files
   410  }