Repository: nanovms/ops
Pull request #1023 (2021-05-09 20:22:21)

764 replace fmt with logger
1. Moved logger from `lepton` package as `log` package.
2. Added default logger for fast access using `log` package.
3. Replaced lots of fmt prints with logger prints.

arknable: A couple of build image tests are failing
arknable: The failed cases are being caused by missing `~/.ops/latest.txt` hence `getLocalRelVersion()` returns wrong version, i.e. `0.0` while the expected version is `0.1.34`.

@eyberg how to generate that `latest.txt` with correct version ?
arknable: oh the failed tests also occurs in `master` branch but `789-elf-validity-check` and `831-better-port-fwd-error` tests passed.
eyberg: @arknable we change the version whenever we manually cut a release via https://github.com/nanovms/ops/blob/master/release.sh but if you need to set it for a test or something than you can inject it via lepton.Version like https://github.com/nanovms/ops/blob/master/release.sh#L5
arknable: @eyberg ah it is ok I can fix that, turns out I can login to CI and it was lint error.

Patch:
diff --git a/aws/aws_image.go b/aws/aws_image.go
index 0bb03d8..99ed02c 100644
--- a/aws/aws_image.go
+++ b/aws/aws_image.go
@@ -13,6 +13,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 
@@ -356,10 +357,10 @@ func (p *AWS) findImageByName(name string) (*ec2.Image, error) {
 		if aerr, ok := err.(awserr.Error); ok {
 			switch aerr.Code() {
 			default:
-				fmt.Println(aerr.Error())
+				log.Error(aerr)
 			}
 		} else {
-			fmt.Println(err.Error())
+			log.Error(err)
 		}
 		return nil, err
 	}
@@ -373,7 +374,7 @@ func (p *AWS) findImageByName(name string) (*ec2.Image, error) {
 
 // SyncImage syncs image from provider to another provider
 func (p *AWS) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
@@ -399,7 +400,7 @@ func (p *AWS) waitSnapshotToBeReady(config *types.Config, importTaskID *string)
 		return nil, err
 	}
 
-	fmt.Println("waiting for snapshot - can take like 5 min...")
+	log.Info("waiting for snapshot - can take like 5 min...")
 
 	waitStartTime := time.Now()
 	bar := progressbar.New(100)
diff --git a/aws/aws_instance.go b/aws/aws_instance.go
index 5cbbef6..68a1ac2 100644
--- a/aws/aws_instance.go
+++ b/aws/aws_instance.go
@@ -13,6 +13,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/olekukonko/tablewriter"
 )
 
@@ -60,9 +61,7 @@ func getAWSInstances(region string, filter []*ec2.Filter) []lepton.CloudInstance
 	}
 	result, err := compute.DescribeInstances(&request)
 	if err != nil {
-		fmt.Println(err)
-		fmt.Println("failed getting instances")
-		os.Exit(1)
+		log.Fatalf("failed getting instances: ", err.Error())
 	}
 
 	var cinstances []lepton.CloudInstance
@@ -159,7 +158,7 @@ func (p *AWS) CreateInstance(ctx *lepton.Context) error {
 	ctx.Logger().Debug("getting aws images")
 	result, err := getAWSImages(p.ec2)
 	if err != nil {
-		ctx.Logger().Error("failed getting images")
+		ctx.Logger().Errorf("failed getting images")
 		return err
 	}
 
@@ -277,11 +276,11 @@ func (p *AWS) CreateInstance(ctx *lepton.Context) error {
 	ctx.Logger().Debug("running instance with input %v", instanceInput)
 	_, err = svc.RunInstances(instanceInput)
 	if err != nil {
-		fmt.Println("Could not create instance", err)
+		log.Errorf("Could not create instance", err)
 		return err
 	}
 
-	fmt.Println("Created instance", tagInstanceName)
+	log.Info("Created instance", tagInstanceName)
 
 	// create dns zones/records to associate DNS record to instance IP
 	if cloudConfig.DomainName != "" {
@@ -384,10 +383,10 @@ func (p *AWS) DeleteInstance(ctx *lepton.Context, instanceName string) error {
 		if aerr, ok := err.(awserr.Error); ok {
 			switch aerr.Code() {
 			default:
-				fmt.Println(aerr.Error())
+				log.Error(aerr)
 			}
 		} else {
-			fmt.Println(err.Error())
+			log.Error(err)
 		}
 		return err
 	}
@@ -429,10 +428,10 @@ func (p *AWS) GetInstanceLogs(ctx *lepton.Context, instanceName string) (string,
 		if aerr, ok := err.(awserr.Error); ok {
 			switch aerr.Code() {
 			default:
-				fmt.Println(aerr.Error())
+				log.Error(aerr)
 			}
 		} else {
-			fmt.Println(err.Error())
+			log.Error(err)
 		}
 		return "", err
 	}
diff --git a/aws/aws_store.go b/aws/aws_store.go
index 2ce2e98..90d0316 100644
--- a/aws/aws_store.go
+++ b/aws/aws_store.go
@@ -5,6 +5,7 @@ import (
 	"math"
 	"os"
 
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 
 	"github.com/aws/aws-sdk-go/aws"
@@ -40,7 +41,7 @@ func (s *S3) CopyToBucket(config *types.Config, archPath string) error {
 	}
 
 	fileStats, _ := file.Stat()
-	fmt.Println("Uploading image with", fmt.Sprintf("%fMB", float64(fileStats.Size())/math.Pow(10, 6)))
+	log.Info("Uploading image with", fmt.Sprintf("%fMB", float64(fileStats.Size())/math.Pow(10, 6)))
 
 	uploader := s3manager.NewUploader(sess)
 	_, err = uploader.Upload(&s3manager.UploadInput{
@@ -80,10 +81,10 @@ func (s *S3) DeleteFromBucket(config *types.Config, key string) error {
 		if aerr, ok := err.(awserr.Error); ok {
 			switch aerr.Code() {
 			default:
-				fmt.Println(aerr.Error())
+				log.Error(aerr)
 			}
 		} else {
-			fmt.Println(err.Error())
+			log.Error(err)
 		}
 		return err
 	}
diff --git a/aws/role.go b/aws/role.go
index 45feeb8..016ffd7 100644
--- a/aws/role.go
+++ b/aws/role.go
@@ -11,6 +11,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/iam"
+	"github.com/nanovms/ops/log"
 )
 
 const vmiName = "vmimport"
@@ -115,7 +116,7 @@ func appendBucket(role string, bucket string) string {
 	rp := &RolePolicy{}
 	err := json.Unmarshal([]byte(role), rp)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	for i := 0; i < len(rp.Statement); i++ {
@@ -130,7 +131,7 @@ func appendBucket(role string, bucket string) string {
 
 	b, err := json.Marshal(rp)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return string(b)
@@ -138,7 +139,7 @@ func appendBucket(role string, bucket string) string {
 }
 
 func roleError(bucket string, err error) {
-	fmt.Println(err)
+	log.Error(err)
 	fmt.Println(roleMsg)
 
 	rp := strings.ReplaceAll(rolePolicy, "my-bucket", bucket)
@@ -253,7 +254,7 @@ func VerifyRole(zone string, bucket string) {
 }
 
 func createRole(svc *iam.IAM, bucket string) error {
-	fmt.Println("creating a vmimport role for bucket " + bucket)
+	log.Info("creating a vmimport role for bucket " + bucket)
 
 	ri := &iam.CreateRoleInput{
 		AssumeRolePolicyDocument: aws.String(vmieDoc),
diff --git a/azure/azure.go b/azure/azure.go
index 4d66694..a7fffa0 100644
--- a/azure/azure.go
+++ b/azure/azure.go
@@ -12,6 +12,7 @@ import (
 	"github.com/Azure/go-autorest/autorest/azure/auth"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute"
@@ -142,8 +143,7 @@ func (a *Azure) getLocation(config *types.Config) string {
 		location = a.locationDefault
 	}
 	if location == "" {
-		fmt.Println("Error: a location must be set via either the Zone attribute in CloudConfig or the AZURE_LOCATION_DEFAULT environment variable.")
-		os.Exit(1)
+		log.Fatalf("Error: a location must be set via either the Zone attribute in CloudConfig or the AZURE_LOCATION_DEFAULT environment variable.")
 	}
 	return location
 }
diff --git a/azure/azure_image.go b/azure/azure_image.go
index 42871db..584fba3 100644
--- a/azure/azure_image.go
+++ b/azure/azure_image.go
@@ -10,6 +10,7 @@ import (
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 )
@@ -117,9 +118,9 @@ func (a *Azure) CreateImage(ctx *lepton.Context, imagePath string) error {
 
 	_, err = imagesClient.CreateOrUpdate(context.TODO(), a.groupName, imgName, imageParams)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	} else {
-		fmt.Println("Image created")
+		log.Info("Image created")
 	}
 
 	return nil
@@ -200,7 +201,7 @@ func (a *Azure) DeleteImage(ctx *lepton.Context, imagename string) error {
 
 // SyncImage syncs image from provider to another provider
 func (a *Azure) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/azure/azure_instance.go b/azure/azure_instance.go
index 568f6a8..38113c4 100644
--- a/azure/azure_instance.go
+++ b/azure/azure_instance.go
@@ -15,6 +15,7 @@ import (
 	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/olekukonko/tablewriter"
 )
 
@@ -66,14 +67,14 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 	if configVPC != "" {
 		vnet, err = a.GetVPC(configVPC)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return fmt.Errorf("error getting virtual network with id %s", configVPC)
 		}
 	} else {
 		ctx.Logger().Info("creating virtual network with id %s", vmName)
 		vnet, err = a.CreateVirtualNetwork(context.TODO(), location, vmName)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("error creating virtual network")
 		}
 	}
@@ -84,14 +85,14 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 	if configSecurityGroup != "" {
 		nsg, err = a.GetNetworkSecurityGroup(context.TODO(), configSecurityGroup)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("error getting security group")
 		}
 	} else {
 		ctx.Logger().Info("creating network security group with id %s", vmName)
 		nsg, err = a.CreateNetworkSecurityGroup(context.TODO(), location, vmName, c)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("error creating network security group")
 		}
 	}
@@ -102,14 +103,14 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 	if configSubnet != "" {
 		subnet, err = a.GetVirtualNetworkSubnet(context.TODO(), *vnet.Name, configSubnet)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("error getting subnet")
 		}
 	} else {
 		ctx.Logger().Info("creating subnet with id %s", vmName)
 		subnet, err = a.CreateSubnetWithNetworkSecurityGroup(context.TODO(), *vnet.Name, vmName, "10.0.0.0/24", *nsg.Name)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("error creating subnet")
 		}
 	}
@@ -118,7 +119,7 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 	ctx.Logger().Info("creating public ip with id %s", vmName)
 	ip, err := a.CreatePublicIP(context.TODO(), location, vmName)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("error creating public ip")
 	}
 
@@ -127,7 +128,7 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 	ctx.Logger().Info("creating network interface controller with id %s", vmName)
 	nic, err := a.CreateNIC(context.TODO(), location, *vnet.Name, *subnet.Name, *nsg.Name, *ip.Name, vmName)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("error creating network interface controller")
 	}
 
@@ -201,19 +202,17 @@ func (a *Azure) CreateInstance(ctx *lepton.Context) error {
 		},
 	)
 	if err != nil {
-		fmt.Printf("cannot create vm: %v\n", err.Error())
-		os.Exit(1)
+		log.Fatalf("cannot create vm: %v\n", err.Error())
 	}
 
 	err = future.WaitForCompletionRef(nctx, vmClient.Client)
 	if err != nil {
-		fmt.Printf("cannot get the vm create or update future response: %v\n", err.Error())
-		os.Exit(1)
+		log.Fatalf("cannot get the vm create or update future response: %v\n", err.Error())
 	}
 
 	_, err = future.Result(*vmClient)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	if ctx.Config().CloudConfig.DomainName != "" {
@@ -300,7 +299,7 @@ func (a *Azure) convertToCloudInstance(instance *compute.VirtualMachine, nicClie
 
 	pubip, err := ipClient.Get(context.TODO(), a.groupName, cinstance.Name, "")
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 	publicIP = *(*pubip.PublicIPAddressPropertiesFormat).IPAddress
 
@@ -359,13 +358,13 @@ func (a *Azure) DeleteInstance(ctx *lepton.Context, instancename string) error {
 	ctx.Logger().Info("Deleting vm with ID %s...", instancename)
 	future, err := vmClient.Delete(context.TODO(), a.groupName, instancename)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("Unable to delete instance")
 	}
 
 	err = future.WaitForCompletionRef(context.TODO(), vmClient.Client)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("error waiting for vm deletion")
 	}
 
@@ -379,7 +378,7 @@ func (a *Azure) DeleteInstance(ctx *lepton.Context, instancename string) error {
 
 		nic, err := nicClient.Get(context.TODO(), a.groupName, nicID, "")
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			return errors.New("failed getting nic")
 		}
 
@@ -417,7 +416,7 @@ func (a *Azure) StartInstance(ctx *lepton.Context, instancename string) error {
 	fmt.Printf("Starting instance %s", instancename)
 	_, err := vmClient.Start(context.TODO(), a.groupName, instancename)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed starting virtual machine")
 	}
 
@@ -458,7 +457,7 @@ func (a *Azure) GetInstanceLogs(ctx *lepton.Context, instancename string) (strin
 
 	accountName, accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT"), os.Getenv("AZURE_STORAGE_ACCESS_KEY")
 	if len(accountName) == 0 || len(accountKey) == 0 {
-		fmt.Println("Either the AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY environment variable is not set")
+		log.Warn("Either the AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY environment variable is not set")
 	}
 
 	credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
@@ -478,8 +477,7 @@ func (a *Azure) GetInstanceLogs(ctx *lepton.Context, instancename string) (strin
 
 	vm, err := vmClient.Get(context.TODO(), a.groupName, vmName, compute.InstanceView)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	// this is unique per vm || per boot?
diff --git a/azure/azure_network.go b/azure/azure_network.go
index 2026d38..f1dc85f 100644
--- a/azure/azure_network.go
+++ b/azure/azure_network.go
@@ -104,13 +104,13 @@ func (a *Azure) DeleteNIC(ctx *lepton.Context, nic *network.Interface) error {
 	nicName := getAzureResourceNameFromID(*nic.ID)
 	nicDeleteTask, err := nicClient.Delete(context.TODO(), a.groupName, nicName)
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err)
 		return errors.New("error deleting network interface controller")
 	}
 
 	err = nicDeleteTask.WaitForCompletionRef(context.TODO(), nicClient.Client)
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err)
 		return errors.New("error waiting for network interface controller deleting")
 	}
 
@@ -128,13 +128,13 @@ func (a *Azure) DeleteIP(ctx *lepton.Context, ipConfiguration *network.Interface
 	logger.Info("Deleting public IP %s...", ipID)
 	deleteIPTask, err := ipClient.Delete(context.TODO(), a.groupName, ipID)
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err)
 		return errors.New("failed deleting ip")
 	}
 
 	err = deleteIPTask.WaitForCompletionRef(context.TODO(), ipClient.Client)
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err)
 		return errors.New("failed waiting for ip deletion")
 	}
 
@@ -158,7 +158,7 @@ func (a *Azure) DeleteNetworkSecurityGroup(ctx *lepton.Context, securityGroupID
 	securityGroupName := getAzureResourceNameFromID(securityGroupID)
 	securityGroup, err := nsgClient.Get(context.TODO(), a.groupName, securityGroupName, "")
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err)
 		return errors.New("error getting network security group")
 	}
 
@@ -170,13 +170,13 @@ func (a *Azure) DeleteNetworkSecurityGroup(ctx *lepton.Context, securityGroupID
 		logger.Info("Deleting %s...", *securityGroup.ID)
 		nsgTask, err := nsgClient.Delete(context.TODO(), a.groupName, *securityGroup.Name)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("failed deleting security group")
 		}
 
 		err = nsgTask.WaitForCompletionRef(context.TODO(), nsgClient.Client)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("failed waiting for security group deletion")
 		}
 	}
@@ -203,13 +203,13 @@ func (a *Azure) DeleteSubnetwork(ctx *lepton.Context, subnetID string) error {
 
 	subnet, err := subnetsClient.Get(context.TODO(), a.groupName, vnName, subnetName, "")
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return fmt.Errorf("failed getting subnet")
 	}
 
 	virtualNetwork, err := vnetClient.Get(context.TODO(), a.groupName, vnName, "")
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed getting virtual network")
 	}
 
@@ -217,26 +217,26 @@ func (a *Azure) DeleteSubnetwork(ctx *lepton.Context, subnetID string) error {
 		logger.Info("Deleting %s...", *subnet.ID)
 		subnetDeleteTask, err := subnetsClient.Delete(context.TODO(), a.groupName, subnetName, subnetName)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("error deleting subnet")
 		}
 
 		err = subnetDeleteTask.WaitForCompletionRef(context.TODO(), subnetsClient.Client)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("error waiting for subnet deletion")
 		}
 
 		logger.Info("Deleting virtualNetworks/%s", vnName)
 		vnDeleteTask, err := vnetClient.Delete(context.TODO(), a.groupName, vnName)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("error deleting virtual network")
 		}
 
 		err = vnDeleteTask.WaitForCompletionRef(context.TODO(), vnetClient.Client)
 		if err != nil {
-			logger.Error(err.Error())
+			logger.Error(err)
 			return errors.New("error waiting for virtual network deletion")
 		}
 	} else {
diff --git a/azure/azure_store.go b/azure/azure_store.go
index cc355a0..3589f4d 100644
--- a/azure/azure_store.go
+++ b/azure/azure_store.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"log"
 	"net/url"
 	"os"
 	"os/exec"
@@ -14,6 +13,7 @@ import (
 	"strings"
 
 	"github.com/Azure/azure-storage-blob-go/azblob"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -61,7 +61,7 @@ func (az *Storage) virtualSize(archPath string) uint32 {
 	cmd := exec.Command("qemu-img", args...)
 	out, err := cmd.Output()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	qi := &qemuInfo{}
@@ -73,19 +73,19 @@ func (az *Storage) virtualSize(archPath string) uint32 {
 func (az *Storage) resizeImage(basePath string, newPath string, resizeSz uint32) {
 	in, err := os.Open(basePath)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 	defer in.Close()
 
 	out, err := os.Create(newPath)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 	defer out.Close()
 
 	_, err = io.Copy(out, in)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	szstr := fmt.Sprint(resizeSz)
@@ -98,7 +98,7 @@ func (az *Storage) resizeImage(basePath string, newPath string, resizeSz uint32)
 	cmd := exec.Command("qemu-img", args...)
 	_, err = cmd.Output()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 }
 
@@ -137,7 +137,7 @@ func (az *Storage) CopyToBucket(config *types.Config, imgPath string) error {
 	cmd := exec.Command("qemu-img", args...)
 	err := cmd.Run()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	ctx := context.Background()
@@ -147,7 +147,7 @@ func (az *Storage) CopyToBucket(config *types.Config, imgPath string) error {
 		fmt.Printf("Creating a container named %s\n", containerName)
 		_, err = containerURL.Create(ctx, azblob.Metadata{}, azblob.PublicAccessNone)
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 	}
 
@@ -155,13 +155,13 @@ func (az *Storage) CopyToBucket(config *types.Config, imgPath string) error {
 
 	file, err := os.Open(vhdPath)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 	defer file.Close()
 
 	fi, err := file.Stat()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	max := 4194304
@@ -185,8 +185,7 @@ func (az *Storage) CopyToBucket(config *types.Config, imgPath string) error {
 
 		_, err = blobURL.UploadPages(ctx, int64(i*max), bytes.NewReader(page[:n]), azblob.PageBlobAccessConditions{}, nil)
 		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
+			log.Fatal(err)
 		}
 	}
 
@@ -222,14 +221,12 @@ func containerExists(containerURL azblob.ContainerURL) bool {
 func getContainerURL(containerName string) azblob.ContainerURL {
 	accountName, accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT"), os.Getenv("AZURE_STORAGE_ACCESS_KEY")
 	if len(accountName) == 0 || len(accountKey) == 0 {
-		fmt.Println("Either the AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY environment variable is not set")
-		os.Exit(1)
+		log.Fatalf("Either the AZURE_STORAGE_ACCOUNT or AZURE_STORAGE_ACCESS_KEY environment variable is not set")
 	}
 
 	credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
 	if err != nil {
-		fmt.Println("Invalid credentials with error: " + err.Error())
-		os.Exit(1)
+		log.Fatalf("Invalid credentials with error: " + err.Error())
 	}
 	p := azblob.NewPipeline(credential, azblob.PipelineOptions{})
 
diff --git a/azure/azure_volume.go b/azure/azure_volume.go
index 08801ae..d3c9d4d 100644
--- a/azure/azure_volume.go
+++ b/azure/azure_volume.go
@@ -10,6 +10,7 @@ import (
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 )
 
 // CreateVolume uploads the volume raw file and creates a disk from it
@@ -172,7 +173,7 @@ func (a *Azure) AttachVolume(ctx *lepton.Context, image, name string) error {
 		return fmt.Errorf("cannot update vm: %v", err)
 	}
 
-	fmt.Println("attaching the volume - this can take a few minutes - you can ctrl-c this after a bit")
+	log.Info("attaching the volume - this can take a few minutes - you can ctrl-c this after a bit")
 
 	err = future.WaitForCompletionRef(context.TODO(), vmClient.Client)
 	if err != nil {
@@ -206,7 +207,7 @@ func (a *Azure) DetachVolume(ctx *lepton.Context, image, name string) error {
 		return fmt.Errorf("cannot update vm: %v", err)
 	}
 
-	fmt.Println("detaching the volume - this can take a few minutes - you can ctrl-c this after a bit")
+	log.Info("detaching the volume - this can take a few minutes - you can ctrl-c this after a bit")
 
 	err = future.WaitForCompletionRef(context.TODO(), vmClient.Client)
 	if err != nil {
diff --git a/cmd/cmd_build.go b/cmd/cmd_build.go
index b7027eb..999dade 100644
--- a/cmd/cmd_build.go
+++ b/cmd/cmd_build.go
@@ -2,9 +2,9 @@ package cmd
 
 import (
 	"fmt"
-	"os"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/spf13/cobra"
 )
 
@@ -58,8 +58,7 @@ func buildCommandHandler(cmd *cobra.Command, args []string) {
 	var imagePath string
 
 	if imagePath, err = p.BuildImage(ctx); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 	fmt.Printf("Bootable image file:%s\n", imagePath)
 }
diff --git a/cmd/cmd_image.go b/cmd/cmd_image.go
index 406e634..60e693e 100644
--- a/cmd/cmd_image.go
+++ b/cmd/cmd_image.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/nanovms/ops/lepton"
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/provider"
 	"github.com/nanovms/ops/types"
 	"github.com/spf13/cobra"
@@ -222,7 +223,7 @@ func imageDeleteCommandHandler(cmd *cobra.Command, args []string) {
 	}
 
 	if len(imagesToDelete) == 0 {
-		fmt.Println("There are no images to delete")
+		log.Info("There are no images to delete")
 		return
 	}
 
@@ -256,7 +257,7 @@ func imageDeleteCommandHandler(cmd *cobra.Command, args []string) {
 	for range imagesToDelete {
 		err = <-responses
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 	}
 }
diff --git a/cmd/cmd_pkg.go b/cmd/cmd_pkg.go
index 153899d..eb2f6b0 100644
--- a/cmd/cmd_pkg.go
+++ b/cmd/cmd_pkg.go
@@ -3,7 +3,6 @@ package cmd
 import (
 	"bufio"
 	"fmt"
-	"log"
 	"os"
 	"path"
 	"path/filepath"
@@ -16,6 +15,7 @@ import (
 
 	"github.com/nanovms/ops/lepton"
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 )
 
 // PackageCommands gives package related commands
@@ -146,14 +146,13 @@ func cmdPackageDescribe(cmd *cobra.Command, args []string) {
 
 	description := path.Join(expackage, "README.md")
 	if _, err := os.Stat(description); err != nil {
-		fmt.Println("Error: Package information not provided.")
+		log.Errorf("Error: Package information not provided.")
 		return
 	}
 
 	file, err := os.Open(description)
 	if err != nil {
-		fmt.Println(err.Error())
-		os.Exit(1)
+		log.Fatal(err)
 	}
 	defer file.Close()
 
@@ -164,8 +163,7 @@ func cmdPackageDescribe(cmd *cobra.Command, args []string) {
 	}
 
 	if err := scanner.Err(); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 }
 
diff --git a/cmd/cmd_profile.go b/cmd/cmd_profile.go
index 162a5c7..048be1c 100644
--- a/cmd/cmd_profile.go
+++ b/cmd/cmd_profile.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/qemu"
 	"github.com/spf13/cobra"
 )
@@ -35,7 +36,7 @@ func (p *Profile) save() {
 
 	err := ioutil.WriteFile(local, []byte(str), 0644)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 }
 
@@ -46,7 +47,7 @@ func (p *Profile) setProfile() {
 
 	qv, err := qemu.Version()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	p.QemuVersion = qv
@@ -69,7 +70,7 @@ func (p *Profile) virtualized() bool {
 
 	content, err := ioutil.ReadFile(s)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	text := string(content)
diff --git a/cmd/cmd_root.go b/cmd/cmd_root.go
index aeef779..a653c0f 100644
--- a/cmd/cmd_root.go
+++ b/cmd/cmd_root.go
@@ -1,12 +1,40 @@
 package cmd
 
 import (
+	"os"
+	"strings"
+
+	"github.com/nanovms/ops/log"
+	"github.com/nanovms/ops/types"
 	"github.com/spf13/cobra"
 )
 
 // GetRootCommand provides set all commands for Ops
 func GetRootCommand() *cobra.Command {
-	var rootCmd = &cobra.Command{Use: "ops"}
+	var rootCmd = &cobra.Command{
+		Use: "ops",
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			var config *types.Config
+
+			configFlag, _ := cmd.Flags().GetString("config")
+			configFlag = strings.TrimSpace(configFlag)
+
+			if configFlag != "" {
+				config := &types.Config{}
+				if err := unWarpConfig(configFlag, config); err != nil {
+					return err
+				}
+
+				globalFlags := NewGlobalCommandFlags(cmd.Flags())
+				if err := globalFlags.MergeToConfig(config); err != nil {
+					return err
+				}
+			}
+
+			log.InitDefault(os.Stdout, config)
+			return nil
+		},
+	}
 
 	// persist flags transversal to every command
 	PersistGlobalCommandFlags(rootCmd.PersistentFlags())
diff --git a/cmd/cmd_update.go b/cmd/cmd_update.go
index 54aedd5..db90108 100644
--- a/cmd/cmd_update.go
+++ b/cmd/cmd_update.go
@@ -7,6 +7,7 @@ import (
 	"runtime"
 
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/spf13/cobra"
 )
 
@@ -21,25 +22,23 @@ func UpdateCommand() *cobra.Command {
 }
 
 func updateCommandHandler(cmd *cobra.Command, args []string) {
-	fmt.Println("Checking for updates...")
+	log.Info("Checking for updates...")
 	err := api.DoUpdate(fmt.Sprintf(api.OpsReleaseURL, runtime.GOOS))
 	if err != nil {
-		fmt.Println("Failed to update.", err)
+		log.Errorf("Failed to update.", err)
 	} else {
-		fmt.Println("Updates ops to latest release.")
+		log.Info("Updates ops to latest release.")
 	}
 	local, remote := api.LocalReleaseVersion, api.LatestReleaseVersion
 	_, err = os.Stat(path.Join(api.GetOpsHome(), local))
 	if local == "0.0" || parseVersion(local, 4) != parseVersion(remote, 4) || os.IsNotExist(err) {
 		err = api.DownloadReleaseImages(remote)
 		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
+			log.Fatal(err)
 		}
 		err = api.DownloadCommonFiles()
 		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
+			log.Fatal(err)
 		}
 		api.UpdateLocalRelease(remote)
 		fmt.Printf("Update nanos to %s version.\n", remote)
diff --git a/cmd/download_package.go b/cmd/download_package.go
index 63d239e..691233e 100644
--- a/cmd/download_package.go
+++ b/cmd/download_package.go
@@ -1,11 +1,11 @@
 package cmd
 
 import (
-	"fmt"
 	"os"
 	"path"
 
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 )
 
 func downloadLocalPackage(pkg string) string {
@@ -21,23 +21,20 @@ func downloadPackage(pkg string) string {
 func downloadAndExtractPackage(packagesDirPath, pkg string) string {
 	err := os.MkdirAll(packagesDirPath, 0755)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	expackage := path.Join(packagesDirPath, pkg)
 	opsPackage, err := api.DownloadPackage(pkg)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	api.ExtractPackage(opsPackage, packagesDirPath)
 
 	err = os.Remove(opsPackage)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	return expackage
diff --git a/cmd/error.go b/cmd/error.go
index 7ab21e5..a70b89f 100644
--- a/cmd/error.go
+++ b/cmd/error.go
@@ -6,23 +6,23 @@ import (
 
 	"github.com/go-errors/errors"
 	"github.com/nanovms/ops/constants"
+	"github.com/nanovms/ops/log"
 	"github.com/spf13/cobra"
 )
 
 func exitWithError(errs string) {
-	fmt.Println(fmt.Sprintf(constants.ErrorColor, errs))
-	os.Exit(1)
+	log.Fatalf(fmt.Sprintf(constants.ErrorColor, errs))
 }
 
 func exitForCmd(cmd *cobra.Command, errs string) {
-	fmt.Println(fmt.Sprintf(constants.ErrorColor, errs))
+	log.Errorf(constants.ErrorColor, errs)
 	cmd.Help()
 	os.Exit(1)
 }
 
 func panicOnError(err error) {
 	if err != nil {
-		fmt.Println(err.(*errors.Error).ErrorStack())
+		log.Errorf(err.(*errors.Error).ErrorStack())
 		panic(err)
 	}
 }
diff --git a/cmd/flags_config.go b/cmd/flags_config.go
index 3bc4291..c3eedf6 100644
--- a/cmd/flags_config.go
+++ b/cmd/flags_config.go
@@ -5,10 +5,11 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"os"
+	"path"
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/onprem"
 	"github.com/nanovms/ops/types"
 	"github.com/spf13/pflag"
@@ -36,6 +37,8 @@ func (flags *ConfigCommandFlags) MergeToConfig(c *types.Config) (err error) {
 
 		err = unWarpConfig(flags.Config, c)
 
+		c.LocalFilesParentDirectory = path.Dir(flags.Config)
+
 		return
 	} else if c == nil {
 		*c = *lepton.NewConfig()
@@ -48,8 +51,7 @@ func (flags *ConfigCommandFlags) MergeToConfig(c *types.Config) (err error) {
 func unWarpConfig(file string, c *types.Config) (err error) {
 	data, err := ioutil.ReadFile(file)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error reading config: %v\n", err)
-		os.Exit(1)
+		log.Fatalf("error reading config: %v\n", err)
 	}
 
 	return ConvertJSONToConfig(data, c)
diff --git a/cmd/flags_nightly.go b/cmd/flags_nightly.go
index 8559a3f..fe822c3 100644
--- a/cmd/flags_nightly.go
+++ b/cmd/flags_nightly.go
@@ -7,9 +7,9 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/nanovms/ops/types"
-
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
+	"github.com/nanovms/ops/types"
 	"github.com/spf13/pflag"
 	"github.com/ttacon/chalk"
 )
@@ -71,13 +71,11 @@ func updateNanosToolsPaths(c *types.Config, version string) {
 	}
 
 	if _, err := os.Stat(c.Kernel); os.IsNotExist(err) {
-		fmt.Fprintf(os.Stderr, "error: %v: %v\n", c.Kernel, err)
-		os.Exit(1)
+		log.Fatalf("error: %v: %v\n", c.Kernel, err)
 	}
 
 	if _, err := os.Stat(c.Boot); os.IsNotExist(err) {
-		fmt.Fprintf(os.Stderr, "error: %v: %v\n", c.Boot, err)
-		os.Exit(1)
+		log.Fatalf("error: %v: %v\n", c.Boot, err)
 	}
 
 	if c.NameServer == "" {
diff --git a/cmd/program.go b/cmd/program.go
index 4dd33b5..d945af4 100644
--- a/cmd/program.go
+++ b/cmd/program.go
@@ -1,11 +1,11 @@
 package cmd
 
 import (
-	"fmt"
 	"os"
 	"path"
 
 	api "github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 )
 
 func checkProgramExists(program string) {
@@ -13,7 +13,6 @@ func checkProgramExists(program string) {
 	_, err1 := os.Stat(program)
 
 	if os.IsNotExist(err) && os.IsNotExist(err1) {
-		fmt.Fprintf(os.Stderr, "error: %v: %v\n", program, err)
-		os.Exit(1)
+		log.Fatalf("error: %v: %v\n", program, err)
 	}
 }
diff --git a/cmd/subtract_date_notation.go b/cmd/subtract_date_notation.go
index 02efd1c..f0e7ce3 100644
--- a/cmd/subtract_date_notation.go
+++ b/cmd/subtract_date_notation.go
@@ -1,12 +1,12 @@
 package cmd
 
 import (
-	"fmt"
 	"regexp"
 	"strconv"
 	"time"
 
 	"github.com/go-errors/errors"
+	"github.com/nanovms/ops/log"
 )
 
 // SubtractTimeNotation subtracts time notation timestamp from date passed by argument
@@ -19,7 +19,7 @@ func SubtractTimeNotation(date time.Time, notation string) (newDate time.Time, e
 
 	n, err := strconv.Atoi(groups[1])
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return newDate, err
 	}
 
diff --git a/digitalocean/digital_ocean_image.go b/digitalocean/digital_ocean_image.go
index c9af612..ec938dd 100644
--- a/digitalocean/digital_ocean_image.go
+++ b/digitalocean/digital_ocean_image.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/digitalocean/godo"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 )
@@ -114,7 +115,7 @@ func (do *DigitalOcean) DeleteImage(ctx *lepton.Context, imagename string) error
 
 // SyncImage syncs image from provider to another provider
 func (do *DigitalOcean) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/digitalocean/digital_ocean_store.go b/digitalocean/digital_ocean_store.go
index 269f37d..ec13825 100644
--- a/digitalocean/digital_ocean_store.go
+++ b/digitalocean/digital_ocean_store.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/minio/minio-go"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -24,8 +25,7 @@ func (s *Spaces) getSignedURL(key string, bucket string, region string) string {
 
 	client, err := minio.New(endpoint, accessKey, secKey, ssl)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	reqParams := make(url.Values)
@@ -33,7 +33,7 @@ func (s *Spaces) getSignedURL(key string, bucket string, region string) string {
 	presignedURL, err := client.PresignedGetObject(bucket, key, time.Second*5*60, reqParams)
 
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return ""
 	}
 
@@ -55,14 +55,12 @@ func (s *Spaces) CopyToBucket(config *types.Config, archPath string) error {
 
 	client, err := s.getMinioClient(config)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	stat, err := file.Stat()
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	bucket := config.CloudConfig.BucketName
@@ -70,13 +68,12 @@ func (s *Spaces) CopyToBucket(config *types.Config, archPath string) error {
 
 	n, err := client.PutObject(bucket, key, file, stat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
-	fmt.Println("Uploaded", "my-objectname", " of size: ", n, "Successfully.")
+	log.Info("Uploaded", "my-objectname", " of size: ", n, "Successfully.")
 
-	fmt.Printf("Successfully uploaded %q to %q\n", config.CloudConfig.ImageName, bucket)
+	log.Info("Successfully uploaded %q to %q\n", config.CloudConfig.ImageName, bucket)
 
 	policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::ops/` + key + `"],"Sid": ""}]}`
 
@@ -94,13 +91,12 @@ func (s *Spaces) DeleteFromBucket(config *types.Config, key string) error {
 
 	client, err := s.getMinioClient(config)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	err = client.RemoveObject(bucket, key)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
diff --git a/fs/manifest.go b/fs/manifest.go
index 5faca13..300b263 100644
--- a/fs/manifest.go
+++ b/fs/manifest.go
@@ -8,6 +8,8 @@ import (
 	"reflect"
 	"strconv"
 	"strings"
+
+	"github.com/nanovms/ops/log"
 )
 
 // link refers to a link filetype
@@ -51,17 +53,21 @@ func (m *Manifest) AddNetworkConfig(networkConfig *ManifestNetworkConfig) {
 }
 
 // AddUserProgram adds user program
-func (m *Manifest) AddUserProgram(imgpath string) {
+func (m *Manifest) AddUserProgram(imgpath string) (err error) {
 	parts := strings.Split(imgpath, "/")
 	if parts[0] == "." {
 		parts = parts[1:]
 	}
 	program := path.Join("/", path.Join(parts...))
-	err := m.AddFile(program, imgpath)
+
+	err = m.AddFile(program, imgpath)
 	if err != nil {
-		panic(err)
+		return
 	}
+
 	m.SetProgram(program)
+
+	return
 }
 
 // SetProgram sets user program
@@ -142,7 +148,11 @@ func (m *Manifest) AddKernel(path string) {
 }
 
 // AddDirectory adds all files in dir to image
-func (m *Manifest) AddDirectory(dir string) error {
+func (m *Manifest) AddDirectory(dir string, workDir string) error {
+	if err := os.Chdir(workDir); err != nil {
+		return err
+	}
+
 	err := filepath.Walk(dir, func(hostpath string, info os.FileInfo, err error) error {
 		if err != nil {
 			return err
@@ -182,7 +192,7 @@ func (m *Manifest) AddDirectory(dir string) error {
 				}
 				if reflect.TypeOf(node[parts[i]]).Kind() == reflect.String {
 					err := fmt.Errorf("directory %s is conflicting with an existing file", hostpath)
-					fmt.Println(err)
+					log.Error(err)
 					return err
 				}
 				node = node[parts[i]].(map[string]interface{})
@@ -234,7 +244,7 @@ func (m *Manifest) AddRelativeDirectory(src string) error {
 				}
 				if reflect.TypeOf(node[parts[i]]).Kind() == reflect.String {
 					err := fmt.Errorf("directory %s is conflicting with an existing file", hostpath)
-					fmt.Println(err)
+					log.Error(err)
 					return err
 				}
 				node = node[parts[i]].(map[string]interface{})
@@ -282,7 +292,7 @@ func (m *Manifest) AddLink(filepath string, hostpath string) error {
 	pathtest := node[parts[len(parts)-1]]
 	if pathtest != nil && reflect.TypeOf(pathtest).Kind() != reflect.String {
 		err := fmt.Errorf("file %s overriding an existing directory", filepath)
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -300,8 +310,7 @@ func (m *Manifest) AddLink(filepath string, hostpath string) error {
 
 	s, err := os.Readlink(hostpath)
 	if err != nil {
-		fmt.Println("bad link")
-		os.Exit(1)
+		log.Fatalf("bad link")
 	}
 
 	node[parts[len(parts)-1]] = link{path: s}
@@ -328,8 +337,7 @@ func (m *Manifest) AddFileTo(dir map[string]interface{}, filepath string, hostpa
 	pathtest := node[parts[len(parts)-1]]
 	if pathtest != nil && reflect.TypeOf(pathtest).Kind() != reflect.String {
 		err := fmt.Errorf("file '%s' overriding an existing directory", filepath)
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	if pathtest != nil && reflect.TypeOf(pathtest).Kind() == reflect.String && pathtest != hostpath {
diff --git a/gcp/gcp_image.go b/gcp/gcp_image.go
index e73a69f..faf59fa 100644
--- a/gcp/gcp_image.go
+++ b/gcp/gcp_image.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 	"golang.org/x/oauth2/google"
@@ -199,7 +200,7 @@ func (p *GCloud) DeleteImage(ctx *lepton.Context, imagename string) error {
 
 // SyncImage syncs image from provider to another provider
 func (p *GCloud) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/gcp/gcp_instance.go b/gcp/gcp_instance.go
index 51f952e..23c6beb 100644
--- a/gcp/gcp_instance.go
+++ b/gcp/gcp_instance.go
@@ -82,7 +82,7 @@ func (p *GCloud) CreateInstance(ctx *lepton.Context) error {
 	if c.CloudConfig.DomainName != "" {
 		instance, err := p.Service.Instances.Get(c.CloudConfig.ProjectID, c.CloudConfig.Zone, instanceName).Do()
 		if err != nil {
-			ctx.Logger().Error("failed getting instance")
+			ctx.Logger().Errorf("failed getting instance")
 			return err
 		}
 
@@ -104,7 +104,7 @@ func (p *GCloud) CreateInstance(ctx *lepton.Context) error {
 		_, err = p.Service.Firewalls.Insert(c.CloudConfig.ProjectID, rule).Context(context.TODO()).Do()
 
 		if err != nil {
-			ctx.Logger().Error("%v", err)
+			ctx.Logger().Errorf("%v", err)
 			return errors.New("Failed to add Firewall rule")
 		}
 	}
@@ -115,7 +115,7 @@ func (p *GCloud) CreateInstance(ctx *lepton.Context) error {
 		_, err = p.Service.Firewalls.Insert(c.CloudConfig.ProjectID, rule).Context(context.TODO()).Do()
 
 		if err != nil {
-			ctx.Logger().Error("%v", err)
+			ctx.Logger().Errorf("%v", err)
 			return errors.New("Failed to add Firewall rule")
 		}
 	}
diff --git a/gcp/gcp_network.go b/gcp/gcp_network.go
index 58c0c34..81003a7 100644
--- a/gcp/gcp_network.go
+++ b/gcp/gcp_network.go
@@ -48,14 +48,14 @@ func (p *GCloud) findOrCreateVPC(ctx *lepton.Context, computeService *compute.Se
 		ctx.Logger().Info("Creating vpc with name %s", vpcName)
 		network, err = p.CreateVPC(computeService, c.CloudConfig.ProjectID, vpcName)
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			err = fmt.Errorf("failed creating vpc %s", vpcName)
 			return
 		}
 		ctx.Logger().Info("vpc created")
 
 	} else if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		err = fmt.Errorf("failed getting vpc %s", vpcName)
 	}
 
@@ -126,7 +126,7 @@ func (p *GCloud) getNIC(ctx *lepton.Context, computeService *compute.Service) (n
 				err = fmt.Errorf("make sure you have subnet \"%s\" under vpc \"%s\" in region \"%s\"", subnetName, vpcName, region)
 				return
 			} else if err != nil {
-				ctx.Logger().Error(err.Error())
+				ctx.Logger().Error(err)
 				err = fmt.Errorf("failed getting subnet %s", subnetName)
 				return
 			}
diff --git a/gcp/gcp_store.go b/gcp/gcp_store.go
index 92d4b3b..db7135d 100644
--- a/gcp/gcp_store.go
+++ b/gcp/gcp_store.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 
 	storage "cloud.google.com/go/storage"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -20,9 +21,8 @@ func (s *Storage) CopyToBucket(config *types.Config, archPath string) error {
 	ctx := context.Background()
 	client, err := storage.NewClient(ctx)
 	if err != nil {
-		fmt.Println(err)
-		fmt.Println("Have you set GOOGLE_APPLICATION_CREDENTIALS?")
-		os.Exit(1)
+		log.Error(err)
+		log.Fatalf("Have you set GOOGLE_APPLICATION_CREDENTIALS?")
 	}
 
 	defer client.Close()
@@ -31,12 +31,12 @@ func (s *Storage) CopyToBucket(config *types.Config, archPath string) error {
 	_, err = bucket.Attrs(ctx)
 	if err != nil {
 		// Creates the new bucket.
-		fmt.Println("creating bucket:", config.CloudConfig.BucketName)
+		log.Info("creating bucket:", config.CloudConfig.BucketName)
 		if err := bucket.Create(ctx, config.CloudConfig.ProjectID, nil); err != nil {
 			return fmt.Errorf("failed to create bucket: %+v", err)
 		}
 	} else {
-		fmt.Println("bucket found:", config.CloudConfig.BucketName)
+		log.Info("bucket found:", config.CloudConfig.BucketName)
 	}
 
 	wr := bucket.Object(filepath.Base(archPath)).NewWriter(ctx)
diff --git a/go.sum b/go.sum
index a9d5aa5..9aaa550 100644
--- a/go.sum
+++ b/go.sum
@@ -19,12 +19,15 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
 cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
 cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
@@ -32,7 +35,9 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+git.sr.ht/~yoink00/goflenfig v0.1.0 h1:8RqL7olbs64EHus97F+S1wuPKFco9Av2FmY3Sk6bPoQ=
 git.sr.ht/~yoink00/goflenfig v0.1.0/go.mod h1:+b5SR2TmdQHQ2WqgotlpJEN0xjijoDz6V3pzyfyc6v4=
 github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY=
 github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
@@ -77,28 +82,42 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
 github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/UpCloudLtd/upcloud-go-api v0.0.0-20210127073406-2964ed7e5972 h1:OUrt9phz41jCKOgqu3Iq8KH+5OfyfgcdFT28R9QLN0Y=
 github.com/UpCloudLtd/upcloud-go-api v0.0.0-20210127073406-2964ed7e5972/go.mod h1:nKv1Y0cTJTGYSd3lEcFfEnbPPiIj3gT4lJzV0ZfTlpQ=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/aws/aws-sdk-go v1.35.20 h1:Hs7x9Czh+MMPnZLQqHhsuZKeNFA3Vuf7pdy2r5QlVb0=
 github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
 github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 h1:qg9VbHo1TlL0KDM0vYvBG9EY0X0Yku5WYIPoFWt8f6o=
 github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@@ -110,15 +129,20 @@ github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-ini/ini v1.52.0 h1:3UeUAveYUTCYV/G0jNDiIrrtIeAl1oAjshYyU2PaAlQ=
 github.com/go-ini/ini v1.52.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
@@ -155,6 +179,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
@@ -177,7 +202,9 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v0.0.0-20170306145142-6a5e28554805 h1:skl44gU1qEIcRpwKjb9bhlRwjvr96wLdvpTogCBBJe8=
 github.com/google/uuid v0.0.0-20170306145142-6a5e28554805/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -195,7 +222,9 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -208,17 +237,23 @@ github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfE
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
 github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d h1:oNAwILwmgWKFpuU+dXvI6dl9jG2mAWAZLX3r9s0PPiw=
 github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -230,24 +265,30 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
 github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
 github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
 github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/schollz/progressbar/v2 v2.13.2 h1:3L9bP5KQOGEnFP8P5V8dz+U0yo5I29iY5Oa9s9EAwn0=
 github.com/schollz/progressbar/v2 v2.13.2/go.mod h1:6YZjqdthH6SCZKv2rqGryrxPtfmRB/DWZxSMfCXPyD8=
@@ -260,12 +301,15 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
 github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -282,13 +326,17 @@ github.com/terra-farm/go-virtualbox v0.0.4 h1:UAEVNAYLA3g80Z//fMZFVSWHEPqRWkWP2g
 github.com/terra-farm/go-virtualbox v0.0.4/go.mod h1:5n2X+HKR2eAzHfuGFnrZlCrgiYrseNHIcNTSpA/ViyU=
 github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
 github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/vmware/govmomi v0.22.2 h1:hmLv4f+RMTTseqtJRijjOWzwELiaLMIoHv2D6H3bF4I=
 github.com/vmware/govmomi v0.22.2/go.mod h1:Y+Wq4lst78L85Ge/F8+ORXIWiKYqaro1vhAulACy9Lc=
+github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728 h1:sH9mEk+flyDxiUa5BuPiuhDETMbzrt9A20I2wktMvRQ=
 github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
@@ -322,8 +370,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -338,6 +388,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -394,6 +445,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -444,6 +496,7 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -581,6 +634,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
 gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -601,7 +655,11 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/hyperv/hyperv_instance.go b/hyperv/hyperv_instance.go
index 6928be8..21430ba 100644
--- a/hyperv/hyperv_instance.go
+++ b/hyperv/hyperv_instance.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/wsl"
 	"github.com/olekukonko/tablewriter"
 )
@@ -142,7 +143,7 @@ func (p *Provider) GetInstances(ctx *lepton.Context) (instances []lepton.CloudIn
 
 			date, err := time.Parse("02/01/2006 15:04:05", cols[2]+" "+cols[3])
 			if err != nil {
-				fmt.Println(err)
+				log.Error(err)
 				continue
 			}
 
diff --git a/hyperv/powershell.go b/hyperv/powershell.go
index 2db89e6..a9e1604 100644
--- a/hyperv/powershell.go
+++ b/hyperv/powershell.go
@@ -115,7 +115,7 @@ func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
 	powershellAvailable, path, err := IsPowershellAvailable()
 
 	if !powershellAvailable {
-		log.Fatal("Cannot find PowerShell in the path")
+		log.Fatalf("Cannot find PowerShell in the path")
 		return "", err
 	}
 
diff --git a/lepton/config.go b/lepton/config.go
index 14f8ad3..8c85182 100644
--- a/lepton/config.go
+++ b/lepton/config.go
@@ -2,11 +2,11 @@ package lepton
 
 import (
 	"encoding/json"
-	"fmt"
 	"io/ioutil"
 	"os"
 	"os/user"
 
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -18,13 +18,11 @@ func NewConfig() *types.Config {
 	if conf != "" {
 		data, err := ioutil.ReadFile(conf)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "error reading config: %v\n", err)
-			os.Exit(1)
+			log.Errorf("error reading config: %v\n", err)
 		}
 		err = json.Unmarshal(data, &c)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "error config: %v\n", err)
-			os.Exit(1)
+			log.Errorf("error config: %v\n", err)
 		}
 	} else {
 		usr, err := user.Current()
@@ -36,13 +34,11 @@ func NewConfig() *types.Config {
 		if _, err = os.Stat(conf); err == nil {
 			data, err := ioutil.ReadFile(conf)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "error reading config: %v\n", err)
-				os.Exit(1)
+				log.Fatalf("error reading config: %v\n", err)
 			}
 			err = json.Unmarshal(data, &c)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "error config: %v\n", err)
-				os.Exit(1)
+				log.Fatalf("error config: %v\n", err)
 			}
 		}
 	}
@@ -50,6 +46,7 @@ func NewConfig() *types.Config {
 	c.RunConfig.Accel = true
 	c.RunConfig.Memory = "2G"
 	c.VolumesDir = LocalVolumeDir
+	c.LocalFilesParentDirectory = "."
 
 	return c
 }
diff --git a/lepton/const.go b/lepton/const.go
index c3b7dc0..168d580 100644
--- a/lepton/const.go
+++ b/lepton/const.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/constants"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -87,7 +88,7 @@ func getImageTempDir(c *types.Config) string {
 	if c.BuildDir == "" {
 		dir, err := ioutil.TempDir("", temp)
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 
 		c.BuildDir = dir
@@ -168,8 +169,7 @@ func getLatestRelVersion() string {
 	if err != nil {
 		fmt.Printf(constants.WarningColor, "version lookup failed, using local.\n")
 		if LocalReleaseVersion == "0.0" {
-			fmt.Printf(constants.ErrorColor, "No local build found.")
-			os.Exit(1)
+			log.Fatalf(constants.ErrorColor, "No local build found.")
 		}
 		return LocalReleaseVersion
 	}
diff --git a/lepton/image.go b/lepton/image.go
index 13f0300..8860822 100644
--- a/lepton/image.go
+++ b/lepton/image.go
@@ -16,6 +16,7 @@ import (
 
 	"github.com/go-errors/errors"
 	"github.com/nanovms/ops/fs"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -233,7 +234,9 @@ func setManifestFromConfig(m *fs.Manifest, c *types.Config) error {
 	m.AddKlibs(c.RunConfig.Klibs)
 
 	for _, f := range c.Files {
-		err := m.AddFile(f, f)
+		hostPath := path.Join(c.LocalFilesParentDirectory, f)
+		filePath := f
+		err := m.AddFile(filePath, hostPath)
 		if err != nil {
 			return err
 		}
@@ -247,7 +250,7 @@ func setManifestFromConfig(m *fs.Manifest, c *types.Config) error {
 	}
 
 	for _, d := range c.Dirs {
-		err := m.AddDirectory(d)
+		err := m.AddDirectory(d, c.LocalFilesParentDirectory)
 		if err != nil {
 			return err
 		}
@@ -311,8 +314,12 @@ func BuildManifest(c *types.Config) (*fs.Manifest, error) {
 
 	addCommonFilesToManifest(m)
 
-	m.AddUserProgram(c.Program)
-	err := setManifestFromConfig(m, c)
+	err := m.AddUserProgram(c.Program)
+	if err != nil {
+		return nil, err
+	}
+
+	err = setManifestFromConfig(m, c)
 	if err != nil {
 		return nil, errors.Wrap(err, 1)
 	}
@@ -513,7 +520,7 @@ func DownloadFileWithProgress(filepath string, url string, timeout int) error {
 
 // DownloadFile downloads file using URL
 func DownloadFile(filepath string, url string, timeout int, showProgress bool) error {
-	fmt.Println("Downloading..", url)
+	log.Info("Downloading..", url)
 	out, err := os.Create(filepath + ".tmp")
 	if err != nil {
 		return err
diff --git a/lepton/ldd_darwin.go b/lepton/ldd_darwin.go
index 467c721..0e732ca 100644
--- a/lepton/ldd_darwin.go
+++ b/lepton/ldd_darwin.go
@@ -2,7 +2,6 @@ package lepton
 
 import (
 	"debug/elf"
-	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
@@ -10,6 +9,7 @@ import (
 	"github.com/go-errors/errors"
 	"github.com/nanovms/ops/constants"
 	"github.com/nanovms/ops/fs"
+	"github.com/nanovms/ops/log"
 )
 
 // GetElfFileInfo returns an object with elf information of the path program
@@ -81,8 +81,7 @@ func _getSharedLibs(targetRoot string, path string) ([]string, error) {
 	fd, err := elf.Open(path)
 	if err != nil {
 		if strings.Contains(err.Error(), "bad magic number") {
-			fmt.Printf(constants.ErrorColor, "Only ELF binaries are supported. Is thia a Mach-0 (osx) binary? run 'file "+path+"' on it\n")
-			os.Exit(1)
+			log.Fatalf(constants.ErrorColor, "Only ELF binaries are supported. Is thia a Mach-0 (osx) binary? run 'file "+path+"' on it\n")
 		}
 		return nil, errors.WrapPrefix(err, path, 0)
 	}
diff --git a/lepton/ldd_linux.go b/lepton/ldd_linux.go
index 03cc96a..2e77e4c 100644
--- a/lepton/ldd_linux.go
+++ b/lepton/ldd_linux.go
@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"debug/elf"
 	"fmt"
+	"log"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -61,8 +62,7 @@ func getSharedLibs(targetRoot string, path string) ([]string, error) {
 		return nil, errors.WrapPrefix(err, path, 0)
 	}
 	if !isELF {
-		fmt.Printf(constants.ErrorColor, "Only ELF binaries are supported. Is thia a Linux binary? run 'file "+path+"' on it\n")
-		os.Exit(1)
+		log.Fatalf(constants.ErrorColor, "Only ELF binaries are supported. Is thia a Linux binary? run 'file "+path+"' on it\n")
 	}
 
 	if _, err := os.Stat(path); err != nil {
diff --git a/lepton/package.go b/lepton/package.go
index 1be9bd3..91060fa 100644
--- a/lepton/package.go
+++ b/lepton/package.go
@@ -18,6 +18,7 @@ import (
 
 	"github.com/go-errors/errors"
 	"github.com/nanovms/ops/constants"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -149,15 +150,13 @@ func PackageManifestChanged(fino os.FileInfo, remoteURL string) bool {
 func sha256Of(filename string) string {
 	f, err := os.Open(filename)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 	defer f.Close()
 
 	h := sha256.New()
 	if _, err := io.Copy(h, f); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	return fmt.Sprintf("%x", h.Sum(nil))
@@ -183,8 +182,7 @@ func ExtractPackage(archive string, dest string) {
 		}
 
 		if (*list)[fname].SHA256 != sha {
-			fmt.Println("This package doesn't match what is in the manifest.")
-			os.Exit(1)
+			log.Fatalf("This package doesn't match what is in the manifest.")
 		}
 
 	}
diff --git a/lepton/provider.go b/lepton/provider.go
index 36ef863..7acc0ec 100644
--- a/lepton/provider.go
+++ b/lepton/provider.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -116,7 +117,7 @@ func CreateDNSRecord(config *types.Config, aRecordIP string, dnsService DNSServi
 // Context captures required info for provider operation
 type Context struct {
 	config *types.Config
-	logger *Logger
+	logger *log.Logger
 }
 
 // Config returns context configuration
@@ -125,7 +126,7 @@ func (c Context) Config() *types.Config {
 }
 
 // Logger returns logger
-func (c Context) Logger() *Logger {
+func (c Context) Logger() *log.Logger {
 	return c.logger
 }
 
@@ -133,7 +134,7 @@ func (c Context) Logger() *Logger {
 // valid providers are "gcp", "aws" and "onprem"
 func NewContext(c *types.Config) *Context {
 
-	logger := NewLogger(os.Stdout)
+	logger := log.New(os.Stdout)
 
 	if c.RunConfig.ShowDebug {
 		logger.SetDebug(true)
diff --git a/lepton/utils.go b/lepton/utils.go
index 91233dd..67afa59 100644
--- a/lepton/utils.go
+++ b/lepton/utils.go
@@ -2,7 +2,7 @@ package lepton
 
 import (
 	"fmt"
-	"os"
+	"log"
 	"strconv"
 	"unicode/utf8"
 
@@ -10,8 +10,7 @@ import (
 )
 
 func exitWithError(errs string) {
-	fmt.Println(fmt.Sprintf(constants.ErrorColor, errs))
-	os.Exit(1)
+	log.Fatal(fmt.Sprintf(constants.ErrorColor, errs))
 }
 
 // isDomainValid returns an error if the domain name is not valid
diff --git a/lepton/volume.go b/lepton/volume.go
index 2ea20fc..6138f05 100644
--- a/lepton/volume.go
+++ b/lepton/volume.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/go-errors/errors"
 	"github.com/nanovms/ops/fs"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 )
@@ -111,18 +112,21 @@ func symlinkVolume(dir, name, uuid string) error {
 	if err == nil {
 		err := os.Remove(dst)
 		if err != nil {
-			fmt.Println(msg)
+			log.Errorf(msg)
+			log.Error(err)
 			return err
 		}
 	}
 	if err != nil && !os.IsNotExist(err) {
-		fmt.Println(msg)
+		log.Errorf(msg)
+		log.Error(err)
 		return err
 	}
 
 	err = os.Symlink(src, dst)
 	if err != nil {
-		fmt.Println(msg)
+		log.Errorf(msg)
+		log.Error(err)
 		return err
 	}
 	return nil
diff --git a/lepton/console_colors.go b/log/console_colors.go
similarity index 98%
rename from lepton/console_colors.go
rename to log/console_colors.go
index fa46e89..5444dbb 100644
--- a/lepton/console_colors.go
+++ b/log/console_colors.go
@@ -1,4 +1,4 @@
-package lepton
+package log
 
 // ConsoleColorsType has a set of methods that return the color directive to change console color
 type ConsoleColorsType struct{}
diff --git a/lepton/logger.go b/log/logger.go
similarity index 78%
rename from lepton/logger.go
rename to log/logger.go
index 9aa4b07..098c0ec 100644
--- a/lepton/logger.go
+++ b/log/logger.go
@@ -1,4 +1,4 @@
-package lepton
+package log
 
 import (
 	"fmt"
@@ -14,8 +14,8 @@ type Logger struct {
 	debug  bool
 }
 
-// NewLogger returns an instance of Logger
-func NewLogger(output io.Writer) *Logger {
+// New returns an instance of Logger
+func New(output io.Writer) *Logger {
 	return &Logger{output, false, false, false, false}
 }
 
@@ -58,13 +58,20 @@ func (l *Logger) Warn(message string, a ...interface{}) {
 	}
 }
 
-// Error checks error level is activated to write the message
-func (l *Logger) Error(message string, a ...interface{}) {
+// Errorf checks error level is activated to write the formatted message
+func (l *Logger) Errorf(message string, a ...interface{}) {
 	if l.err == true {
 		l.Log(ConsoleColors.Red()+message+ConsoleColors.White(), a...)
 	}
 }
 
+// Error checks error level is activated to write error object
+func (l *Logger) Error(err error) {
+	if l.err == true {
+		l.Log(ConsoleColors.Red() + err.Error() + ConsoleColors.White())
+	}
+}
+
 // Debug checks debug level is activated to write the message
 func (l *Logger) Debug(message string, a ...interface{}) {
 	if l.debug == true {
diff --git a/log/logger_default.go b/log/logger_default.go
new file mode 100644
index 0000000..a29b3d1
--- /dev/null
+++ b/log/logger_default.go
@@ -0,0 +1,93 @@
+package log
+
+import (
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/nanovms/ops/types"
+)
+
+var defaultLogger *Logger
+
+// Make sure default logger instantiated by default.
+func init() {
+	defaultLogger = New(os.Stdout)
+}
+
+// InitDefault creates default logger for package-level logging access.
+func InitDefault(output io.Writer, config *types.Config) {
+	defaultLogger = New(output)
+
+	if config == nil {
+		return
+	}
+
+	if config.RunConfig.ShowDebug {
+		defaultLogger.SetDebug(true)
+		defaultLogger.SetWarn(true)
+		defaultLogger.SetError(true)
+		defaultLogger.SetInfo(true)
+	}
+
+	if config.RunConfig.ShowWarnings {
+		defaultLogger.SetWarn(true)
+	}
+
+	if config.RunConfig.ShowErrors {
+		defaultLogger.SetError(true)
+	}
+
+	if config.RunConfig.Verbose {
+		defaultLogger.SetInfo(true)
+	}
+}
+
+// Info logs info-level message using default logger.
+func Info(message string, a ...interface{}) {
+	defaultLogger.Info(message, a...)
+}
+
+// Warn logs warning-level message using default logger.
+func Warn(message string, a ...interface{}) {
+	defaultLogger.Warn(message, a...)
+}
+
+// Errorf logs error-level formatted string message using default logger.
+func Errorf(message string, a ...interface{}) {
+	defaultLogger.Errorf(message, a...)
+}
+
+// Error logs error-level message using default logger.
+func Error(err error) {
+	defaultLogger.Error(err)
+}
+
+// Fatalf logs error-level formatted string message using default logger then calls os.Exit(1).
+func Fatalf(message string, a ...interface{}) {
+	defaultLogger.Errorf(message, a...)
+	os.Exit(1)
+}
+
+// Fatal logs error-level message using default logger then calls os.Exit(1).
+func Fatal(err error) {
+	defaultLogger.Error(err)
+	os.Exit(1)
+}
+
+// Panicf logs error-level formatted string message using default logger then calls panic().
+func Panicf(message string, a ...interface{}) {
+	defaultLogger.Errorf(message, a...)
+	panic(fmt.Sprintf(message+"\n", a...))
+}
+
+// Panic logs error-level message using default logger then calls panic().
+func Panic(err error) {
+	defaultLogger.Error(err)
+	panic(err)
+}
+
+// Debug logs debug-level message using default logger.
+func Debug(message string, a ...interface{}) {
+	defaultLogger.Debug(message, a...)
+}
diff --git a/oci/oci_image.go b/oci/oci_image.go
index 52c3af3..8c967f1 100644
--- a/oci/oci_image.go
+++ b/oci/oci_image.go
@@ -113,13 +113,13 @@ func (p *Provider) CreateImage(ctx *lepton.Context, imagePath string) (err error
 
 	image, err := p.fileSystem.Open(imagePath)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return fmt.Errorf("failed reading file %s", imagePath)
 	}
 
 	imageStats, err := image.Stat()
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return fmt.Errorf("failed getting file stats of %s", imagePath)
 	}
 
@@ -133,7 +133,7 @@ func (p *Provider) CreateImage(ctx *lepton.Context, imagePath string) (err error
 		PutObjectBody: image,
 	})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed uploading image")
 	}
 
@@ -152,7 +152,7 @@ func (p *Provider) CreateImage(ctx *lepton.Context, imagePath string) (err error
 		},
 	})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed importing image from storage")
 	}
 
@@ -221,7 +221,7 @@ func (p *Provider) GetImages(ctx *lepton.Context) (images []lepton.CloudImage, e
 
 	imagesList, err := p.computeClient.ListImages(context.TODO(), core.ListImagesRequest{CompartmentId: types.StringPtr(p.compartmentID)})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return nil, errors.New("failed getting images")
 	}
 
diff --git a/oci/oci_instance.go b/oci/oci_instance.go
index c108fde..a121376 100644
--- a/oci/oci_instance.go
+++ b/oci/oci_instance.go
@@ -29,13 +29,13 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) error {
 
 	subnet, err := p.GetSubnet(ctx)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed getting subnet")
 	}
 
 	sg, err := p.CreateNetworkSecurityGroup(ctx, *subnet.VcnId)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed creating network security group")
 	}
 
@@ -67,7 +67,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) error {
 		},
 	})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed launching instance")
 	}
 
@@ -83,7 +83,7 @@ func (p *Provider) ListInstances(ctx *lepton.Context) (err error) {
 
 	err = p.AddInstancesNetworkDetails(ctx, &instances)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		err = errors.New("failed getting instances network details")
 		return
 	}
@@ -129,7 +129,7 @@ func (p *Provider) AddInstancesNetworkDetails(ctx *lepton.Context, instances *[]
 		instance := (*instances)[i]
 		vnicsAttachments, err := p.computeClient.ListVnicAttachments(context.TODO(), core.ListVnicAttachmentsRequest{CompartmentId: &p.compartmentID, InstanceId: &instance.ID})
 		if err != nil {
-			ctx.Logger().Error(err.Error())
+			ctx.Logger().Error(err)
 			responses <- errors.New("failed getting vnic attachments of instance " + instance.ID + "\n")
 			return
 		}
@@ -137,7 +137,7 @@ func (p *Provider) AddInstancesNetworkDetails(ctx *lepton.Context, instances *[]
 		for _, vnic := range vnicsAttachments.Items {
 			vnicDetails, err := p.networkClient.GetVnic(context.TODO(), core.GetVnicRequest{VnicId: vnic.VnicId})
 			if err != nil {
-				ctx.Logger().Error(err.Error())
+				ctx.Logger().Error(err)
 				responses <- errors.New("failed getting details of vnic " + *vnic.VnicId + "\n")
 				return
 			}
@@ -216,13 +216,13 @@ func (p *Provider) GetInstanceByID(ctx *lepton.Context, id string) (instance *le
 func (p *Provider) DeleteInstance(ctx *lepton.Context, instancename string) error {
 	instance, err := p.GetInstanceByID(ctx, instancename)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed getting instance")
 	}
 
 	_, err = p.computeClient.TerminateInstance(context.TODO(), core.TerminateInstanceRequest{InstanceId: &instance.ID})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed terminate instance")
 	}
 
@@ -233,13 +233,13 @@ func (p *Provider) DeleteInstance(ctx *lepton.Context, instancename string) erro
 func (p *Provider) StopInstance(ctx *lepton.Context, instancename string) error {
 	instance, err := p.GetInstanceByID(ctx, instancename)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed getting instance")
 	}
 
 	_, err = p.computeClient.InstanceAction(context.TODO(), core.InstanceActionRequest{Action: core.InstanceActionActionStop, InstanceId: &instance.ID})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed terminate instance")
 	}
 
@@ -250,13 +250,13 @@ func (p *Provider) StopInstance(ctx *lepton.Context, instancename string) error
 func (p *Provider) StartInstance(ctx *lepton.Context, instancename string) error {
 	instance, err := p.GetInstanceByID(ctx, instancename)
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed getting instance")
 	}
 
 	_, err = p.computeClient.InstanceAction(context.TODO(), core.InstanceActionRequest{Action: core.InstanceActionActionStart, InstanceId: &instance.ID})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		return errors.New("failed terminate instance")
 	}
 
diff --git a/oci/oci_network.go b/oci/oci_network.go
index 595757d..2cc55da 100644
--- a/oci/oci_network.go
+++ b/oci/oci_network.go
@@ -45,7 +45,7 @@ func (p *Provider) CreateNetworkSecurityGroup(ctx *lepton.Context, vcnID string)
 		},
 	})
 	if err != nil {
-		ctx.Logger().Error(err.Error())
+		ctx.Logger().Error(err)
 		err = errors.New("failed creating security group")
 		return
 	}
diff --git a/onprem/onprem_instance.go b/onprem/onprem_instance.go
index ef4878f..f3c77af 100644
--- a/onprem/onprem_instance.go
+++ b/onprem/onprem_instance.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/qemu"
 	"github.com/olekukonko/tablewriter"
 )
@@ -69,12 +70,12 @@ func (p *OnPrem) CreateInstance(ctx *lepton.Context) error {
 
 	d1, err := json.Marshal(i)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	err = ioutil.WriteFile(instances+"/"+pid, d1, 0644)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return nil
@@ -200,7 +201,7 @@ func (p *OnPrem) DeleteInstance(ctx *lepton.Context, instancename string) error
 
 	err = sysKill(pid)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	opshome := lepton.GetOpsHome()
@@ -228,8 +229,7 @@ func (p *OnPrem) GetInstanceLogs(ctx *lepton.Context, instancename string) (stri
 
 	body, err := ioutil.ReadFile("/tmp/" + instancename + ".log")
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	return string(body), nil
diff --git a/onprem/onprem_volume.go b/onprem/onprem_volume.go
index 837e9b0..6168483 100644
--- a/onprem/onprem_volume.go
+++ b/onprem/onprem_volume.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -68,7 +69,7 @@ func (op *OnPrem) DeleteVolume(ctx *lepton.Context, name string) error {
 // with the given volume label
 // label can refer to volume UUID or volume label
 func (op *OnPrem) AttachVolume(ctx *lepton.Context, image, name string) error {
-	fmt.Println("not implemented")
+	log.Warn("not implemented")
 	fmt.Println("use <ops run> or <ops pkg load> with --mounts flag instead")
 	fmt.Println("alternatively, use <ops image create -t onprem> with --mounts flag")
 	fmt.Println("and run it with <ops instance create -t onprem>")
@@ -77,7 +78,7 @@ func (op *OnPrem) AttachVolume(ctx *lepton.Context, image, name string) error {
 
 // DetachVolume detaches volume
 func (op *OnPrem) DetachVolume(ctx *lepton.Context, image, name string) error {
-	fmt.Println("not implemented")
+	log.Warn("not implemented")
 	return nil
 }
 
diff --git a/openstack/openstack.go b/openstack/openstack.go
index 7161058..ad99e7e 100644
--- a/openstack/openstack.go
+++ b/openstack/openstack.go
@@ -2,10 +2,10 @@ package openstack
 
 import (
 	"errors"
-	"fmt"
 	"os"
 
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 
 	"github.com/gophercloud/gophercloud"
@@ -41,7 +41,7 @@ func (o *OpenStack) Initialize(config *types.ProviderConfig) error {
 func (o *OpenStack) findFlavorByName(name string) (id string, err error) {
 	client, err := o.getComputeClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	listOpts := flavors.ListOpts{
diff --git a/openstack/openstack_image.go b/openstack/openstack_image.go
index 9f4c38f..1652b61 100644
--- a/openstack/openstack_image.go
+++ b/openstack/openstack_image.go
@@ -11,6 +11,7 @@ import (
 	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata"
 	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 )
@@ -47,7 +48,7 @@ func (o *OpenStack) findImage(name string) (id string, err error) {
 		Region: os.Getenv("OS_REGION_NAME"),
 	})
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	listOpts := images.ListOpts{
@@ -112,16 +113,16 @@ func (o *OpenStack) CreateImage(ctx *lepton.Context, imagePath string) error {
 
 	imgName = strings.ReplaceAll(imgName, "-image", "")
 
-	fmt.Println("creating image:\t" + imgName)
+	log.Info("creating image:\t" + imgName)
 
 	imagesClient, err := o.getImagesClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	image, err := o.createImage(imagesClient, imgName)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	imagePath = lepton.LocalImageDir + "/" + imgName + ".img"
@@ -141,7 +142,7 @@ func (o *OpenStack) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error)
 		Region: os.Getenv("OS_REGION_NAME"),
 	})
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	listOpts := images.ListOpts{
@@ -155,7 +156,7 @@ func (o *OpenStack) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error)
 
 	allImages, err := images.ExtractImages(allPages)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	for _, image := range allImages {
@@ -211,18 +212,18 @@ func (o *OpenStack) deleteImage(imagesClient *gophercloud.ServiceClient, imageID
 func (o *OpenStack) DeleteImage(ctx *lepton.Context, imagename string) error {
 	imageID, err := o.findImage(imagename)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
 	imageClient, err := o.getImagesClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	err = images.Delete(imageClient, imageID).ExtractErr()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -231,7 +232,7 @@ func (o *OpenStack) DeleteImage(ctx *lepton.Context, imagename string) error {
 
 // SyncImage syncs image from provider to another provider
 func (o *OpenStack) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/openstack/openstack_instance.go b/openstack/openstack_instance.go
index 11ec6fb..1262052 100644
--- a/openstack/openstack_instance.go
+++ b/openstack/openstack_instance.go
@@ -14,6 +14,7 @@ import (
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 	"github.com/gophercloud/gophercloud/pagination"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/olekukonko/tablewriter"
 )
 
@@ -24,7 +25,7 @@ func getOpenStackInstances(provider *gophercloud.ProviderClient, opts servers.Li
 		Region: os.Getenv("OS_REGION_NAME"),
 	})
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	pager := servers.List(client, opts)
@@ -32,7 +33,7 @@ func getOpenStackInstances(provider *gophercloud.ProviderClient, opts servers.Li
 	err = pager.EachPage(func(page pagination.Page) (bool, error) {
 		serverList, err := servers.ExtractServers(page)
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 			return false, err
 		}
 
@@ -87,14 +88,14 @@ func getOpenStackInstances(provider *gophercloud.ProviderClient, opts servers.Li
 func (o *OpenStack) CreateInstance(ctx *lepton.Context) error {
 	client, err := o.getComputeClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	imageName := ctx.Config().CloudConfig.ImageName
 
 	imageID, err := o.findImage(imageName)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -103,7 +104,7 @@ func (o *OpenStack) CreateInstance(ctx *lepton.Context) error {
 	flavorID, err := o.findFlavorByName(ctx.Config().CloudConfig.Flavor)
 
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	fmt.Printf("Deploying flavorID %s\n", flavorID)
@@ -279,17 +280,17 @@ func (o *OpenStack) DeleteInstance(ctx *lepton.Context, instancename string) err
 func (o *OpenStack) StartInstance(ctx *lepton.Context, instancename string) error {
 	client, err := o.getComputeClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	server, err := o.findInstance(instancename)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	err = startstop.Start(client, server.ID).ExtractErr()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return nil
@@ -299,17 +300,17 @@ func (o *OpenStack) StartInstance(ctx *lepton.Context, instancename string) erro
 func (o *OpenStack) StopInstance(ctx *lepton.Context, instancename string) error {
 	client, err := o.getComputeClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	server, err := o.findInstance(instancename)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	err = startstop.Stop(client, server.ID).ExtractErr()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return nil
@@ -320,7 +321,7 @@ func (o *OpenStack) findInstance(name string) (volume *servers.Server, err error
 
 	client, err := o.getComputeClient()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	opts := servers.ListOpts{}
@@ -330,7 +331,7 @@ func (o *OpenStack) findInstance(name string) (volume *servers.Server, err error
 	err = pager.EachPage(func(page pagination.Page) (bool, error) {
 		serverList, err := servers.ExtractServers(page)
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 			return false, err
 		}
 
diff --git a/openstack/openstack_volume.go b/openstack/openstack_volume.go
index c72b22e..3385018 100644
--- a/openstack/openstack_volume.go
+++ b/openstack/openstack_volume.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack"
@@ -76,7 +77,7 @@ func (o *OpenStack) CreateVolume(ctx *lepton.Context, name, data, size, provider
 		return vol, err
 	}
 
-	fmt.Println("creating volume...")
+	log.Info("creating volume...")
 	err = volumes.WaitForStatus(volumesClient, r.ID, "available", 60)
 	if err != nil {
 		return vol, err
diff --git a/qemu/qemu_unix.go b/qemu/qemu_unix.go
index ec7f199..1f9d396 100644
--- a/qemu/qemu_unix.go
+++ b/qemu/qemu_unix.go
@@ -18,7 +18,7 @@ import (
 
 	"golang.org/x/sys/unix"
 
-	"github.com/nanovms/ops/constants"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -39,7 +39,7 @@ func newQemu() Hypervisor {
 func (q *qemu) Stop() {
 	if q.cmd != nil {
 		if err := q.cmd.Process.Kill(); err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 
 		// do not print errors as the command could be started with Run()
@@ -49,7 +49,7 @@ func (q *qemu) Stop() {
 
 func logv(rconfig *types.RunConfig, msg string) {
 	if rconfig.Verbose {
-		fmt.Println(msg)
+		log.Info(msg)
 	}
 }
 
@@ -82,11 +82,11 @@ func (q *qemu) Start(rconfig *types.RunConfig) error {
 	if rconfig.Background {
 		err := q.cmd.Start()
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 	} else {
 		if err := q.cmd.Run(); err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 	}
 
@@ -232,13 +232,13 @@ func (q *qemu) setAccel(rconfig *types.RunConfig) {
 	if supportedErr != nil {
 		msg, terminate := qemuAccelWarningMessage(supportedErr)
 		if msg != "" {
-			fmt.Println(msg)
+			log.Warn(msg)
 		}
 		if terminate {
 			os.Exit(1)
 		}
 		if isAdded {
-			fmt.Printf(constants.WarningColor, "Anyway, we will try to enable hardware acceleration\n")
+			log.Warn("Anyway, we will try to enable hardware acceleration\n")
 		}
 	}
 }
@@ -326,9 +326,9 @@ func (q *qemu) setConfig(rconfig *types.RunConfig) {
 	} else {
 		q.addOption("-machine", "virt")
 
-		q.addOption("-machine", "gic-version=3")
+		q.addOption("-machine", "gic-version=2")
 		q.addOption("-machine", "highmem=off")
-		q.addOption("-kernel", "/home/ubuntu/.ops/0.1.31/kernel.img")
+		q.addOption("-kernel", "./kernel.img")
 
 		q.addOption("-device", "virtio-blk-pci,drive=hd0")
 
diff --git a/types/config.go b/types/config.go
index a132fc2..2cef5bd 100644
--- a/types/config.go
+++ b/types/config.go
@@ -81,6 +81,10 @@ type Config struct {
 	// RunConfig
 	RunConfig RunConfig
 
+	// LocalFilesParentDirectory is the parent directory of the files/directories specified in Files and Dirs
+	// The default value is the directory from where the ops command is running
+	LocalFilesParentDirectory string
+
 	// TargetRoot
 	TargetRoot string
 
diff --git a/vbox/vbox_instance.go b/vbox/vbox_instance.go
index 1c683fb..1c395c7 100644
--- a/vbox/vbox_instance.go
+++ b/vbox/vbox_instance.go
@@ -27,20 +27,20 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 
 	vmPath, err := findOrCreateVmsDir()
 	if err != nil {
-		ctx.Logger().Error("failed converting vms path")
+		ctx.Logger().Errorf("failed converting vms path")
 		return
 	}
 
 	vmPath = path.Join(vmPath, name)
 	err = os.Mkdir(vmPath, 0755)
 	if err != nil {
-		ctx.Logger().Error("failed creating vm directory")
+		ctx.Logger().Errorf("failed creating vm directory")
 		return
 	}
 
 	imagesDir, err := findOrCreateVdiImagesDir()
 	if err != nil {
-		ctx.Logger().Error("failed converting vdi images dir")
+		ctx.Logger().Errorf("failed converting vdi images dir")
 		return
 	}
 
@@ -51,13 +51,13 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 	if wsl.IsWSL() {
 		vmPath, err = wsl.ConvertPathFromWSLtoWindows(vmPath)
 		if err != nil {
-			ctx.Logger().Error("failed converting vm path")
+			ctx.Logger().Errorf("failed converting vm path")
 			return
 		}
 
 		imagePath, err = wsl.ConvertPathFromWSLtoWindows(imagePath)
 		if err != nil {
-			ctx.Logger().Error("failed converting image path %s", imagePath)
+			ctx.Logger().Errorf("failed converting image path %s", imagePath)
 			return
 		}
 
@@ -66,13 +66,13 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 
 	err = vbox.CloneHD(imagePath, vmImagePath)
 	if err != nil {
-		ctx.Logger().Error("failed cloning hdd")
+		ctx.Logger().Errorf("failed cloning hdd")
 		return
 	}
 
 	vm, err := vbox.CreateMachine(name, vmPath)
 	if err != nil {
-		ctx.Logger().Error("failed creating machine")
+		ctx.Logger().Errorf("failed creating machine")
 		return
 	}
 
@@ -84,7 +84,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 		Bootable:    true,
 	})
 	if err != nil {
-		ctx.Logger().Error("failed adding storage controller")
+		ctx.Logger().Errorf("failed adding storage controller")
 		return
 	}
 
@@ -95,7 +95,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 		Medium:    vmImagePath,
 	})
 	if err != nil {
-		ctx.Logger().Error("failed attaching storage")
+		ctx.Logger().Errorf("failed attaching storage")
 		return
 	}
 
@@ -103,13 +103,13 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 
 	err = modifyVM.Run()
 	if err != nil {
-		ctx.Logger().Error("failed changing vm memory")
+		ctx.Logger().Errorf("failed changing vm memory")
 		return
 	}
 
 	err = virtualbox.SetGuestProperty(vm.Name, "Image", imageName)
 	if err != nil {
-		ctx.Logger().Error("failed to set image as guest property")
+		ctx.Logger().Errorf("failed to set image as guest property")
 		return
 	}
 
@@ -118,13 +118,13 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 		Hardware: vbox.VirtIO,
 	})
 	if err != nil {
-		ctx.Logger().Error("failed setting NIC")
+		ctx.Logger().Errorf("failed setting NIC")
 		return
 	}
 
 	err = vm.Start()
 	if err != nil {
-		ctx.Logger().Error("failed started vm")
+		ctx.Logger().Errorf("failed started vm")
 		return
 	}
 
@@ -137,7 +137,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 				GuestPort: uint16(port),
 			})
 			if err != nil {
-				ctx.Logger().Error("failed creating port forward rule")
+				ctx.Logger().Errorf("failed creating port forward rule")
 				return err
 			}
 		}
@@ -147,7 +147,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) (err error) {
 	if len(ctx.Config().RunConfig.Ports) != 0 {
 		err = virtualbox.SetGuestProperty(vm.Name, "Ports", strings.Join(ctx.Config().RunConfig.Ports, ","))
 		if err != nil {
-			ctx.Logger().Error("failed to set ports as guest property")
+			ctx.Logger().Errorf("failed to set ports as guest property")
 			return
 		}
 	}
diff --git a/vsphere/vsphere_image.go b/vsphere/vsphere_image.go
index 5d73ace..55c5ce9 100644
--- a/vsphere/vsphere_image.go
+++ b/vsphere/vsphere_image.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 	"github.com/vmware/govmomi/find"
@@ -41,7 +42,7 @@ func (v *Vsphere) BuildImageWithPackage(ctx *lepton.Context, pkgpath string) (st
 }
 
 func (v *Vsphere) createImage(key string, bucket string, region string) {
-	fmt.Println("un-implemented")
+	log.Warn("un-implemented")
 }
 
 // CreateImage - Creates image on vsphere using nanos images
@@ -66,7 +67,7 @@ func (v *Vsphere) CreateImage(ctx *lepton.Context, imagePath string) error {
 	f := find.NewFinder(v.client, true)
 	ds, err := f.DatastoreOrDefault(context.TODO(), v.datastore)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -76,7 +77,7 @@ func (v *Vsphere) CreateImage(ctx *lepton.Context, imagePath string) error {
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -94,7 +95,7 @@ func (v *Vsphere) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error) {
 	f := find.NewFinder(v.client, true)
 	ds, err := f.DatastoreOrDefault(context.TODO(), v.datastore)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return nil, err
 	}
 
@@ -111,12 +112,12 @@ func (v *Vsphere) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error) {
 
 	task, err := search(context.TODO(), ds.Path(""), &spec)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	info, err := task.WaitForResult(context.TODO(), nil)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	switch r := info.Result.(type) {
@@ -133,7 +134,7 @@ func (v *Vsphere) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error) {
 			}
 		}
 	case vmwareTypes.ArrayOfHostDatastoreBrowserSearchResults:
-		fmt.Println("un-implemented")
+		log.Warn("un-implemented")
 	}
 
 	return cimages, nil
@@ -172,13 +173,13 @@ func (v *Vsphere) ListImages(ctx *lepton.Context) error {
 
 // DeleteImage deletes image from VSphere
 func (v *Vsphere) DeleteImage(ctx *lepton.Context, imagename string) error {
-	fmt.Println("un-implemented")
+	log.Warn("un-implemented")
 	return nil
 }
 
 // SyncImage syncs image from provider to another provider
 func (v *Vsphere) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/vsphere/vsphere_instance.go b/vsphere/vsphere_instance.go
index 2c7bc9e..3bd0b6b 100644
--- a/vsphere/vsphere_instance.go
+++ b/vsphere/vsphere_instance.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/olekukonko/tablewriter"
 	"github.com/vmware/govmomi/find"
 	"github.com/vmware/govmomi/govc/host/esxcli"
@@ -45,7 +46,7 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 	// add disk
 	scsi, err := devices.CreateSCSIController("pvscsi")
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	devices = append(devices, scsi)
@@ -53,13 +54,13 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 
 	dcontroller, err := devices.FindDiskController(controller)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	f := find.NewFinder(v.client, true)
 	ds, err := f.DatastoreOrDefault(context.TODO(), v.datastore)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -75,24 +76,24 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 	// infer network stub
 	net, err := f.NetworkOrDefault(context.TODO(), v.network)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	backing, err := net.EthernetCardBackingInfo(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	device, err := object.EthernetCardTypes().CreateEthernetCard("vmxnet3", backing)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	devices = append(devices, device)
 
 	deviceChange, err := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	spec.DeviceChange = deviceChange
@@ -103,13 +104,13 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
 	folders, err := dc.Folders(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	spec.Files = &types.VirtualMachineFileInfo{
@@ -120,14 +121,14 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 
 	pool, err := f.ResourcePoolOrDefault(context.TODO(), v.resourcePool)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		fmt.Println("Did you set the correct Resource Pool? https://nanovms.gitbook.io/ops/vsphere#create-instance ")
 		os.Exit(1)
 	}
 
 	task, err := folder.CreateVM(context.TODO(), *spec, pool, nil)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -135,7 +136,7 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 	if err != nil {
 		fmt.Printf("%+v", info)
 		fmt.Printf("%+v", info.Reason)
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -149,7 +150,7 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 	// add serial for logs
 	serial, err := devices.CreateSerialPort()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	err = vm.AddDevice(context.TODO(), serial)
@@ -179,7 +180,7 @@ func (v *Vsphere) CreateInstance(ctx *lepton.Context) error {
 
 	err = vm.EditDevice(context.TODO(), devices.ConnectSerialPort(d, uri, false, ""))
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return nil
@@ -303,7 +304,7 @@ func (v *Vsphere) ipFor(instancename string) string {
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	f.SetDatacenter(dc)
@@ -313,7 +314,7 @@ func (v *Vsphere) ipFor(instancename string) string {
 		if _, ok := err.(*find.NotFoundError); ok {
 			fmt.Println("can't find vm " + instancename)
 		}
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	var get func(*object.VirtualMachine) (string, error) = func(vm *object.VirtualMachine) (string, error) {
@@ -335,7 +336,7 @@ func (v *Vsphere) ipFor(instancename string) string {
 
 				ip, err := guest.IpAddress(vm)
 				if err != nil {
-					fmt.Println(err)
+					log.Error(err)
 					return "", err
 				}
 
@@ -351,7 +352,7 @@ func (v *Vsphere) ipFor(instancename string) string {
 
 	ip, err := get(vm)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return ip
@@ -361,14 +362,14 @@ func (v *Vsphere) findHostPath() string {
 	f := find.NewFinder(v.client, true)
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	f.SetDatacenter(dc)
 
 	host, err := f.DefaultHostSystem(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return host.InventoryPath
@@ -380,12 +381,12 @@ func (v *Vsphere) runCLI(args []string) (*esxcli.Response, error) {
 	hostPath := v.findHostPath()
 	host, err := f.HostSystemOrDefault(context.TODO(), hostPath)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	e, err := esxcli.NewExecutor(v.client, host)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return e.Run(args)
@@ -395,7 +396,7 @@ func (v *Vsphere) iphackEnabled() bool {
 	args := []string{"system", "settings", "advanced", "list", "-o", "/Net/GuestIPHack"}
 	res, err := v.runCLI(args)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	for _, val := range res.Values {
@@ -411,25 +412,23 @@ func (v *Vsphere) iphackEnabled() bool {
 
 func (v *Vsphere) setGuestIPHack() {
 	if v.iphackEnabled() {
-		fmt.Println("ip hack enabled")
+		log.Info("ip hack enabled")
 	} else {
-		fmt.Println("setting ip hack")
+		log.Info("setting ip hack")
 
 		args := []string{"system", "settings", "advanced", "set", "-o", "/Net/GuestIPHack", "-i", "1"}
 
 		res, err := v.runCLI(args)
 		if err != nil {
-			fmt.Println(err)
+			log.Error(err)
 		}
 
 		debug := false // FIXME: should have a debug log throughout OPS
 		if debug {
 			for _, val := range res.Values {
-				fmt.Println(val)
+				log.Debug(fmt.Sprint(val))
 			}
-
 		}
-
 	}
 
 	fmt.Println("IP hack has been enabled for all new ARP requests, however, for existing hosts the easiest way to trigger that is to simply reboot the vm.")
@@ -442,7 +441,7 @@ func (v *Vsphere) DeleteInstance(ctx *lepton.Context, instancename string) error
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -453,14 +452,14 @@ func (v *Vsphere) DeleteInstance(ctx *lepton.Context, instancename string) error
 		if _, ok := err.(*find.NotFoundError); ok {
 			fmt.Println("can't find vm " + instancename)
 		}
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	vm := vms[0]
 
 	task, err := vm.PowerOff(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	// Ignore error since the VM may already been in powered off
@@ -489,7 +488,7 @@ func (v *Vsphere) StartInstance(ctx *lepton.Context, instancename string) error
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -500,12 +499,12 @@ func (v *Vsphere) StartInstance(ctx *lepton.Context, instancename string) error
 		if _, ok := err.(*find.NotFoundError); ok {
 			fmt.Println("can't find vm " + instancename)
 		}
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	task, err := vms[0].PowerOn(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	_, err = task.WaitForResult(context.TODO(), nil)
@@ -520,7 +519,7 @@ func (v *Vsphere) StopInstance(ctx *lepton.Context, instancename string) error {
 
 	dc, err := f.DatacenterOrDefault(context.TODO(), v.datacenter)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return err
 	}
 
@@ -531,12 +530,12 @@ func (v *Vsphere) StopInstance(ctx *lepton.Context, instancename string) error {
 		if _, ok := err.(*find.NotFoundError); ok {
 			fmt.Println("can't find vm " + instancename)
 		}
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	task, err := vms[0].PowerOff(context.TODO())
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	_, err = task.WaitForResult(context.TODO(), nil)
diff --git a/vsphere/vsphere_store.go b/vsphere/vsphere_store.go
index 0351252..55a05cc 100644
--- a/vsphere/vsphere_store.go
+++ b/vsphere/vsphere_store.go
@@ -1,10 +1,10 @@
 package vsphere
 
 import (
-	"fmt"
 	"os/exec"
 	"strings"
 
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -27,7 +27,7 @@ func (s *Datastores) CopyToBucket(config *types.Config, archPath string) error {
 	cmd := exec.Command("qemu-img", args...)
 	err := cmd.Run()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return nil
diff --git a/vultr/vultr_image.go b/vultr/vultr_image.go
index b674056..0b8fa7b 100644
--- a/vultr/vultr_image.go
+++ b/vultr/vultr_image.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 	"github.com/olekukonko/tablewriter"
 )
@@ -53,12 +54,12 @@ func (v *Vultr) createImage(key string, bucket string, region string) {
 	client := &http.Client{}
 	resp, err := client.Do(req)
 	if err != nil {
-		panic(err)
+		log.Panic(err)
 	}
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 }
 
 func (v *Vultr) destroyImage(snapshotid string) {
@@ -81,7 +82,7 @@ func (v *Vultr) destroyImage(snapshotid string) {
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 }
 
 // CreateImage - Creates image on v using nanos images
@@ -112,8 +113,7 @@ func (v *Vultr) ListImages(ctx *lepton.Context) error {
 	client := http.Client{}
 	req, err := http.NewRequest("GET", "https://api.vultr.com/v1/snapshot/list", nil)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 	token := os.Getenv("TOKEN")
 
@@ -122,21 +122,19 @@ func (v *Vultr) ListImages(ctx *lepton.Context) error {
 
 	resp, err := client.Do(req)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	var data map[string]vultrSnap
 
 	err = json.Unmarshal(body, &data)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	table := tablewriter.NewWriter(os.Stdout)
@@ -171,7 +169,7 @@ func (v *Vultr) DeleteImage(ctx *lepton.Context, snapshotID string) error {
 
 // SyncImage syncs image from provider to another provider
 func (v *Vultr) SyncImage(config *types.Config, target lepton.Provider, image string) error {
-	fmt.Println("not yet implemented")
+	log.Warn("not yet implemented")
 	return nil
 }
 
diff --git a/vultr/vultr_instance.go b/vultr/vultr_instance.go
index 9a0b207..b1d13d6 100644
--- a/vultr/vultr_instance.go
+++ b/vultr/vultr_instance.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/olekukonko/tablewriter"
 )
 
@@ -47,7 +48,7 @@ func (v *Vultr) CreateInstance(ctx *lepton.Context) error {
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 
 	return nil
 }
@@ -69,8 +70,7 @@ func (v *Vultr) ListInstances(ctx *lepton.Context) error {
 	client := http.Client{}
 	req, err := http.NewRequest("GET", "https://api.vultr.com/v1/server/list", nil)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 	token := os.Getenv("TOKEN")
 
@@ -79,21 +79,19 @@ func (v *Vultr) ListInstances(ctx *lepton.Context) error {
 
 	resp, err := client.Do(req)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	var data map[string]vultrServer
 
 	err = json.Unmarshal(body, &data)
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	table := tablewriter.NewWriter(os.Stdout)
@@ -142,7 +140,7 @@ func (v *Vultr) DeleteInstance(ctx *lepton.Context, instanceID string) error {
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 	return nil
 }
 
@@ -167,7 +165,7 @@ func (v *Vultr) StartInstance(ctx *lepton.Context, instanceID string) error {
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 	return nil
 }
 
@@ -192,7 +190,7 @@ func (v *Vultr) StopInstance(ctx *lepton.Context, instanceID string) error {
 	defer resp.Body.Close()
 
 	body, _ := ioutil.ReadAll(resp.Body)
-	fmt.Println("response Body:", string(body))
+	log.Info("response Body:", string(body))
 	return nil
 }
 
diff --git a/vultr/vultr_store.go b/vultr/vultr_store.go
index 9f41b77..b098d92 100644
--- a/vultr/vultr_store.go
+++ b/vultr/vultr_store.go
@@ -1,12 +1,12 @@
 package vultr
 
 import (
-	"fmt"
 	"net/url"
 	"os"
 	"time"
 
 	"github.com/minio/minio-go"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/types"
 )
 
@@ -18,8 +18,7 @@ func (s *Objects) getSignedURL(key string, bucket string, region string) string
 	secKey := os.Getenv("VULTR_SECRET")
 
 	if accessKey == "" || secKey == "" {
-		fmt.Println("danger will robinson - can not find VULTR_ACCESS || VULTR_SECRET env vars")
-		os.Exit(1)
+		log.Fatalf("danger will robinson - can not find VULTR_ACCESS || VULTR_SECRET env vars")
 	}
 
 	endpoint := region + ".vultrobjects.com"
@@ -28,8 +27,7 @@ func (s *Objects) getSignedURL(key string, bucket string, region string) string
 
 	client, err := minio.New(endpoint, accessKey, secKey, ssl)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	reqParams := make(url.Values)
@@ -37,7 +35,7 @@ func (s *Objects) getSignedURL(key string, bucket string, region string) string
 	presignedURL, err := client.PresignedGetObject(bucket, key, time.Second*5*60, reqParams)
 
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 		return ""
 	}
 
@@ -65,25 +63,22 @@ func (s *Objects) CopyToBucket(config *types.Config, archPath string) error {
 
 	client, err := minio.New(endpoint, accessKey, secKey, ssl)
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	stat, err := file.Stat()
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	n, err := client.PutObject(bucket, config.CloudConfig.ImageName+".img", file, stat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
 	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
-	fmt.Println("Uploaded", "my-objectname", " of size: ", n, "Successfully.")
+	log.Info("Uploaded", "my-objectname", " of size: ", n, "Successfully.")
 
-	fmt.Printf("Successfully uploaded %q to %q\n", config.CloudConfig.ImageName, bucket)
+	log.Info("Successfully uploaded %q to %q\n", config.CloudConfig.ImageName, bucket)
 
 	return nil
 }


Test patch:
diff --git a/cmd/flags_config_test.go b/cmd/flags_config_test.go
index b8592d2..6fe04b1 100644
--- a/cmd/flags_config_test.go
+++ b/cmd/flags_config_test.go
@@ -39,7 +39,8 @@ func TestConfigFlagsMergeToConfig(t *testing.T) {
 				NetMask:   "255.255.255.0",
 				Gateway:   "192.168.1.254",
 			},
-			VolumesDir: lepton.LocalVolumeDir,
+			VolumesDir:                lepton.LocalVolumeDir,
+			LocalFilesParentDirectory: ".",
 		}
 
 		writeConfigToFile(expected, configFileName)
@@ -67,7 +68,8 @@ func TestConfigFlagsMergeToConfig(t *testing.T) {
 			RunConfig: types.RunConfig{
 				Memory: "2G",
 			},
-			VolumesDir: lepton.LocalVolumeDir,
+			VolumesDir:                lepton.LocalVolumeDir,
+			LocalFilesParentDirectory: ".",
 		}
 		config := &types.Config{
 			CloudConfig: types.ProviderConfig{
diff --git a/cmd/utils_test.go b/cmd/utils_test.go
index a23cc2d..da40a61 100644
--- a/cmd/utils_test.go
+++ b/cmd/utils_test.go
@@ -2,9 +2,7 @@ package cmd_test
 
 import (
 	"encoding/json"
-	"fmt"
 	"io/ioutil"
-	"log"
 	"os"
 	"testing"
 
@@ -13,6 +11,7 @@ import (
 
 	"github.com/nanovms/ops/cmd"
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -59,7 +58,7 @@ func buildInstance(imageName string) string {
 
 	err := createInstanceCmd.Execute()
 	if err != nil {
-		fmt.Println(err)
+		log.Error(err)
 	}
 
 	return instanceName
@@ -129,6 +128,6 @@ func writeConfigToFile(config *types.Config, fileName string) {
 
 	err := ioutil.WriteFile(fileName, json, 0666)
 	if err != nil {
-		panic(err)
+		log.Panic(err)
 	}
 }
diff --git a/lepton/logger_test.go b/log/logger_test.go
similarity index 60%
rename from lepton/logger_test.go
rename to log/logger_test.go
index 614f194..9534dde 100644
--- a/lepton/logger_test.go
+++ b/log/logger_test.go
@@ -1,10 +1,9 @@
-package lepton_test
+package log
 
 import (
 	"bytes"
+	"errors"
 	"testing"
-
-	"github.com/nanovms/ops/lepton"
 )
 
 const (
@@ -14,7 +13,7 @@ const (
 func TestLogger(t *testing.T) {
 	t.Run("Log should print to output", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.Log("test %d,%d,%d", 1, 2, 3)
 
@@ -28,7 +27,7 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Info should not print to output by default", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.SetInfo(false)
 		logger.Info("test %d,%d,%d", 1, 2, 3)
@@ -43,13 +42,13 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Info should print to output by default if set", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.SetInfo(true)
 		logger.Info("test %d,%d,%d", 1, 2, 3)
 
 		got := b.String()
-		want := lepton.ConsoleColors.Blue() + "test 1,2,3" + lepton.ConsoleColors.White() + newline
+		want := ConsoleColors.Blue() + "test 1,2,3" + ConsoleColors.White() + newline
 
 		if got != want {
 			t.Errorf("got %v want %v", got, want)
@@ -58,7 +57,7 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Warn should not print to output by default", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.Warn("test %d,%d,%d", 1, 2, 3)
 
@@ -72,13 +71,13 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Warn should print if set", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.SetWarn(true)
 		logger.Warn("test %d,%d,%d", 1, 2, 3)
 
 		got := b.String()
-		want := lepton.ConsoleColors.Yellow() + "test 1,2,3" + lepton.ConsoleColors.White() + newline
+		want := ConsoleColors.Yellow() + "test 1,2,3" + ConsoleColors.White() + newline
 
 		if got != want {
 			t.Errorf("got %v want %v", got, want)
@@ -87,9 +86,9 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Error should not print to output by default", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
-		logger.Error("test %d,%d,%d", 1, 2, 3)
+		logger.Errorf("test %d,%d,%d", 1, 2, 3)
 
 		got := b.String()
 		want := ""
@@ -101,13 +100,13 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Error should print if set", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.SetError(true)
-		logger.Error("test %d,%d,%d", 1, 2, 3)
+		logger.Errorf("test %d,%d,%d", 1, 2, 3)
 
 		got := b.String()
-		want := lepton.ConsoleColors.Red() + "test 1,2,3" + lepton.ConsoleColors.White() + newline
+		want := ConsoleColors.Red() + "test 1,2,3" + ConsoleColors.White() + newline
 
 		if got != want {
 			t.Errorf("got %v want %v", got, want)
@@ -116,7 +115,7 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Debug should not print to output by default", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.Debug("test %d,%d,%d", 1, 2, 3)
 
@@ -130,13 +129,45 @@ func TestLogger(t *testing.T) {
 
 	t.Run("Debug should print if set", func(t *testing.T) {
 		var b bytes.Buffer
-		logger := lepton.NewLogger(&b)
+		logger := New(&b)
 
 		logger.SetDebug(true)
 		logger.Debug("test %d,%d,%d", 1, 2, 3)
 
 		got := b.String()
-		want := lepton.ConsoleColors.Cyan() + "test 1,2,3" + lepton.ConsoleColors.White() + newline
+		want := ConsoleColors.Cyan() + "test 1,2,3" + ConsoleColors.White() + newline
+
+		if got != want {
+			t.Errorf("got %v want %v", got, want)
+		}
+	})
+}
+
+func TestLoggerError(t *testing.T) {
+	t.Run("Log Error should print error string to output", func(t *testing.T) {
+		var b bytes.Buffer
+		logger := New(&b)
+		logger.SetError(true)
+
+		logger.Error(errors.New("something wrong is happening"))
+
+		got := b.String()
+		want := ConsoleColors.Red() + "something wrong is happening" + ConsoleColors.White() + newline
+
+		if got != want {
+			t.Errorf("got %v want %v", got, want)
+		}
+	})
+
+	t.Run("Log Errorf should print formatted string to output", func(t *testing.T) {
+		var b bytes.Buffer
+		logger := New(&b)
+		logger.SetError(true)
+
+		logger.Errorf("something %s is happening", "fishy")
+
+		got := b.String()
+		want := ConsoleColors.Red() + "something fishy is happening" + ConsoleColors.White() + newline
 
 		if got != want {
 			t.Errorf("got %v want %v", got, want)
diff --git a/onprem/onprem_volume_test.go b/onprem/onprem_volume_test.go
index 9accc24..3093008 100644
--- a/onprem/onprem_volume_test.go
+++ b/onprem/onprem_volume_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/log"
 	"github.com/nanovms/ops/onprem"
 	"github.com/nanovms/ops/types"
 )
@@ -173,7 +174,7 @@ func TestOnPremVolume_AddMounts(t *testing.T) {
 
 			err = os.Symlink(src, dst)
 			if err != nil {
-				fmt.Println(err)
+				log.Error(err)
 			}
 
 			mounts = append(mounts, fmt.Sprintf("%s:%s", tt.mount, tt.mountAt))
diff --git a/testutils/build_programs.go b/testutils/build_programs.go
index 60b22d7..a9c5a9a 100644
--- a/testutils/build_programs.go
+++ b/testutils/build_programs.go
@@ -37,7 +37,6 @@ func BuildBasicProgram() (binaryPath string) {
 	cmd := exec.Command("go", "build", sourcePath)
 	err = cmd.Run()
 	if err != nil {
-		fmt.Println(err)
 		log.Fatal(err)
 	}