// Run is a go routine that performs the deploy job actions.
func (d *DeployWorker) Run() {
	d.wg.Add(1)
	defer d.wg.Done()
	// Write the start of job record to the DB.
	d.db.StartDeploy(d.Opts.Domain, d.Opts.Environment, d.Name, d.Version)

	// Build standard file name ex: foo.com-development-video-mobile-1.0.1-23.tar.gz
	tarFilePrefix := fmt.Sprintf("%s-%s-%s-%s", d.Opts.Domain, d.Opts.Environment, d.Name, d.Version)
	tarFileName := fmt.Sprintf("%s.tar.gz", tarFilePrefix)

	tarPath := fmt.Sprintf("%s%s/", tmpDir, d.Name)          // evaluates as "/tmp/" + "Appname" => "/tmp/Appname/"
	tarFilePath := fmt.Sprintf("%s%s", tarPath, tarFileName) // evaluates as "/tmp/Appname/" + "foo.tar.gz" => "/tmp/Appname/foo.tar.gz"

	if err := os.MkdirAll(tarPath, 0744); err != nil {
		d.log.Errorf("Cannot make tar temp path %s: %s", tarPath, err.Error())
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}
	// Download and untar the assets for this deploy from Artifactory.
	if errMsg := d.downloadAssets(tarPath, tarFilePath, tarFileName); errMsg != "" {
		d.log.Errorf(errMsg)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}

	defer os.Remove(tarFilePath)
	// evaluates as "/tmp/Appname/" + "foo.com-development-video-mobile-1.0.1-23" + "/"
	untarredPath := fmt.Sprintf("%s%s/", tarPath, tarFilePrefix)
	defer os.RemoveAll(untarredPath)

	// Get the filenames from the directory:
	metaFileName, serviceFileName, etcd2FileName := "", "", ""
	files, _ := ioutil.ReadDir(untarredPath)
	for _, f := range files {
		name := path.Base(f.Name())
		switch path.Ext(name) {
		case ".json":
			metaFileName = name
		case ".service":
			serviceFileName = name
		case ".tmpl":
			serviceFileName = name
		case ".etcd2":
			etcd2FileName = name
		default:
		}
	}

	// Validate deploy files exist and set paths.
	if metaFileName == "" {
		d.log.Errorf("Metadata file not found in %s", tarFilePath)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}
	if serviceFileName == "" {
		d.log.Errorf("Service unit file not found in %s", tarFilePath)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}
	metaFilePath := fmt.Sprintf("%s%s", untarredPath, metaFileName)
	serviceFilePath := fmt.Sprintf("%s%s", untarredPath, serviceFileName)
	etcd2FilePath := ""
	if etcd2FileName != "" {
		etcd2FilePath = fmt.Sprintf("%s%s", untarredPath, etcd2FileName)
	}

	// Get the metadata from the file.
	metaData, errMsg := d.getMetaData(metaFilePath)
	if errMsg != "" {
		d.log.Errorf(errMsg)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}

	// Submit a deploy request to the client library.
	co := &coscl.Options{
		Name:             metaData.Name,
		Version:          metaData.Version,
		ImageVersion:     metaData.ImageVersion,
		NumInstances:     metaData.NumInstances,
		TemplateFilePath: serviceFilePath,
		Etcd2FilePath:    etcd2FilePath,
		Token:            d.Opts.DeployToken,
		Url:              d.Opts.DeployURL,
		Debug:            false,
	}

	cl := coscl.New(co) // API client
	deployID, errMsg := d.submitDeployRequest(cl)
	if errMsg != "" {
		d.log.Errorf(errMsg)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, "", cosddb.Failed)
		return
	}
	co.DeployID = deployID
	d.DeployID = deployID

	// Loop check the status of the deploy and wait for the deploy to complete. Timeout 1 minute.
	errMsg = d.submitStatusRequest(cl, deployID)
	if errMsg != "" {
		d.log.Errorf(errMsg)
		d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, deployID, cosddb.Failed)
		return
	}

	// Mark the job complete.
	d.db.UpdateDeployByName(d.Opts.Domain, d.Opts.Environment, d.Name, d.DeployID, cosddb.Success)
}
// main is the main entry point for the client.
func main() {
	opts := &client.Options{}
	var showVersion bool

	flag.StringVar(&opts.Name, "n", "", "Name of the service.")
	flag.StringVar(&opts.Name, "name", "", "Name of the service.")
	flag.StringVar(&opts.Version, "r", "", "Version of the service.")
	flag.StringVar(&opts.Version, "service_version", "", "Version of the service.")
	flag.StringVar(&opts.ImageVersion, "k", "latest", "Version of the docker image.")
	flag.StringVar(&opts.ImageVersion, "image_version", "latest", "Version of the docker image.")
	flag.IntVar(&opts.NumInstances, "i", 2, "Number of instances to instantiate.")
	flag.IntVar(&opts.NumInstances, "instances", 2, "Number of instances to instantiate.")
	flag.StringVar(&opts.TemplateFilePath, "t", "", "path/file to the unit .service file.")
	flag.StringVar(&opts.TemplateFilePath, "template_filepath", "", "path/file to the unit .service file.")
	flag.StringVar(&opts.Etcd2FilePath, "e", "", "path/file to the etcd2 key/value file.")
	flag.StringVar(&opts.Etcd2FilePath, "etcd2_filepath", "", "path/file to the etcd2 key/value file.")

	flag.StringVar(&opts.Token, "b", "", "API authorization token")
	flag.StringVar(&opts.Token, "bearer_token", "", "API authorization token.")
	flag.StringVar(&opts.Url, "u", "", "URL of the load balancer to the coreos-deploy server.")
	flag.StringVar(&opts.Url, "url", "", "URL of the load balancer to the coreos-deploy server.")

	flag.StringVar(&opts.DeployID, "p", "", "Deploy ID to lookup.")
	flag.StringVar(&opts.DeployID, "deploy_id", "", "Deploy ID to lookup.")

	flag.BoolVar(&opts.Debug, "d", false, "Enable debugging output.")
	flag.BoolVar(&opts.Debug, "debug", false, "Enable debugging output.")
	flag.BoolVar(&showVersion, "V", false, "Show version.")
	flag.BoolVar(&showVersion, "version", false, "Show version.")
	flag.Usage = client.PrintUsageAndExit
	flag.Parse()

	// Version flag request?
	if showVersion {
		client.PrintVersionAndExit()
	}

	// Check additional params beyond the flags.
	for _, arg := range flag.Args() {
		switch strings.ToLower(arg) {
		case "version":
			client.PrintVersionAndExit()
		case "help":
			client.PrintUsageAndExit()
		}
	}

	// Validate the mandatory options.
	if err := opts.Validate(); err != nil {
		client.PrintErr(err.Error())
		return
	}

	// Run the deploy.
	c := client.New(opts)
	result, err := c.Execute()
	if err != nil {
		client.PrintErr(err.Error())
	} else {
		fmt.Println(result)
	}
}