func ListCronJobs() ([]batchv1beta1.CronJob, error) {
	cronjobs, err := clientset.BatchV1beta1().CronJobs(namespace).List(metav1.ListOptions{})
	if err != nil {
		if strings.Contains(err.Error(), "cronjobs.batch is forbidden") {
			fmt.Println(err.Error())
			fmt.Println("You do not have permission to list CronJobs.")
			os.Exit(1)
		}
		panic(err)
	}
	if config.Config().IsSet("owner") {
		var output []batchv1beta1.CronJob
		for _, job := range cronjobs.Items {
			owner, err := envsubst.String(config.Config().GetString("owner"))
			if err != nil {
				panic(err.Error())
			}
			if job.Annotations["krontabOwner"] == owner {
				output = append(output, job)
			}
		}
		return output, nil
	}
	return cronjobs.Items, nil
}
ListCronJobs gets a list of Kubernetes CronJob resources
package crontab
import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/a8m/envsubst"
	"github.com/dustin/go-humanize"
	uuid "github.com/satori/go.uuid"
	"gopkg.in/yaml.v2"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	batchv1Types "k8s.io/client-go/kubernetes/typed/batch/v1"
	batchv1beta1Types "k8s.io/client-go/kubernetes/typed/batch/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"github.com/jacobtomlinson/krontab/config"
	"github.com/jacobtomlinson/krontab/input"
	"github.com/jacobtomlinson/krontab/template"
)
var namespace string
var clientset *kubernetes.Clientset
var cronjobsClient batchv1beta1Types.CronJobInterface
var jobsClient batchv1Types.JobInterface
var crontabHeader = `# Welcome to krontab, a crontab like editor for Kubernetes cron jobs.
#
# This is a virtual file and was generated from the kubernetes API. Any edits you make here will be
# sent back to the kubernetes API to create/update/delete CronJob resources. Next time you open this crontab
# you may notice different formatting and comments to how you save it. Comments are meaningful and contain
# metadata about the job.
#
# Example job
# -----------
#
# # template: default
# 0 1 * * * echo hello world  # name: hello-world
#
# Templates
# ---------
#
# Krontab uses templates to turn your your crontab into kubernetes compliant CronJob resources. You will find
# a default template by running 'krontab get template default'. This is a minimal CrobJob resource with runs your
# command in a ubuntu container. When krontab creates your jobs it will replace the container command and schedule pattern
# in the template with the command and schedule pattern from the crontab.
#
# All cronjobs following a '# template: <name>' comment will be created using that template. If no template comments exist
# then the default template will be used.
#
# Names
# -----
# Kubernetes requires each job to have a name and is specified at the end of a job with a '# name: <name>' comment.
# If you do not specify a name one will be autogenerated.
`
type KronJob struct {
	Template string
	Name     string
	Timing   string
	Command  string
}
func (k KronJob) Create() error {
	cronjob, _ := k.Construct()
	_, err := cronjobsClient.Create(cronjob)
	if err != nil {
		panic(err.Error())
	}
	return err
}
func (k KronJob) Construct() (*batchv1beta1.CronJob, error) {
	templateYaml, err := template.GetTemplate(k.Template)
	decode := scheme.Codecs.UniversalDeserializer().Decode
	obj, _, err := decode([]byte(templateYaml), nil, nil)
	if err != nil {
		panic(err.Error())
	}
	cronjob := obj.(*batchv1beta1.CronJob)
	cronjob.Spec.JobTemplate.Name = k.Name
	cronjob.Name = k.Name
	cronjob.Spec.Schedule = k.Timing
	cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Name = k.Name
	cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command = strings.Split(strings.TrimSpace(k.Command), " ")
	if cronjob.Annotations == nil {
		cronjob.Annotations = make(map[string]string)
	}
	cronjob.Annotations["krontabTemplate"] = k.Template
	cronjob.Annotations["krontabManaged"] = "true"
	if config.Config().IsSet("owner") {
		owner, err := envsubst.String(config.Config().GetString("owner"))
		if err != nil {
			panic(err.Error())
		}
		cronjob.Annotations["krontabOwner"] = owner
	}
	return cronjob, nil
}
func (k KronJob) Run() (string, error) {
	cronjob, _ := k.Construct()
	suffix := "-" + strconv.Itoa(int(time.Now().Unix()))
	job := &batchv1.Job{
		ObjectMeta: metav1.ObjectMeta{
			Name: k.Name + suffix,
		},
		Spec: cronjob.Spec.JobTemplate.Spec,
	}
	if job.Annotations == nil {
		job.Annotations = make(map[string]string)
	}
	job.Annotations["krontabOneShot"] = "true"
	job.Annotations["krontabTemplate"] = k.Template
	job.Annotations["krontabManaged"] = "true"
	if config.Config().IsSet("owner") {
		owner, err := envsubst.String(config.Config().GetString("owner"))
		if err != nil {
			panic(err.Error())
		}
		job.Annotations["krontabOwner"] = owner
	}
	result, err := jobsClient.Create(job)
	if err != nil {
		panic(err)
	}
	return result.GetObjectMeta().GetName(), nil
}
func (k KronJob) Exists() bool {
	exists := false
	jobs, err := ListKronJobs()
	if err != nil {
		panic(err)
	}
	for _, existingJob := range jobs {
		if k.Name == existingJob.Name {
			exists = true
		}
	}
	return exists
}
func (k KronJob) Delete() error {
	deletePolicy := metav1.DeletePropagationForeground
	err := cronjobsClient.Delete(k.Name, &metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	})
	return err
}
func (k KronJob) Update() error {
	cronjob, err := cronjobsClient.Get(k.Name, metav1.GetOptions{})
	if err != nil {
		panic(err)
	}
	if cronjob.Annotations["krontabTemplate"] != k.Template {
		if !template.IsTemplate(k.Template) {
			return fmt.Errorf("no such template %s", k.Template)
		}
		err = k.Delete()
		if err != nil {
			panic(err)
		}
		err = k.Create()
		if err != nil {
			panic(err)
		}
		return nil
	}
	cronjob.Spec.JobTemplate.Name = k.Name
	cronjob.Name = k.Name
	cronjob.Spec.Schedule = k.Timing
	cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Name = k.Name
	cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command = strings.Split(k.Command, " ")
	_, err = cronjobsClient.Update(cronjob)
	return err
}
func EditCrontab() {
	crontab, err := BuildCrontab()
	if err != nil {
		panic(err)
	}
	rawKrontab, err := input.UserInput(crontabHeader + crontab)
	if err != nil {
		fmt.Println("Krontab was not changed. Exiting without applying.")
		os.Exit(0)
	}
	jobs, err := ParseCrontab(rawKrontab)
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	for _, job := range jobs {
		if job.Exists() {
			err = job.Update()
			if err != nil {
				panic(err)
			}
		} else {
			err = job.Create()
			if err != nil {
				panic(err)
			}
		}
	}
	existingJobs, err := ListKronJobs()
	if err != nil {
		panic(err)
	}
	for _, existingJob := range existingJobs {
		found := false
		for _, job := range jobs {
			if existingJob.Name == job.Name {
				found = true
			}
		}
		if !found {
			existingJob.Delete()
		}
	}
}
func ListCrontab() {
	crontab, err := BuildCrontab()
	if err != nil {
		panic(err)
	}
	fmt.Printf(crontabHeader)
	fmt.Println(crontab)
}