blob: 0d56b99f5bbc16406b7b77b2ed3afccdf1b82982 [file] [log] [blame]
package input
import (
"bytes"
"context"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"cloud.google.com/go/storage"
)
const contextTimeOut = time.Second * 50
// gcsDowndload calls the GCS client api to download a specifed object from
// a GCS bucket. ADC is used for authorization
// Input:
// (io.Writier) w - Output destination for download info
// (string) bucket - Name of the GCS bucket
// (string) object - Name of the GCS object
// (string) destDir - Destination for downloaded GCS object
// Output:
// (string) downloadedFile - Path to downloaded GCS object
func gcsDowndload(w io.Writer, bucket, object, destDir string) (string, error) {
// Call API to download GCS object into tempDir
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
return "", err
}
defer client.Close()
ctx, cancel := context.WithTimeout(ctx, contextTimeOut)
defer cancel()
rc, err := client.Bucket(bucket).Object(object).NewReader(ctx)
if err != nil {
return "", err
}
defer rc.Close()
data, err := ioutil.ReadAll(rc)
if err != nil {
return "", err
}
log.Print(log.New(w, "Blob "+object+" downloaded.\n", log.Ldate|log.Ltime|log.Lshortfile))
downloadedFile := filepath.Join(destDir, object)
if err := ioutil.WriteFile(downloadedFile, data, 0666); err != nil {
return "", err
}
return downloadedFile, nil
}
// getPartitionStart finds the start partition offset of the disk
// Input:
// (string) diskFile - Name of DOS/MBR file (ex: disk.raw)
// (string) parition - The parition number you are pulling the offset from
// Output:
// (int) start - The start of the partition on the disk
func getPartitionStart(partition, diskRaw string) (int, error) {
//create command
cmd1 := exec.Command("fdisk", "-l", diskRaw)
cmd2 := exec.Command("grep", "disk.raw"+partition)
reader, writer := io.Pipe()
var buf bytes.Buffer
cmd1.Stdout = writer
cmd2.Stdin = reader
cmd2.Stdout = &buf
cmd1.Start()
cmd2.Start()
cmd1.Wait()
writer.Close()
cmd2.Wait()
reader.Close()
words := strings.Fields(buf.String())
start, err := strconv.Atoi(words[1])
if err != nil {
return -1, err
}
return start, nil
}
// mountDisk finds a free loop device and mounts a DOS/MBR disk file
// Input:
// (string) diskFile - Name of DOS/MBR file (ex: disk.raw)
// (string) mountDir - Mount Destiination
// Output: nil on success, else error
func mountDisk(diskFile, mountDir string, flag int) error {
sectorSize := 512
startOfPartition, err := getPartitionStart("3", diskFile)
if err != nil {
return err
}
offset := strconv.Itoa(sectorSize * startOfPartition)
out, err := exec.Command("sudo", "losetup", "--show", "-fP", diskFile).Output()
if err != nil {
return err
}
_, err1 := exec.Command("sudo", "mount", "-o", "ro,loop,offset="+offset, string(out[:len(out)-1]), mountDir).Output()
if err1 != nil {
return err1
}
return nil
}
// GetGcsImage calls the GCS client api that downloads a specifed object from
// a GCS bucket and unzips its contents. ADC is used for authorization
// Input:
// (string) gcsPath - GCS "bucket/object" path for COS Image (.tar.gz file)
// Output:
// (string) imageDir - Path to the mounted directory of the COS Image
func GetGcsImage(gcsPath string, flag int) (string, error) {
bucket := strings.Split(gcsPath, "/")[0]
object := strings.Split(gcsPath, "/")[1]
tempDir, err := ioutil.TempDir(".", "tempDir-"+object) // Removed at end
if err != nil {
return "", err
}
tarFile, err := gcsDowndload(os.Stdout, bucket, object, tempDir)
if err != nil {
return "", err
}
imageDir := filepath.Join(tempDir, "Image-"+object)
if err = os.Mkdir(imageDir, 0700); err != nil {
return "", err
}
_, err1 := exec.Command("tar", "-xzf", tarFile, "-C", imageDir).Output()
if err1 != nil {
return "", err1
}
diskRaw := filepath.Join(imageDir, "disk.raw")
if err = mountDisk(diskRaw, imageDir, flag); err != nil {
return "", err
}
return imageDir, nil
}