package main import ( "bufio" "context" "io" "log" "net/http" "os" "strings" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/schollz/progressbar/v3" ) func listBucketContents(minioClient *minio.Client, bucketName string) ([]string, error) { var contents []string // Create a done channel to control the listing ctx := context.Background() // List all objects from bucket for object := range minioClient.ListObjects(ctx, bucketName, minio.ListObjectsOptions{ Recursive: true, }) { if object.Err != nil { return nil, object.Err } contents = append(contents, object.Key) } return contents, nil } type IsoInfo struct { Url string Hash string Distro string } func processStdin() []IsoInfo { var isos []IsoInfo scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { parts := strings.Split(scanner.Text(), " ") if len(parts) == 3 { iso := IsoInfo{ Url: parts[0], Hash: parts[1], Distro: parts[2], } isos = append(isos, iso) } } if err := scanner.Err(); err != nil { log.Println("Error reading from stdin:", err) } return isos } // CustomReader wraps an io.Reader with a progress bar type CustomReader struct { Reader io.Reader ProgressBar *progressbar.ProgressBar } func (r *CustomReader) Read(p []byte) (int, error) { n, err := r.Reader.Read(p) if n > 0 { r.ProgressBar.Add(n) } return n, err } func downloadAndUploadIso(minioClient *minio.Client, iso IsoInfo) error { // Create the destination file path destPath := "cache/" + iso.Hash + ".iso" // Download the ISO file resp, err := http.Get(iso.Url) if err != nil { return err } defer resp.Body.Close() // // Create download progress bar downloadBar := progressbar.DefaultBytes( resp.ContentLength, "Downloading: "+iso.Url, ) // Create the destination file destFile, err := os.Create(destPath) if err != nil { return err } defer destFile.Close() // Create a wrapped reader with progress bar reader := io.TeeReader(resp.Body, downloadBar) // Copy the downloaded content to the file _, err = io.Copy(destFile, reader) if err != nil { return err } // Reopen file for uploading uploadFile, err := os.Open(destPath) if err != nil { return err } defer uploadFile.Close() // Get file info for content-length fileInfo, err := uploadFile.Stat() if err != nil { return err } // Create upload progress bar uploadBar := progressbar.DefaultBytes( fileInfo.Size(), "Uploading: "+destPath, ) // Create wrapped reader for upload progress uploadReader := &CustomReader{ Reader: uploadFile, ProgressBar: uploadBar, } // Upload the file to MinIO _, err = minioClient.PutObject(context.Background(), "isos", "cache/"+iso.Hash+".iso", uploadReader, fileInfo.Size(), minio.PutObjectOptions{ ContentType: "application/x-iso9660-image", }) if err != nil { return err } return nil } func main() { endpoint := os.Getenv("STORAGE_URL") accessKeyID := "root" secretAccessKey := os.Getenv("MINIO_SECRET") if secretAccessKey == "" { log.Fatalln("MINIO_SECRET environment variable not set") } useSSL := false // Initialize minio client minioClient, err := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), Secure: useSSL, }) if err != nil { log.Fatalln(err) } // List contents of the "isos" bucket contents, err := listBucketContents(minioClient, "isos") if err != nil { log.Fatalln(err) } isos := processStdin() for _, iso := range isos { // Check if ISO exists in Minio isoPath := "cache/" + iso.Distro + "/" + iso.Hash + ".iso" exists := false for _, item := range contents { if item == isoPath { exists = true break } } if !exists { downloadAndUploadIso(minioClient, iso) } } }