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 }