github.com/triarius/goreleaser@v1.12.5/internal/pipe/blob/blob_minio_test.go (about) 1 package blob 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "net" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/triarius/goreleaser/internal/artifact" 16 "github.com/triarius/goreleaser/pkg/config" 17 "github.com/triarius/goreleaser/pkg/context" 18 "github.com/stretchr/testify/require" 19 "gocloud.dev/blob" 20 ) 21 22 const ( 23 minioUser = "minio" 24 minioPwd = "miniostorage" 25 containerName = "goreleaserTestMinio" 26 ) 27 28 var listen string 29 30 func TestMain(m *testing.M) { 31 listener, err := net.Listen("tcp", "127.0.0.1:0") 32 if err != nil { 33 fmt.Println(err) 34 os.Exit(1) 35 } 36 listener.Close() 37 listen = listener.Addr().String() 38 39 cleanup, err := start(listen) 40 if err != nil { 41 fmt.Println(err) 42 os.Exit(1) 43 } 44 prepareEnv() 45 46 code := m.Run() 47 if err := cleanup(); err != nil { 48 fmt.Println(err) 49 os.Exit(1) 50 } 51 os.Exit(code) 52 } 53 54 func TestMinioUpload(t *testing.T) { 55 name := "basic" 56 folder := t.TempDir() 57 srcpath := filepath.Join(folder, "source.tar.gz") 58 tgzpath := filepath.Join(folder, "bin.tar.gz") 59 debpath := filepath.Join(folder, "bin.deb") 60 checkpath := filepath.Join(folder, "check.txt") 61 sigpath := filepath.Join(folder, "f.sig") 62 certpath := filepath.Join(folder, "f.pem") 63 require.NoError(t, os.WriteFile(checkpath, []byte("fake checksums"), 0o744)) 64 require.NoError(t, os.WriteFile(srcpath, []byte("fake\nsrc"), 0o744)) 65 require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744)) 66 require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744)) 67 require.NoError(t, os.WriteFile(sigpath, []byte("fake\nsig"), 0o744)) 68 require.NoError(t, os.WriteFile(certpath, []byte("fake\ncert"), 0o744)) 69 ctx := context.New(config.Project{ 70 Dist: folder, 71 ProjectName: "testupload", 72 Blobs: []config.Blob{ 73 { 74 Provider: "s3", 75 Bucket: name, 76 Region: "us-east", 77 Endpoint: "http://" + listen, 78 IDs: []string{"foo", "bar"}, 79 ExtraFiles: []config.ExtraFile{ 80 { 81 Glob: "./testdata/*.golden", 82 }, 83 }, 84 }, 85 }, 86 }) 87 ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} 88 ctx.Artifacts.Add(&artifact.Artifact{ 89 Type: artifact.Checksum, 90 Name: "checksum.txt", 91 Path: checkpath, 92 }) 93 ctx.Artifacts.Add(&artifact.Artifact{ 94 Type: artifact.Signature, 95 Name: "checksum.txt.sig", 96 Path: sigpath, 97 Extra: map[string]interface{}{ 98 artifact.ExtraID: "foo", 99 }, 100 }) 101 ctx.Artifacts.Add(&artifact.Artifact{ 102 Type: artifact.Certificate, 103 Name: "checksum.pem", 104 Path: certpath, 105 Extra: map[string]interface{}{ 106 artifact.ExtraID: "foo", 107 }, 108 }) 109 ctx.Artifacts.Add(&artifact.Artifact{ 110 Type: artifact.UploadableSourceArchive, 111 Name: "source.tar.gz", 112 Path: srcpath, 113 Extra: map[string]interface{}{ 114 artifact.ExtraFormat: "tar.gz", 115 }, 116 }) 117 ctx.Artifacts.Add(&artifact.Artifact{ 118 Type: artifact.UploadableArchive, 119 Name: "bin.tar.gz", 120 Path: tgzpath, 121 Extra: map[string]interface{}{ 122 artifact.ExtraID: "foo", 123 }, 124 }) 125 ctx.Artifacts.Add(&artifact.Artifact{ 126 Type: artifact.LinuxPackage, 127 Name: "bin.deb", 128 Path: debpath, 129 Extra: map[string]interface{}{ 130 artifact.ExtraID: "bar", 131 }, 132 }) 133 134 setupBucket(t, name) 135 require.NoError(t, Pipe{}.Default(ctx)) 136 require.NoError(t, Pipe{}.Publish(ctx)) 137 138 require.Subset(t, getFiles(t, ctx, ctx.Config.Blobs[0]), []string{ 139 "testupload/v1.0.0/bin.deb", 140 "testupload/v1.0.0/bin.tar.gz", 141 "testupload/v1.0.0/checksum.txt", 142 "testupload/v1.0.0/checksum.txt.sig", 143 "testupload/v1.0.0/checksum.pem", 144 "testupload/v1.0.0/source.tar.gz", 145 "testupload/v1.0.0/file.golden", 146 }) 147 } 148 149 func TestMinioUploadCustomBucketID(t *testing.T) { 150 name := "fromenv" 151 folder := t.TempDir() 152 tgzpath := filepath.Join(folder, "bin.tar.gz") 153 debpath := filepath.Join(folder, "bin.deb") 154 require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744)) 155 require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744)) 156 // Set custom BUCKET_ID env variable. 157 require.NoError(t, os.Setenv("BUCKET_ID", name)) 158 ctx := context.New(config.Project{ 159 Dist: folder, 160 ProjectName: "testupload", 161 Blobs: []config.Blob{ 162 { 163 Provider: "s3", 164 Bucket: "{{.Env.BUCKET_ID}}", 165 Endpoint: "http://" + listen, 166 }, 167 }, 168 }) 169 ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} 170 ctx.Artifacts.Add(&artifact.Artifact{ 171 Type: artifact.UploadableArchive, 172 Name: "bin.tar.gz", 173 Path: tgzpath, 174 }) 175 ctx.Artifacts.Add(&artifact.Artifact{ 176 Type: artifact.LinuxPackage, 177 Name: "bin.deb", 178 Path: debpath, 179 }) 180 181 setupBucket(t, name) 182 require.NoError(t, Pipe{}.Default(ctx)) 183 require.NoError(t, Pipe{}.Publish(ctx)) 184 } 185 186 func TestMinioUploadRootFolder(t *testing.T) { 187 name := "rootdir" 188 folder := t.TempDir() 189 tgzpath := filepath.Join(folder, "bin.tar.gz") 190 debpath := filepath.Join(folder, "bin.deb") 191 require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744)) 192 require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744)) 193 ctx := context.New(config.Project{ 194 Dist: folder, 195 ProjectName: "testupload", 196 Blobs: []config.Blob{ 197 { 198 Provider: "s3", 199 Bucket: name, 200 Folder: "/", 201 Endpoint: "http://" + listen, 202 }, 203 }, 204 }) 205 ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} 206 ctx.Artifacts.Add(&artifact.Artifact{ 207 Type: artifact.UploadableArchive, 208 Name: "bin.tar.gz", 209 Path: tgzpath, 210 }) 211 ctx.Artifacts.Add(&artifact.Artifact{ 212 Type: artifact.LinuxPackage, 213 Name: "bin.deb", 214 Path: debpath, 215 }) 216 217 setupBucket(t, name) 218 require.NoError(t, Pipe{}.Default(ctx)) 219 require.NoError(t, Pipe{}.Publish(ctx)) 220 } 221 222 func TestMinioUploadInvalidCustomBucketID(t *testing.T) { 223 folder := t.TempDir() 224 tgzpath := filepath.Join(folder, "bin.tar.gz") 225 debpath := filepath.Join(folder, "bin.deb") 226 require.NoError(t, os.WriteFile(tgzpath, []byte("fake\ntargz"), 0o744)) 227 require.NoError(t, os.WriteFile(debpath, []byte("fake\ndeb"), 0o744)) 228 ctx := context.New(config.Project{ 229 Dist: folder, 230 ProjectName: "testupload", 231 Blobs: []config.Blob{ 232 { 233 Provider: "s3", 234 Bucket: "{{.Bad}}", 235 Endpoint: "http://" + listen, 236 }, 237 }, 238 }) 239 ctx.Git = context.GitInfo{CurrentTag: "v1.1.0"} 240 ctx.Artifacts.Add(&artifact.Artifact{ 241 Type: artifact.UploadableArchive, 242 Name: "bin.tar.gz", 243 Path: tgzpath, 244 }) 245 ctx.Artifacts.Add(&artifact.Artifact{ 246 Type: artifact.LinuxPackage, 247 Name: "bin.deb", 248 Path: debpath, 249 }) 250 251 require.NoError(t, Pipe{}.Default(ctx)) 252 require.Error(t, Pipe{}.Publish(ctx)) 253 } 254 255 func prepareEnv() { 256 os.Setenv("AWS_ACCESS_KEY_ID", minioUser) 257 os.Setenv("AWS_SECRET_ACCESS_KEY", minioPwd) 258 os.Setenv("AWS_REGION", "us-east-1") 259 } 260 261 func start(listen string) (func() error, error) { 262 data := filepath.Join(os.TempDir(), containerName) 263 264 fn := func() error { 265 if out, err := exec.Command("docker", "stop", containerName).CombinedOutput(); err != nil { 266 return fmt.Errorf("failed to stop minio: %s: %w", out, err) 267 } 268 if err := os.RemoveAll(data); err != nil { 269 log.Println("failed to remove", data) 270 } 271 return nil 272 } 273 274 // stop container if it is running (likely from previous test) 275 _, _ = exec.Command("docker", "stop", containerName).CombinedOutput() 276 277 if out, err := exec.Command( 278 "docker", "run", "-d", "--rm", 279 "-v", data+":/data", 280 "--name", containerName, 281 "-p", listen+":9000", 282 "-e", "MINIO_ROOT_USER="+minioUser, 283 "-e", "MINIO_ROOT_PASSWORD="+minioPwd, 284 "--health-interval", "1s", 285 "--health-cmd=curl --silent --fail http://localhost:9000/minio/health/ready || exit 1", 286 "minio/minio", 287 "server", "/data", "--console-address", ":9001", 288 ).CombinedOutput(); err != nil { 289 return fn, fmt.Errorf("failed to start minio: %s: %w", out, err) 290 } 291 292 for range time.Tick(time.Second) { 293 out, err := exec.Command("docker", "inspect", "--format='{{json .State.Health}}'", containerName).CombinedOutput() 294 if err != nil { 295 return fn, fmt.Errorf("failed to check minio status: %s: %w", string(out), err) 296 } 297 if strings.Contains(string(out), `"Status":"healthy"`) { 298 log.Println("minio is healthy") 299 break 300 } 301 log.Println("waiting for minio to be healthy") 302 } 303 304 return fn, nil 305 } 306 307 func setupBucket(tb testing.TB, name string) { 308 tb.Helper() 309 mc(tb, "mc mb local/"+name) 310 tb.Cleanup(func() { 311 mc(tb, "mc rb --force local/"+name) 312 }) 313 } 314 315 func mc(tb testing.TB, cmd string) { 316 tb.Helper() 317 318 if out, err := exec.Command( 319 "docker", "run", "--rm", 320 "--link", containerName, 321 "--entrypoint", "sh", 322 "minio/mc", 323 "-c", fmt.Sprintf( 324 "mc config host add local http://%s:9000 %s %s; %s", 325 containerName, minioUser, minioPwd, cmd, 326 ), 327 ).CombinedOutput(); err != nil { 328 tb.Fatalf("failed to create test bucket: %s", string(out)) 329 } 330 } 331 332 func getFiles(t *testing.T, ctx *context.Context, cfg config.Blob) []string { 333 t.Helper() 334 url, err := urlFor(ctx, cfg) 335 require.NoError(t, err) 336 conn, err := blob.OpenBucket(ctx, url) 337 require.NoError(t, err) 338 defer conn.Close() 339 iter := conn.List(nil) 340 var files []string 341 for { 342 file, err := iter.Next(ctx) 343 if err != nil && err == io.EOF { 344 break 345 } 346 require.NoError(t, err) 347 files = append(files, file.Key) 348 } 349 return files 350 }