cos_image_analyzer: Milestone 2

Change-Id: I6cf45c2f1f72126e76dcca59dae3214fa8171dc3
diff --git a/src/cmd/cos_image_analyzer/internal/binary/binaryDiff.go b/src/cmd/cos_image_analyzer/internal/binary/binarydiff.go
similarity index 71%
rename from src/cmd/cos_image_analyzer/internal/binary/binaryDiff.go
rename to src/cmd/cos_image_analyzer/internal/binary/binarydiff.go
index 02355f8..7f22f4c 100644
--- a/src/cmd/cos_image_analyzer/internal/binary/binaryDiff.go
+++ b/src/cmd/cos_image_analyzer/internal/binary/binarydiff.go
@@ -14,11 +14,11 @@
 
 // BinaryDiff is a tool that finds all binary differneces of two COS images
 // (COS version, rootfs, kernel command line, stateful parition, ...)
-// Input:
-//   (string) img1Path - The path to the root directory for COS image1
-//   (string) img2Path - The path to the root directory for COS image2
-// Output:
-//   (stdout) terminal ouput - All differences printed to the terminal
+//
+// Input:  (string) img1Path - The path to the root directory for COS image1
+//		   (string) img2Path - The path to the root directory for COS image2
+//
+// Output: (stdout) terminal ouput - All differences printed to the terminal
 func BinaryDiff(img1Path, img2Path string) error {
 	fmt.Println("================== Binary Differences ==================")
 
@@ -34,13 +34,15 @@
 	}
 
 	// Compare Version (Major)
-	_, err := utilities.CmpMapValues(verMap1, verMap2, "VERSION")
+	_, err = utilities.CmpMapValues(verMap1, verMap2, "VERSION")
 	if err != nil {
 		return err
 	}
 	// Compare BUILD_ID (Minor)
-	_, err := utilities.CmpMapValues(verMap1, verMap2, "BUILD_ID")
+	_, err = utilities.CmpMapValues(verMap1, verMap2, "BUILD_ID")
 	if err != nil {
 		return err
 	}
+
+	return nil
 }
diff --git a/src/cmd/cos_image_analyzer/internal/input/cleanup_api.go b/src/cmd/cos_image_analyzer/internal/input/cleanup_api.go
new file mode 100644
index 0000000..1d3bc50
--- /dev/null
+++ b/src/cmd/cos_image_analyzer/internal/input/cleanup_api.go
@@ -0,0 +1,23 @@
+package input
+
+import (
+	"os"
+	"os/exec"
+)
+
+// Cleanup is called to remove a mounted directory and its loop device
+//   (string) mountDir - Active mount directory ready to close
+//   (string) loopDevice - Active loop device ready to close
+// Output: nil on success, else error
+func Cleanup(mountDir, loopDevice string) error {
+	_, err := exec.Command("sudo", "umount", mountDir).Output()
+	if err != nil {
+		return err
+	}
+	_, err1 := exec.Command("sudo", "losetup", "-d", loopDevice).Output()
+	if err1 != nil {
+		return err1
+	}
+	os.Remove(mountDir)
+	return nil
+}
diff --git a/src/cmd/cos_image_analyzer/internal/input/gce_api.go b/src/cmd/cos_image_analyzer/internal/input/gce_api.go
new file mode 100644
index 0000000..c147b4c
--- /dev/null
+++ b/src/cmd/cos_image_analyzer/internal/input/gce_api.go
@@ -0,0 +1,96 @@
+package input
+
+import (
+	"bytes"
+	"encoding/json"
+	// "fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"path/filepath"
+	"strings"
+)
+
+const timeOut = "7200"
+const imageFormat = "vmdk"
+const name = "gcr.io/compute-image-tools/gce_vm_image_export:release"
+
+type Steps struct {
+	Args [6]string `json:"args"`
+	Name string    `json:"name"`
+	Env  [1]string `json:"env"`
+}
+
+type GcePayload struct {
+	Timeout string    `json:"timeout"`
+	Steps   [1]Steps  `json:"steps"`
+	Tags    [2]string `json:"tags"`
+}
+
+// gceExport calls the cloud build REST api that exports a public compute
+// image to a specfic GCS bucket.
+// Input:
+//   (string) projectID - project ID of the cloud project holding the image
+//   (string) bucket - name of the GCS bucket holding the COS Image
+//   (string) image - name of the source image to be exported
+// Output: None
+func gceExport(projectID, bucket, image string) error {
+	// API Variables
+	gceURL := "https://cloudbuild.googleapis.com/v1/projects/" + projectID + "/builds"
+	destURI := "gs://" + bucket + "/" + image + "." + imageFormat
+	args := [6]string{"-oauth=/usr/local/google/home/acueva/cos-googlesource/tools/src/cmd/cos_image_analyzer/internal/utilities/oauth.json", "-timeout=" + timeOut, "-source_image=" + image, "-client_id=api", "-format=" + imageFormat, "-destination_uri=" + destURI}
+	env := [1]string{"BUILD_ID=$BUILD_ID"}
+	tags := [2]string{"gce-daisy", "gce-daisy-image-export"}
+
+	// Build API bodies
+	steps := [1]Steps{Steps{Args: args, Name: name, Env: env}}
+	payload := &GcePayload{
+		Timeout: timeOut,
+		Steps:   steps,
+		Tags:    tags}
+
+	requestBody, err := json.Marshal(payload)
+	if err != nil {
+		return err
+	}
+	log.Println(string(requestBody))
+
+	resp, err := http.Post(gceURL, "application/json", bytes.NewBuffer(requestBody))
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+
+	log.Println(string(body))
+	return nil
+}
+
+// GetCosImage calls the cloud build api to export a public COS image to a
+// a GCS bucket and then calls GetGcsImage() to download that image from GCS.
+// ADC is used for authorization.
+// Input:
+//   (string) cosCloudPath - The "projectID/gcs-bucket/image" path of the
+//   source image to be exported
+// Output:
+//   (string) imageDir - Path to the mounted directory of the  COS Image
+func GetCosImage(cosCloudPath string) (string, error) {
+	spiltPath := strings.Split(cosCloudPath, "/")
+	projectID, bucket, image := spiltPath[0], spiltPath[1], spiltPath[2]
+
+	if err := gceExport(projectID, bucket, image); err != nil {
+		return "", err
+	}
+
+	gcsPath := filepath.Join(bucket, image)
+	imageDir, err := GetGcsImage(gcsPath, 1)
+	if err != nil {
+		return "", err
+	}
+
+	return imageDir, nil
+}
diff --git a/src/cmd/cos_image_analyzer/internal/input/gcs_api.go b/src/cmd/cos_image_analyzer/internal/input/gcs_api.go
new file mode 100644
index 0000000..3f657d9
--- /dev/null
+++ b/src/cmd/cos_image_analyzer/internal/input/gcs_api.go
@@ -0,0 +1,155 @@
+package input
+
+import (
+	"bytes"
+	"cloud.google.com/go/storage"
+	"context"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+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
+}
diff --git a/src/cmd/cos_image_analyzer/internal/input/parse_input.go b/src/cmd/cos_image_analyzer/internal/input/parse_input.go
new file mode 100644
index 0000000..9e081d1
--- /dev/null
+++ b/src/cmd/cos_image_analyzer/internal/input/parse_input.go
@@ -0,0 +1,94 @@
+package input
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"os"
+)
+
+// Custom usage function. See -h flag
+func printUsage() {
+	usageTemplate := `NAME
+cos_image_analyzer - finds all meaningful differences of two COS Images
+(binary, package, commit, and release notes differences)
+
+SYNOPSIS 
+%s [-local] DIRECTORY-1 DIRECTORY-2 (default true)
+	DIRECTORY 1/2 - the local directory path to the root of the COS Image
+
+%s [-gcs] GCS-PATH-1 GCS-PATH-2 
+	GCS-PATH 1/2 - GCS "bucket/object" path for the COS Image (.tar.gz file) 
+	Ex: %s -gcs my-bucket/cos-77-12371-273-0.tar.gz my-bucket/cos-81-12871-119-0.tar.gz
+
+%s [-cos-cloud]  COS-CLOUD-PATH-1 COS-CLOUD-PATH-2 
+	COS-CLOUD-PATH 1/2 - The "projectID/gcs-bucket/image" path of the source image to be exported
+	Ex: %s -cos-cloud my-project/my-bucket/my-exported-image1 my-project/my-bucket/my-exported-image2
+
+DESCRIPTION
+`
+	usage := fmt.Sprintf(usageTemplate, os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0])
+	fmt.Printf("%s", usage)
+	flag.PrintDefaults()
+	fmt.Println("\nOUTPUT\n(stdout) terminal output - All differences printed to the terminal")
+}
+
+// ParseInput handles the input based on its type and returns the root
+// directory path of both images to the start of the CosImageAnalyzer
+//
+// Input:  None (reads command-line args)
+//
+// Output: (string) rootImg1 - The local filesystem path for COS image1
+//		   (string) rootImg2 - The local filesystem path for COS image2
+func ParseInput() (string, string, error) {
+	// Flag Declaration
+	flag.Usage = printUsage
+	localPtr := flag.Bool("local", true, "input is two mounted images on local filesystem")
+	gcsPtr := flag.Bool("gcs", false, "input is two objects stored on Google Cloud Storage")
+	cosCloudPtr := flag.Bool("cos-cloud", false, "input is two public COS-cloud images")
+	flag.Parse()
+
+	if flag.NFlag() > 1 {
+		printUsage()
+		return "", "", errors.New("Error: Only one flag allowed")
+	}
+
+	// Input Selection
+	if *gcsPtr {
+		if len(flag.Args()) != 2 {
+			printUsage()
+			return "", "", errors.New("Error: GCS input requires two agruments")
+		}
+		rootImg1, err := GetGcsImage(flag.Args()[0], 1)
+		if err != nil {
+			return "", "", err
+		}
+		rootImg2, err := GetGcsImage(flag.Args()[1], 2)
+		if err != nil {
+			return "", "", err
+		}
+		return rootImg1, rootImg2, nil
+	} else if *cosCloudPtr {
+		if len(flag.Args()) != 2 {
+			printUsage()
+			return "", "", errors.New("Error: COS-cloud input requires two agruments")
+		}
+		rootImg1, err := GetCosImage(flag.Args()[0])
+		if err != nil {
+			return "", "", err
+		}
+		rootImg2, err := GetCosImage(flag.Args()[1])
+		if err != nil {
+			return "", "", err
+		}
+		return rootImg1, rootImg2, nil
+	} else if *localPtr {
+		if len(flag.Args()) != 2 {
+			printUsage()
+			return "", "", errors.New("Error: Local input requires two arguments")
+		}
+		return flag.Args()[0], flag.Args()[1], nil
+	}
+	printUsage()
+	return "", "", errors.New("Error: At least one flag needs to be true")
+}
diff --git a/src/cmd/cos_image_analyzer/internal/testData/os-release-77 b/src/cmd/cos_image_analyzer/internal/testdata/os-release-77
similarity index 100%
rename from src/cmd/cos_image_analyzer/internal/testData/os-release-77
rename to src/cmd/cos_image_analyzer/internal/testdata/os-release-77
diff --git a/src/cmd/cos_image_analyzer/internal/testData/os-release-81 b/src/cmd/cos_image_analyzer/internal/testdata/os-release-81
similarity index 100%
rename from src/cmd/cos_image_analyzer/internal/testData/os-release-81
rename to src/cmd/cos_image_analyzer/internal/testdata/os-release-81
diff --git a/src/cmd/cos_image_analyzer/internal/utilities/logic_helper.go b/src/cmd/cos_image_analyzer/internal/utilities/logic_helper.go
new file mode 100644
index 0000000..bfdc0ea
--- /dev/null
+++ b/src/cmd/cos_image_analyzer/internal/utilities/logic_helper.go
@@ -0,0 +1,9 @@
+package utilities
+
+// // Helper Function for error checking
+// func check(e error) error {
+// 	if e != nil {
+// 		return e
+// 	}
+// 	return nil
+// }
diff --git a/src/cmd/cos_image_analyzer/internal/utilities/mapHelpers.go b/src/cmd/cos_image_analyzer/internal/utilities/map_helpers.go
similarity index 69%
rename from src/cmd/cos_image_analyzer/internal/utilities/mapHelpers.go
rename to src/cmd/cos_image_analyzer/internal/utilities/map_helpers.go
index 7c8de36..06b1d39 100644
--- a/src/cmd/cos_image_analyzer/internal/utilities/mapHelpers.go
+++ b/src/cmd/cos_image_analyzer/internal/utilities/map_helpers.go
@@ -12,11 +12,10 @@
 // key: first word split by separator, value: rest of line after separator.
 // Ex: Inputs:  textLine: "NAME=Container-Optimized OS", sep: "="
 //	   Outputs:  map: {"NAME":"Container-Optimized OS"}
-// Input:
-//   (string) filePath - The command-line path to the text file
-//   (string) sep - The separator string for the key and value pairs
-// Output:
-//   (map[string]string) mapOfFile - The map of the read-in text file
+//
+// Input:	(string) filePath - The command-line path to the text file
+//			(string) sep - The separator string for the key and value pairs
+// Output: 	(map[string]string) mapOfFile - The map of the read-in text file
 func ReadFileToMap(filePath, sep string) (map[string]string, error) {
 	file, err := os.Open(filePath)
 	if err != nil {
@@ -32,19 +31,18 @@
 	}
 
 	if scanner.Err() != nil {
-		return map[string]string{}, err
+		return map[string]string{}, scanner.Err()
 	}
 	return mapOfFile, nil
 }
 
 // CmpMapValues is a helper function that compares a value shared by two maps
-// Input:
-//   (map[string]string) map1 - First map to be compared
-//   (map[string]string) map2 - Second map to be compared
-//   (string) key - The key of the value be compared in both maps
-// Output:
-//   (stdout) terminal - If equal, print nothing. Else print difference
-//   (int) result - -1 for error, 0 for no difference, 1 for difference
+// Input:  (map[string]string) map1 - First map to be compared
+//		   (map[string]string) map2 - Second map to be compared
+//		   (string) key - The key of the value be compared in both maps
+//
+// Output: (stdout) terminal - If equal, print nothing. Else print difference
+//		   (int)	result - -1 error, 0 for no difference, 1 for difference
 func CmpMapValues(map1, map2 map[string]string, key string) (int, error) {
 	value1, ok1 := map1[key]
 	value2, ok2 := map2[key]
diff --git a/src/cmd/cos_image_analyzer/internal/utilities/mapHelpers_test.go b/src/cmd/cos_image_analyzer/internal/utilities/map_helpers_test.go
similarity index 74%
rename from src/cmd/cos_image_analyzer/internal/utilities/mapHelpers_test.go
rename to src/cmd/cos_image_analyzer/internal/utilities/map_helpers_test.go
index 136aa31..8e43712 100644
--- a/src/cmd/cos_image_analyzer/internal/utilities/mapHelpers_test.go
+++ b/src/cmd/cos_image_analyzer/internal/utilities/map_helpers_test.go
@@ -7,9 +7,9 @@
 // test ReadFileToMap function
 func TestReadFileToMap(t *testing.T) {
 	// test normal file
-	testFile, sep := "../testData/os-release-77", "="
+	testFile, sep := "../testdata/os-release-77", "="
 	expectedMap := map[string]string{"BUILD_ID": "12371.273.0", "ID": "cos"}
-	resultMap := ReadFileToMap(testFile, sep)
+	resultMap, _ := ReadFileToMap(testFile, sep)
 
 	// Compare result with expected
 	if resultMap["BUILD_ID"] != expectedMap["BUILD_ID"] && resultMap["ID"] != expectedMap["ID"] {
@@ -25,12 +25,12 @@
 	testKey1, testKey2 := "ID", "VERSION"
 
 	// test similar keys
-	if result1 := CmpMapValues(testMap1, testMap2, testKey1); result1 != 0 { // Expect 0 for same values
+	if result1, _ := CmpMapValues(testMap1, testMap2, testKey1); result1 != 0 { // Expect 0 for same values
 		t.Errorf("CmpMapValues failed, expected %v, got %v", 0, result1)
 	}
 
 	// test different keys
-	if result2 := CmpMapValues(testMap1, testMap2, testKey2); result2 != 1 { // Expect 1 for different values
+	if result2, _ := CmpMapValues(testMap1, testMap2, testKey2); result2 != 1 { // Expect 1 for different values
 		t.Errorf("CmpMapValues failed, expected %v, got %v", 1, result2)
 	}
 }
diff --git a/src/cmd/cos_image_analyzer/internal/utilities/usage.go b/src/cmd/cos_image_analyzer/internal/utilities/usage.go
deleted file mode 100644
index 2f58383..0000000
--- a/src/cmd/cos_image_analyzer/internal/utilities/usage.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package utilities
-
-import (
-	"flag"
-	"fmt"
-	"os"
-)
-
-// Custom usage function. See -h flag
-func printUsage() {
-	fmt.Println("NAME\ncos_image_analyzer - finds all meaningful differences of two COS Images")
-	fmt.Print("(binary, package, commit, and release notes differences)\n\n")
-	fmt.Printf("SYNOPSIS\n%s [OPTION] argument1 argument2\n\nDESCRIPTION\n", os.Args[0])
-	fmt.Print("Default: input arguments are two local filesystem paths to root directiory of COS images\n\n")
-	flag.PrintDefaults()
-	fmt.Println("\nOUPUT\n(stdout) terminal ouput - All differences printed to the terminal")
-}
diff --git a/src/cmd/cos_image_analyzer/main.go b/src/cmd/cos_image_analyzer/main.go
index e81f15e..017d44a 100644
--- a/src/cmd/cos_image_analyzer/main.go
+++ b/src/cmd/cos_image_analyzer/main.go
@@ -1,24 +1,23 @@
 // cos_Image_Analyzer finds all the meaningful differences of two COS Images
 // (binary, package, commit, and release notes differences)
-// Input:
-//   (string) img1Path - The path for COS image1
-//   (string) img2Path - The path for COS image2
-//   (int) inputFlag - 0-Local filesystem path to root directory,
-//   1-COS cloud names, 2-GCS object names
-// Output:
-//   (stdout) terminal ouput - All differences printed to the terminal
+//
+// Input:  (string) rootImg1 - The path for COS image1
+//		   (string) rootImg2 - The path for COS image2
+//		   (int) inputFlag - 0-Local filesystem path to root directory,
+//		   1-COS cloud names, 2-GCS object names
+//
+// Output: (stdout) terminal ouput - All differences printed to the terminal
 package main
 
 import (
 	"cos.googlesource.com/cos/tools/src/cmd/cos_image_analyzer/internal/binary"
-	"cos.googlesource.com/cos/tools/src/cmd/cos_image_analyzer/internal/utilities"
-	"flag"
-	"log"
+	"cos.googlesource.com/cos/tools/src/cmd/cos_image_analyzer/internal/input"
+	"fmt"
 	"os"
 	"runtime"
 )
 
-func cosImageAnalyzer(img1Path, img2Path string, inputFlag int) error {
+func cosImageAnalyzer(img1Path, img2Path string) error {
 	err := binary.BinaryDiff(img1Path, img2Path)
 	if err != nil {
 		return err
@@ -28,22 +27,21 @@
 
 func main() {
 	if runtime.GOOS != "linux" {
-		log.Fatalf("Error: This is a Linux tool, can not run on %s", runtime.GOOS)
+		fmt.Printf("Error: This is a Linux tool, can not run on %s", runtime.GOOS)
+		os.Exit(1)
 	}
-	// Flag Declartions
-	flag.Usage = utilities.printUsage
-	cloudPtr := flag.Bool("cloud", false, "input arguments are two cos-cloud images")
-	gcsPtr := flag.Bool("gcs", false, "input arguments are two gcs objects")
-	flag.Parse()
-	if flag.NFlag() > 1 || len(flag.Args()) != 2 {
-		log.Fatalf("Error: %s requires at most one flag and two arguments. Use -h flag for usage", os.Args[0])
+	rootImg1, rootImg2, err := input.ParseInput()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
 	}
 
-	inputFlag := 0
-	if *cloudPtr {
-		inputFlag = 1
-	} else if *gcsPtr {
-		inputFlag = 2
+	err1 := cosImageAnalyzer(rootImg1, rootImg2)
+	if err1 != nil {
+		fmt.Println(err1)
+		os.Exit(1)
 	}
-	cosImageAnalyzer(flag.Args()[0], flag.Args()[1], inputFlag)
+	// Cleanup(rootImg1, loop1) Debating on a struct that holds this info
+	// Cleanup(rootImg2, loop2)
+
 }