func (je *JobExecution) Terminate() {
	je.client.unsubscribe()
}
Terminate the job execution if the process has to stop
package awsiotjobs
import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"sync"
	"time"
	mqtt "github.com/eclipse/paho.mqtt.golang"
)
const jobBaseTopic = "$aws/things/%s/jobs/%s"
const publishTimeout = 2 * time.Second
type Config struct {
	Port            int
	CaCertPath      string
	CertificatePath string
	PrivateKeyPath  string
	Endpoint        string
	ThingName       string
	ClientID        string
	Handler         func(je JobExecutioner)
}
func (c *Config) FromFile(file string) error {
	s, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Printf("Invalid config file - ignoring\n")
		return err
	}
	json.Unmarshal(s, &c)
	return nil
}
type nextJobPayload struct {
	clientToken string
}
func NewTLSConfig(caCertPath, certPath, privKeyPath string) *tls.Config {
	certpool := x509.NewCertPool()
	caCert, err := ioutil.ReadFile(caCertPath)
	if err == nil {
		certpool.AppendCertsFromPEM(caCert)
	} else {
		panic(err)
	}
	cert, err := tls.LoadX509KeyPair(certPath, privKeyPath)
	if err != nil {
		panic(err)
	}
	cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		panic(err)
	}
	return &tls.Config{
		RootCAs:            certpool,
		ClientAuth:         tls.NoClientCert,
		ClientCAs:          nil,
		InsecureSkipVerify: false,
		Certificates:       []tls.Certificate{cert},
	}
}
type JobError struct {
	ErrCode    string
	ErrMessage string
}
func (err JobError) Error() string {
	return fmt.Sprintf("code %s, msg: %s", err.ErrCode, err.ErrMessage)
}
type JobDocument map[string]interface{}
type StatusDetails map[string]interface{}
type JobExecutioner interface {
	GetJobDocument() JobDocument
	GetStatusDetails() StatusDetails
	Publish(string, byte, interface{})
	Success(StatusDetails) error
	Fail(JobError) error
	Reject(JobError) error
	InProgress(StatusDetails) error
	Terminate()
	GetThingName() string
	GetJobID() string
}
type JobExecution struct {
	JobID           string        `json:"jobId"`
	ThingName       string        `json:"thingName"`
	JobDocument     JobDocument   `json:"jobDocument"`
	Status          string        `json:"status"`
	StatusDetails   StatusDetails `json:"statusDetails"`
	QueuedAt        int64         `json:"queuedAt"`
	StartedAt       int64         `json:"startedAt"`
	LastUpdatedAt   int64         `json:"lastUpdatedAt"`
	VersionNumber   int64         `json:"versionNumber"`
	ExecutionNumber int64         `json:"executionNumber"`
	client          *Client
	mux             sync.Mutex
}
func (je *JobExecution) GetJobDocument() JobDocument {
	return je.JobDocument
}
func (je *JobExecution) GetStatusDetails() StatusDetails {
	return je.StatusDetails
}
func (je *JobExecution) GetThingName() string {
	return je.client.config.ThingName
}
func (je *JobExecution) GetJobID() string {
	return je.JobID
}
func (je *JobExecution) getUpdatePayload() interface{} {
	payload := make(map[string]interface{})
	payload["status"] = je.Status
	payload["statusDetails"] = je.StatusDetails
	payload["expectedVersion"] = je.VersionNumber
	payload["executionNumber"] = je.ExecutionNumber
	payload["includeJobExecutionState"] = true
	payload["clientToken"] = "client-token"
	jsonPayload, _ := json.Marshal(payload)
	return jsonPayload
}
func (je *JobExecution) sendUpdate() error {
	if je.client.Iot == nil {
		log.Panic("Iot client not set")
	}
	payload := je.getUpdatePayload()
	topic := fmt.Sprintf("%s/update", fmt.Sprintf(jobBaseTopic, je.client.config.ThingName, je.JobID))
	log.Printf("Updating status with %s\non topic %s\n", string(payload.([]byte)), topic)
	token := je.client.Iot.Publish(topic, 1, false, payload)  
	if token.WaitTimeout(publishTimeout) && token.Error() != nil {
		return token.Error()
	}
	return nil
}
func (je *JobExecution) InProgress(statusDetails StatusDetails) error {
	log.Printf("JOB IN_PROGRESS: %v\n", statusDetails)
	je.mux.Lock()
	je.StatusDetails = statusDetails
	je.Status = "IN_PROGRESS"
	je.mux.Unlock()
	return je.sendUpdate()
}
func (je *JobExecution) Success(statusDetails StatusDetails) error {
	log.Printf("JOB SUCCEEDED: %v\n", statusDetails)
	je.mux.Lock()
	je.StatusDetails = statusDetails
	je.Status = "SUCCEEDED"
	je.mux.Unlock()
	err := je.sendUpdate()
	if err != nil {
		return err
	}
	je.unsubscribeFromUpdates()
	return nil
}
func (je *JobExecution) Fail(err JobError) error {
	log.Printf("JOB FAIL: %v\n", err)
	je.mux.Lock()
	je.StatusDetails = map[string]interface{}{
		"error": err.Error(),
	}
	je.Status = "FAILED"
	je.mux.Unlock()
	e := je.sendUpdate()
	if e != nil {
		return err
	}
	je.unsubscribeFromUpdates()
	return nil
}
func (je *JobExecution) Reject(err JobError) error {
	log.Printf("JOB REJECTED: %v\n", err)
	je.mux.Lock()
	je.StatusDetails = map[string]interface{}{
		"error": err.Error(),
	}
	je.Status = "REJECTED"
	je.mux.Unlock()
	e := je.sendUpdate()
	if e != nil {
		return err
	}
	je.unsubscribeFromUpdates()
	return nil
}