func ReaderForTarUrl(url string) (io.ReadCloser, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	response, err := client.Do(req)
	if err != nil {
		return nil, errors.New("Failed response from Tarball Url.")
	}
	return response.Body, nil
}
URL example = https://github.com/tutumcloud/docker-hello-world/archive/v1.0.tar.gz
package main
import (
	"archive/tar"
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"errors"
	"github.com/ant0ine/go-json-rest/rest"
	"github.com/garyburd/redigo/redis"
	"github.com/nu7hatch/gouuid"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"regexp"
	"strings"
)
type PassedParams struct {
	Image_name      string
	Username        string
	Password        string
	Email           string
	Dockerfile      string
	Tar_url         string
	TarFile         []byte
	Github_username string
	Github_reponame string
	Github_tag      string
}
type PushAuth struct {
	Username      string `json:"username"`
	Password      string `json:"password"`
	Serveraddress string `json:"serveraddress"`
	Email         string `json:"email"`
}
type JobID struct {
	JobIdentifier string
}
type JobStatus struct {
	Status string
}
type JobLogs struct {
	Logs string
}
type StreamCatcher struct {
	ErrorDetail ErrorCatcher `json:"errorDetail"`
	Stream      string       `json:"stream"`
}
type ErrorCatcher struct {
	Message string `json:"message"`
	Error   string `json:"error"`
}
func main() {
	handler := rest.ResourceHandler{
		EnableRelaxedContentType: true,
	}
	handler.SetRoutes(
		&rest.Route{"POST", "/api/v1/build", BuildImageFromDockerfile},
		&rest.Route{"GET", "/api/v1/:jobid/status", GetStatusForJobID},
		&rest.Route{"GET", "/api/v1/:jobid/logs", GetLogsForJobID},
	)
	var port string
	if os.Getenv("PORT") != "" {
		port = os.Getenv("PORT")
	} else {
		port = ":8080"
	}
	http.ListenAndServe(port, &handler)
}
func GetStatusForJobID(w rest.ResponseWriter, r *rest.Request) {
	jobid := r.PathParam("jobid")
	c := RedisConnection()
	defer c.Close()
	exists, err := redis.Bool(c.Do("HEXISTS", jobid, "status"))
	if exists == true {
		var status JobStatus
		status.Status, err = redis.String(c.Do("HGET", jobid, "status"))
		if err != nil {
			rest.Error(w, "Redis Cache Error", 404)
		}
		w.WriteJson(status)
	} else {
		rest.Error(w, "Jobid doesn't exist in the cache.  Bad request.", 404)
	}
}
func GetLogsForJobID(w rest.ResponseWriter, r *rest.Request) {
	jobid := r.PathParam("jobid")
	c := RedisConnection()
	defer c.Close()
	exists, err := redis.Bool(c.Do("HEXISTS", jobid, "logs"))
	if exists == true {
		var logs JobLogs
		logs.Logs, err = redis.String(c.Do("HGET", jobid, "logs"))
		if err != nil {
			rest.Error(w, "Redis Cache Error", 404)
		}
		w.WriteJson(logs)
	} else {
		rest.Error(w, "Jobid doesn't exist in the cache.  Bad request.", 404)
	}
}
func BuildImageFromDockerfile(w rest.ResponseWriter, r *rest.Request) {
	passedParams := PassedParams{}
	if strings.Contains(r.Header.Get("Content-Type"), "multipart") == true {
		form, err := r.MultipartReader()
		TarPart, err := form.NextPart()
		TarData, err := ioutil.ReadAll(TarPart)
		JsonPart, err := form.NextPart()
		JsonData, _ := ioutil.ReadAll(JsonPart)
		err = json.Unmarshal(JsonData, &passedParams)
		if err != nil {
			rest.Error(w, err.Error(), 400)
			return
		}
		passedParams.TarFile = TarData
	} else {
		err := r.DecodeJsonPayload(&passedParams)
		if err != nil {
			rest.Error(w, err.Error(), 400)
			return
		}
	}
	if Validate(passedParams) {
		var jobid JobID
		jobid.JobIdentifier = JobUUIDString()
		c := RedisConnection()
		n, err := c.Do("HSET", jobid.JobIdentifier, "status", "Building")
		if n == 0 {
			rest.Error(w, "Unable to access the redis cache.  Make sure the CACHE_PASSWORD is set.", 501)
			return
		}
		if err != nil {
			rest.Error(w, " err trigg Unable to access the redis cache.  Make sure the CACHE_PASSWORD is set.", 400)
			return
		}
		c.Do("HSET", jobid.JobIdentifier, "logs", "Fetching logs...")
		go BuildPushAndDeleteImage(jobid.JobIdentifier, passedParams, c)
		w.WriteJson(jobid)
	} else {
		rest.Error(w, "Insufficient Information.  Must provide at least an Image Name and a Dockerfile/Tarurl.", 400)
		return
	}
}
func BuildPushAndDeleteImage(jobid string, passedParams PassedParams, c redis.Conn) {
	splitImageName := make([]string, 2)
	if strings.Contains(passedParams.Image_name, ".") {
		splitImageName = strings.SplitN(passedParams.Image_name, "/", 2)
	}
	buildUrl := ("/v1.10/build?nocache=true&t=" + passedParams.Image_name)
	dockerDial := Dial()
	dockerConnection := httputil.NewClientConn(dockerDial, nil)
	readerForInput, err := ReaderForInputType(passedParams, c, jobid)
	if err != nil {
		c.Do("HSET", jobid, "status", err)
		return
	}
	buildReq, err := http.NewRequest("POST", buildUrl, readerForInput)
	buildResponse, err := dockerConnection.Do(buildReq)
	defer buildResponse.Body.Close()
	buildReader := bufio.NewReader(buildResponse.Body)
	if err != nil {
		c.Do("HSET", jobid, "status", err)
		return
	}
	var logsString string
	for {
		line, err := buildReader.ReadBytes('\r')
		if err != nil {
			break
		}
		line = bytes.TrimSpace(line)
		var stream StreamCatcher
		err = json.Unmarshal(line, &stream)
		if stream.ErrorDetail.Message != "" {
			buildLogsSlice := []byte(logsString)
			buildLogsSlice = append(buildLogsSlice, []byte(stream.ErrorDetail.Message)...)
			logsString = string(buildLogsSlice)
			c.Do("HSET", jobid, "logs", logsString)
			CacheBuildError := "Failed: " + stream.ErrorDetail.Message
			c.Do("HSET", jobid, "status", CacheBuildError)
			return
		}
		if stream.Stream != "" {
			buildLogsSlice := []byte(logsString)
			buildLogsSlice = append(buildLogsSlice, []byte(stream.Stream)...)
			logsString = string(buildLogsSlice)
			c.Do("HSET", jobid, "logs", logsString)
		}
	}
	c.Do("HSET", jobid, "status", "Pushing")
	pushUrl := ("/v1.10/images/" + passedParams.Image_name + "/push")
	pushReq, err := http.NewRequest("POST", pushUrl, nil)
	pushReq.Header.Add("X-Registry-Auth", StringEncAuth(passedParams, ServerAddress(splitImageName[0])))
	pushResponse, err := dockerConnection.Do(pushReq)
	pushReader := bufio.NewReader(pushResponse.Body)
	if err != nil {
		c.Do("HSET", jobid, "status", err)
		return
	}
	for {
		line, err := pushReader.ReadBytes('\r')
		if err != nil {
			break
		}
		line = bytes.TrimSpace(line)
		var stream StreamCatcher
		err = json.Unmarshal(line, &stream)
		if stream.ErrorDetail.Message != "" {
			pushLogsSlice := []byte(logsString)
			pushLogsSlice = append(pushLogsSlice, []byte(stream.ErrorDetail.Message)...)
			logsString = string(pushLogsSlice)
			c.Do("HSET", jobid, "logs", logsString)
			CachePushError := "Failed: " + stream.ErrorDetail.Message
			c.Do("HSET", jobid, "status", CachePushError)
			return
		}
	}
	c.Do("HSET", jobid, "status", "Finished")
	c.Close()
	deleteUrl := ("/v1.10/images/" + passedParams.Image_name)
	deleteReq, err := http.NewRequest("DELETE", deleteUrl, nil)
	dockerConnection.Do(deleteReq)
	dockerConnection.Close()
}
func Validate(passedParams PassedParams) bool {
	switch {
	case passedParams.Dockerfile == "" && passedParams.Tar_url == "" && passedParams.TarFile == nil && passedParams.Github_reponame == "":
		return false
	case passedParams.Image_name == "":
		return false
	default:
		return true
	}
}
func StringEncAuth(passedParams PassedParams, serveraddress string) string {
	var data PushAuth
	data.Username = passedParams.Username
	data.Password = passedParams.Password
	data.Email = passedParams.Email
	data.Serveraddress = serveraddress
	jsonData, err := json.Marshal(data)
	if err != nil {
		log.Println("error:", err)
	}
	sEnc := base64.StdEncoding.EncodeToString([]byte(jsonData))
	return sEnc
}
func DockerNode() string {
	var docker_node string
	if os.Getenv("DOCKER_HOST") != "" {
		docker_node = os.Getenv("DOCKER_HOST")
	} else {
		docker_node = "http://127.0.0.1:4243"
	}
	return docker_node
}
func Dial() net.Conn {
	var docker_proto string
	var docker_host string
	if os.Getenv("DOCKER_HOST") != "" {
		dockerHost := os.Getenv("DOCKER_HOST")
		splitStrings := strings.SplitN(dockerHost, "://", 2)
		docker_proto = splitStrings[0]
		docker_host = splitStrings[1]
	} else {
		docker_proto = "tcp"
		docker_host = "localhost:4243"
	}
	dockerDial, err := net.Dial(docker_proto, docker_host)
	if err != nil {
		log.Println("Failed to reach docker")
		log.Fatal(err)
	}
	return dockerDial
}
func RedisConnection() redis.Conn {
	c, err := redis.Dial("tcp", CachePort())
	if err != nil {
		log.Println(err)
	}
	if os.Getenv("CACHE_PASSWORD") != "" {
		c.Do("AUTH", os.Getenv("CACHE_PASSWORD"))
	}
	return c
}
func JobUUIDString() string {
	uniqueJobId, uuidErr := uuid.NewV4()
	if uuidErr != nil {
		log.Fatal(uuidErr)
	}
	s := uniqueJobId.String()
	return s
}
func CachePort() string {
	var cacheTCPAddress string
	if os.Getenv("CACHE_1_PORT_6379_TCP_ADDR") != "" {
		cacheTCPAddress = os.Getenv("CACHE_1_PORT_6379_TCP_ADDR")
	} else {
		cacheTCPAddress = ""
	}
	var cachePort string
	if os.Getenv("CACHE_1_PORT_6379_TCP_PORT") != "" {
		cachePort = os.Getenv("CACHE_1_PORT_6379_TCP_PORT")
	} else {
		cachePort = "6379"
	}
	return cacheTCPAddress + ":" + cachePort
}
func ServerAddress(privateRepo string) string {
	var serveraddress string
	if privateRepo != "" {
		serveraddress = ("https://" + privateRepo + "/v1/")
	} else {
		serveraddress = "https://index.docker.io/v1/"
	}
	return serveraddress
}
func ReaderForInputType(passedParams PassedParams, c redis.Conn, jobid string) (io.Reader, error) {
	switch {
	case passedParams.Dockerfile != "":
		return ReaderForDockerfile(passedParams.Dockerfile), nil
	case passedParams.TarFile != nil:
		return bytes.NewReader(passedParams.TarFile), nil
	case passedParams.Tar_url != "":
		return ReaderForTarUrl(passedParams.Tar_url)
	case passedParams.Github_tag != "" && passedParams.Github_username != "" && passedParams.Github_reponame != "":
		return ReaderForGithubTar(passedParams, c, jobid)
	default:
		return nil, errors.New("Failed in the ReaderForInputType.  Got to default case.")
	}
}
func ReaderForGithubTar(passedParams PassedParams, c redis.Conn, jobid string) (*bytes.Buffer, error) {
	url := "https://github.com/" + passedParams.Github_username + "/" + passedParams.Github_reponame + "/archive/" + passedParams.Github_tag + ".tar.gz"
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	response, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	}
	if response.Status == "404 Not Found" {
		c.Do("HSET", jobid, "status", response.Status+" - Make sure the github repo and tag are correct.")
		return nil, errors.New("Failed download from github.")
	}
	contents, err := ioutil.ReadAll(response.Body)
	zippedBytesReader := bytes.NewReader(contents)
	gzipReader, err := gzip.NewReader(zippedBytesReader)
	var b bytes.Buffer
	io.Copy(&b, gzipReader)
	var folderName string
	matchedv, err := regexp.MatchString("^v", passedParams.Github_tag)
	if matchedv {
		githubTagSlice := strings.SplitAfterN(passedParams.Github_tag, "v", 2)
		folderName = passedParams.Github_reponame + "-" + githubTagSlice[1] + "/"
	} else {
		folderName = passedParams.Github_reponame + "-" + passedParams.Github_tag + "/"
	}
	finalBuffer := new(bytes.Buffer)
	tarWriter := tar.NewWriter(finalBuffer)
	tarBytesReader := bytes.NewReader(b.Bytes())
	tarReader := tar.NewReader(tarBytesReader)
	for {
		hdr, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Println(err)
		}
		matchedFolder, err := regexp.MatchString(folderName+".+", hdr.Name)
		if matchedFolder {
			unloadBuffer := new(bytes.Buffer)
			_, err := io.Copy(unloadBuffer, tarReader)
			if err != nil {
				log.Println(err)
			}
			slicedHeader := strings.SplitN(hdr.Name, folderName, 2)
			hdr.Name = slicedHeader[1]
			if err := tarWriter.WriteHeader(hdr); err != nil {
				log.Println(err)
			}
			if _, err := tarWriter.Write(unloadBuffer.Bytes()); err != nil {
				log.Println(err)
			}
		}
	}
	return finalBuffer, nil
}