func watcher(_stop chan bool, connect connector, unmarshall unmarshaller) (chan Event, error) {
	watcher, err := connect()
	if err != nil {
		return nil, err
	}
	out := make(chan Event, 50)
	go func() {
		for {
			select {
			case evt := <-watcher.ResultChan():
				glog.V(5).Infof("Received event: %v", evt)
				if evt.Type == watch.Error {
					glog.Errorf("Error watching: %v", evt)
					metrics.Increase("watch_event_error")
				}
				if evt.Object == nil {
					watcher = reconnectWatcher(connect)
				} else {
					obj, err := unmarshall(evt.Object)
					if err != nil {
						glog.Errorf("Error watching: %v", evt)
					} else {
						publishWatchEvent(out, obj, evt)
					}
				}
			case <-_stop:
				return
			}
		}
	}()
	return out, nil
}
watcher will start watching given watcher and unmarhall the received objects
with the provided unmarshall function. If an error occurs it will reconnect
with the provided connect function. If the initial connection fails it will
return an error, otherwise it will return a channel on which the scanner
events will be published. It will stop watching when the given _stop channel
will contain a message.
package scanner
import (
	"fmt"
	"strconv"
	"strings"
	"time"
	"github.com/golang/glog"
	"github.com/spf13/viper"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"github.com/joyrex2001/nightshift/internal/metrics"
	"github.com/joyrex2001/nightshift/internal/schedule"
)
const (
	ScheduleAnnotation string = "joyrex2001.com/nightshift.schedule"
	IgnoreAnnotation string = "joyrex2001.com/nightshift.ignore"
	SaveStateAnnotation string = "joyrex2001.com/nightshift.savestate"
)
func getKubernetes() (*rest.Config, error) {
	kubeconfig := viper.GetString("openshift.kubeconfig")
	if kubeconfig != "" {
		config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
		if err == nil {
			return config, nil
		}
	}
	return rest.InClusterConfig()
}
func getState(annotations map[string]string) (*State, error) {
	repls, ok := annotations[SaveStateAnnotation]
	if !ok {
		glog.V(5).Info("no previous state available")
		return nil, nil
	}
	repl, err := strconv.Atoi(repls)
	if err != nil {
		return nil, err
	}
	return &State{Replicas: repl}, nil
}
func updateState(meta metav1.ObjectMeta, repl int) metav1.ObjectMeta {
	if meta.Annotations == nil {
		meta.Annotations = map[string]string{}
	}
	meta.Annotations[SaveStateAnnotation] = strconv.Itoa(repl)
	return meta
}
func getSchedule(cfgsched []*schedule.Schedule, annotations map[string]string) ([]*schedule.Schedule, error) {
	dis := strings.ToLower(annotations[IgnoreAnnotation])
	if dis == "true" {
		return nil, nil
	} else if dis != "false" && dis != "" {
		return nil, fmt.Errorf("invalid value '%s' for %s", dis, IgnoreAnnotation)
	}
	if ann := annotations[ScheduleAnnotation]; ann != "" {
		return annotationToSchedule(ann)
	}
	return cfgsched, nil
}
func annotationToSchedule(annotation string) ([]*schedule.Schedule, error) {
	sched := []*schedule.Schedule{}
	for _, ann := range strings.Split(annotation, ";") {
		if ann == "" {
			continue
		}
		s, err := schedule.New(ann)
		if err != nil {
			return nil, err
		}
		sched = append(sched, s)
	}
	return sched, nil
}
type connector func() (watch.Interface, error)
type unmarshaller func(interface{}) (*Object, error)