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