Skip to content

Commit 13cb26d

Browse files
committed
Initial
Initial working example triggering with a minimum set of data and examples in README. Signed-off-by: Alex Ellis (VMware) <[email protected]>
1 parent 386f75b commit 13cb26d

File tree

154 files changed

+103704
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+103704
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vcenter-connector

Gopkg.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Gopkg.toml example
2+
#
3+
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
4+
# for detailed Gopkg.toml documentation.
5+
#
6+
# required = ["github.com/user/thing/cmd/thing"]
7+
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
8+
#
9+
# [[constraint]]
10+
# name = "github.com/user/project"
11+
# version = "1.0.0"
12+
#
13+
# [[constraint]]
14+
# name = "github.com/user/project2"
15+
# branch = "dev"
16+
# source = "github.com/myfork/project2"
17+
#
18+
# [[override]]
19+
# name = "github.com/x/y"
20+
# version = "2.4.0"
21+
#
22+
# [prune]
23+
# non-go = false
24+
# go-tests = true
25+
# unused-packages = true
26+
27+
28+
[prune]
29+
go-tests = true
30+
unused-packages = true
31+
32+
[[constraint]]
33+
name = "github.com/openfaas-incubator/connector-sdk"
34+
version = "0.2.0"
35+
36+
[[constraint]]
37+
name = "github.com/vmware/govmomi"
38+
version = "0.19.0"

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 openfaas-incubator
3+
Copyright (c) 2019 OpenFaaS Author(s)
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,55 @@
11
# vcenter-connector
2+
23
OpenFaaS event connector for VMware vCenter
4+
5+
## Status
6+
7+
This project connects VMware vCenter events to OpenFaaS functions via topics applied in the "topic" annotation.
8+
9+
It is build using the [OpenFaaS Connector SDK](https://github.com/openfaas-incubator/connector-sdk)
10+
11+
The code is under active development and only suitable for testing at this point.
12+
13+
## Example:
14+
15+
* Run the vCenter Simulator
16+
17+
```bash
18+
./vcsim -tls=false
19+
```
20+
21+
* Run the connector
22+
23+
```bash
24+
export OPENFAAS_URL=http://127.0.0.1:31112
25+
go run main.go -vcenter-url="http://user:[email protected]:8989/sdk" -insecure
26+
```
27+
28+
Deploy an echo function that subscribes to the event of "vm.powered.on"
29+
30+
```bash
31+
faas-cli deploy --annotation topic="vm.powered.on" --fprocess=/bin/cat -e write_debug=true --image=functions/alpine:latest --name vmware
32+
```
33+
34+
* Generate some events:
35+
36+
```
37+
GOVC_INSECURE=true GOVC_URL=http://user:[email protected]:8989/sdk govc vm.power -off '*'
38+
```
39+
40+
* Check the logs of the vmware function
41+
42+
```
43+
kubectl logs -n openfaas-fn deploy/vmware
44+
docker service logs vmware
45+
```
46+
47+
## License
48+
49+
MIT
50+
51+
## Acknowledgements
52+
53+
Thanks to VMware's Doug MacEachern for the awesome [govmomi](https://github.com/vmware/govmomi) project providing Golang bindings for vCenter and the [vcsim simulator tool](https://github.com/vmware/govmomi/blob/master/vcsim/README.md).
54+
55+
Thanks to Karol Stępniewski for showing me a demo of events being consumed in OpenFaaS via vCenter over a year ago at KubeCon in Austin. Parts of his "event-driver" originally developed in the Dispatch project have been adapted for this OpenFaaS event-connector including a method to convert camel case event names into names separated by a dot. I wanted to include this for compatibility between the two systems.

main.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"flag"
7+
"log"
8+
"os"
9+
"reflect"
10+
"strings"
11+
"time"
12+
"unicode"
13+
14+
"github.com/openfaas-incubator/connector-sdk/types"
15+
"github.com/openfaas/faas-provider/auth"
16+
"github.com/pkg/errors"
17+
"github.com/vmware/govmomi"
18+
"github.com/vmware/govmomi/event"
19+
"github.com/vmware/govmomi/vim25"
20+
"github.com/vmware/govmomi/vim25/soap"
21+
vtypes "github.com/vmware/govmomi/vim25/types"
22+
)
23+
24+
func main() {
25+
var vcenterURL string
26+
var insecure bool
27+
28+
flag.StringVar(&vcenterURL, "vcenter-url", "", "URL for vcenter user:[email protected]:port")
29+
flag.Bool("insecure", true, "use an insecure connection")
30+
flag.Parse()
31+
32+
if len(vcenterURL) == 0 {
33+
panic("vcenterURL not provided")
34+
}
35+
36+
vcenterClient, vcenterErr := newVCenterClient(context.Background(), vcenterURL, insecure)
37+
38+
if vcenterErr != nil {
39+
panic(vcenterErr)
40+
}
41+
42+
var credentials *auth.BasicAuthCredentials
43+
44+
if val, ok := os.LookupEnv("basic_auth"); ok && len(val) > 0 {
45+
if val == "true" || val == "1" {
46+
47+
reader := auth.ReadBasicAuthFromDisk{}
48+
49+
if val, ok := os.LookupEnv("secret_mount_path"); ok && len(val) > 0 {
50+
reader.SecretMountPath = os.Getenv("secret_mount_path")
51+
}
52+
53+
res, err := reader.Read()
54+
if err != nil {
55+
panic(err)
56+
}
57+
credentials = res
58+
}
59+
}
60+
61+
config := types.ControllerConfig{
62+
GatewayURL: os.Getenv("OPENFAAS_URL"),
63+
PrintResponse: true,
64+
RebuildInterval: time.Second * 10,
65+
UpstreamTimeout: time.Second * 15,
66+
}
67+
68+
controller := types.NewController(credentials, &config)
69+
70+
controller.BeginMapBuilder()
71+
72+
err := bindEvents(vcenterClient.Client, controller)
73+
74+
if err != nil {
75+
panic(err)
76+
}
77+
}
78+
79+
func bindEvents(c *vim25.Client, controller *types.Controller) error {
80+
81+
ctx, cancel := context.WithCancel(context.Background())
82+
defer cancel()
83+
84+
m := event.NewManager(c)
85+
managedTypes := []vtypes.ManagedObjectReference{c.ServiceContent.RootFolder}
86+
eventsPerPage := int32(1)
87+
tail := true
88+
force := true
89+
90+
recv := makeRecv(controller, m)
91+
go func() {
92+
err := m.Events(ctx, managedTypes, eventsPerPage, tail, force, recv)
93+
if err != nil {
94+
log.Printf("error connecting to event-stream: %v", err.Error())
95+
cancel()
96+
}
97+
}()
98+
99+
done := make(chan bool)
100+
101+
<-done
102+
103+
// controller.Invoke()
104+
105+
return nil
106+
}
107+
108+
func makeRecv(controller *types.Controller, m *event.Manager) func(managedObjectReference vtypes.ManagedObjectReference, baseEvent []vtypes.BaseEvent) error {
109+
return func(managedObjectReference vtypes.ManagedObjectReference, baseEvent []vtypes.BaseEvent) error {
110+
log.Printf("Object %v", managedObjectReference)
111+
112+
for i, event := range baseEvent {
113+
log.Printf("Event [%d] %v", i, event)
114+
115+
topic, message, err := handleEvent(event, m)
116+
if err != nil {
117+
log.Printf("error handling event: %s", err.Error())
118+
continue
119+
}
120+
binaryMsg := []byte(message)
121+
log.Printf("Message on topic: %s", topic)
122+
controller.Invoke(topic, &binaryMsg)
123+
124+
}
125+
return nil
126+
}
127+
}
128+
129+
func handleEvent(event vtypes.BaseEvent, m *event.Manager) (string, string, error) {
130+
eventType := reflect.TypeOf(event).Elem().Name()
131+
132+
category, err := m.EventCategory(context.Background(), event)
133+
if err != nil {
134+
return "", "", errors.Wrap(err, "error retrieving event category")
135+
}
136+
137+
log.Printf("category: %v, event type: %v", category, eventType)
138+
139+
topic := convertToTopic(eventType)
140+
141+
message, _ := json.Marshal(OutboundEvent{
142+
Topic: topic,
143+
Category: category,
144+
})
145+
146+
return topic, string(message), nil
147+
}
148+
149+
type OutboundEvent struct {
150+
Topic string
151+
Category string
152+
}
153+
154+
func newVCenterClient(ctx context.Context, vcenterURL string, insecure bool) (*govmomi.Client, error) {
155+
url, err := soap.ParseURL(vcenterURL)
156+
if err != nil {
157+
return nil, err
158+
}
159+
160+
return govmomi.NewClient(ctx, url, insecure)
161+
}
162+
163+
func convertToTopic(eventType string) string {
164+
165+
eventType = strings.Replace(eventType, "Event", "", -1)
166+
return camelCaseToLowerSeparated(eventType, ".")
167+
}
168+
169+
// From https://github.com/vmware/dispatch/blob/master/pkg/utils/str_convert.go
170+
// camelCaseToLowerSeparated converts a camel cased string to a multi-word string delimited by the specified separator
171+
func camelCaseToLowerSeparated(src string, sep string) string {
172+
var words []string
173+
var word []rune
174+
var last rune
175+
for _, r := range src {
176+
if unicode.IsUpper(r) {
177+
if unicode.IsUpper(last) {
178+
// We have two uppercase letters in a row, it might be uppercase word like ID or SDK
179+
word = append(word, r)
180+
} else {
181+
// We have uppercase after lowercase, which always means start of a new word
182+
if len(word) > 0 {
183+
words = append(words, strings.ToLower(string(word)))
184+
}
185+
word = []rune{r}
186+
}
187+
} else {
188+
if unicode.IsUpper(last) && len(word) >= 2 {
189+
// We have a multi-uppercase word followed by an another word, e.g. "SDKToString",
190+
// but word variable contains "SDKT". We need to extract "T" as a first letter of a new word
191+
words = append(words, strings.ToLower(string(word[:len(word)-1])))
192+
word = []rune{last, r}
193+
} else {
194+
word = append(word, r)
195+
}
196+
}
197+
last = r
198+
}
199+
if len(word) > 0 {
200+
words = append(words, strings.ToLower(string(word)))
201+
}
202+
return strings.Join(words, sep)
203+
}

vendor/github.com/openfaas-incubator/connector-sdk/LICENSE

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)