func camelCaseToLowerSeparated(src string, sep string) string {
	var words []string
	var word []rune
	var last rune
	for _, r := range src {
		if unicode.IsUpper(r) {
			if unicode.IsUpper(last) {
				word = append(word, r)
			} else {
				if len(word) > 0 {
					words = append(words, strings.ToLower(string(word)))
				}
				word = []rune{r}
			}
		} else {
			if unicode.IsUpper(last) && len(word) >= 2 {
				words = append(words, strings.ToLower(string(word[:len(word)-1])))
				word = []rune{last, r}
			} else {
				word = append(word, r)
			}
		}
		last = r
	}
	if len(word) > 0 {
		words = append(words, strings.ToLower(string(word)))
	}
	return strings.Join(words, sep)
}
From https://github.com/vmware/dispatch/blob/master/pkg/utils/str_convert.go
camelCaseToLowerSeparated converts a camel cased string to a multi-word
string delimited by the specified separator
package events
import (
	"context"
	"encoding/json"
	"log"
	"net/url"
	"reflect"
	"strings"
	"time"
	"unicode"
	ofsdk "github.com/openfaas-incubator/connector-sdk/types"
	"github.com/pkg/errors"
	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/event"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/soap"
	vtypes "github.com/vmware/govmomi/vim25/types"
)
type OutboundEvent struct {
	Topic    string `json:"topic,omitempty"`
	Category string `json:"category,omitempty"`
	Source   string `json:"source"`
	UserName               string                         `json:"userName,omitempty"`
	CreatedTime            time.Time                      `json:"createdTime,omitempty"`
	ObjectName             string                         `json:"objectName,omitempty"`
	ManagedObjectReference *vtypes.ManagedObjectReference `json:"managedObjectReference,omitempty"`
}
type EventReceiver struct{}
func (e *EventReceiver) Response(res ofsdk.InvokerResponse) {
	if res.Error != nil {
		log.Printf("function %s for topic %s returned status %d with error: %v", res.Function, res.Topic, res.Status, res.Error)
	}
	log.Printf("successfully invoked function %s for topic %s", res.Function, res.Topic)
}
func NewEventReceiver() *EventReceiver {
	return &EventReceiver{}
}
func NewVCenterClient(ctx context.Context, user string, pass string, vcenterURL string, insecure bool) (*govmomi.Client, error) {
	u, err := soap.ParseURL(vcenterURL)
	if err != nil {
		return nil, errors.Wrap(err, "error parsing URL")
	}
	u.User = url.UserPassword(user, pass)
	return govmomi.NewClient(ctx, u, insecure)
}
func Stream(ctx context.Context, c *vim25.Client, controller ofsdk.Controller) error {
	m := event.NewManager(c)
	managedTypes := []vtypes.ManagedObjectReference{c.ServiceContent.RootFolder}
	eventsPerPage := int32(1)
	tail := true
	force := true
	source := c.URL().Host
	recv := makeRecv(controller, m, source)
	err := m.Events(ctx, managedTypes, eventsPerPage, tail, force, recv)
	if err != nil {
		return errors.Wrap(err, "error connecting to event-stream")
	}
	return nil
}
func makeRecv(controller ofsdk.Controller, m *event.Manager, source string) func(managedObjectReference vtypes.ManagedObjectReference, baseEvent []vtypes.BaseEvent) error {
	return func(managedObjectReference vtypes.ManagedObjectReference, baseEvent []vtypes.BaseEvent) error {
		log.Printf("Object %v", managedObjectReference)
		for i, event := range baseEvent {
			log.Printf("Event [%d] %v", i, event)
			topic, message, err := handleEvent(event, m, source)
			if err != nil {
				log.Printf("error handling event: %s", err.Error())
				continue
			}
			binaryMsg := []byte(message)
			log.Printf("Message on topic: %s", topic)
			controller.Invoke(topic, &binaryMsg)
		}
		return nil
	}
}
func handleEvent(event vtypes.BaseEvent, m *event.Manager, source string) (string, string, error) {
	if event == nil {
		return "", "", errors.New("event must not be nil")
	}
	eventType := reflect.TypeOf(event).Elem().Name()
	topic := convertToTopic(eventType)
	user := event.GetEvent().UserName
	createdTime := event.GetEvent().CreatedTime
	category, err := m.EventCategory(context.Background(), event)
	if err != nil {
		return "", "", errors.Wrap(err, "error retrieving event category")
	}
	name, ref := getObjectNameAndMoref(event)
	message, err := json.Marshal(OutboundEvent{
		Topic:                  topic,
		Category:               category,
		UserName:               user,
		CreatedTime:            createdTime,
		ObjectName:             name,
		ManagedObjectReference: ref,
		Source:                 source,
	})
	if err != nil {
		return "", "", errors.Wrap(err, "error marshaling outboundevent")
	}
	log.Printf("message: %s", string(message))
	return topic, string(message), nil
}
func getObjectNameAndMoref(event vtypes.BaseEvent) (string, *vtypes.ManagedObjectReference) {
	var ref *vtypes.ManagedObjectReference
	var objName string
	switch baseEvent := event.(type) {
	case vtypes.BaseAlarmEvent:
		e := baseEvent.GetAlarmEvent()
		objName = e.Alarm.Name
		ref = &e.Alarm.Alarm
	case vtypes.BaseDatastoreEvent:
		e := baseEvent.GetDatastoreEvent()
		objName = e.Datastore.Name
		ref = &e.Datastore.Datastore
	case vtypes.BaseHostEvent:
		e := baseEvent.GetHostEvent()
		objName = e.Host.Name
		ref = &e.Host.Host
	case vtypes.BaseResourcePoolEvent:
		e := baseEvent.GetResourcePoolEvent()
		objName = e.ResourcePool.Name
		ref = &e.ResourcePool.ResourcePool
	case vtypes.BaseVmEvent:
		e := baseEvent.GetVmEvent()
		objName = e.Vm.Name
		ref = &e.Vm.Vm
	}
	return objName, ref
}
func convertToTopic(eventType string) string {
	eventType = strings.Replace(eventType, "Event", "", -1)
	return camelCaseToLowerSeparated(eventType, ".")
}