diff --git a/client/vsphere.go b/client/vsphere.go index 8ae731b..c49e642 100644 --- a/client/vsphere.go +++ b/client/vsphere.go @@ -68,6 +68,9 @@ func readKey(key string) (string, error) { // keep-alive configured via environment variables. Commonly used managers are // exposed for quick access. // +// A custom logger (zap.Logger) can be injected into the context via the logger +// package. +// // Use Logout() to release resources and perform a clean logout from vCenter. func New(ctx context.Context) (*Client, error) { vclient, err := NewSOAP(ctx) diff --git a/client/vsphere_test.go b/client/vsphere_test.go index 174108b..e4e54a8 100644 --- a/client/vsphere_test.go +++ b/client/vsphere_test.go @@ -37,7 +37,8 @@ func TestNewClient(t *testing.T) { "VCENTER_INSECURE": "", "VCENTER_SECRET_PATH": "", }, - wantErr: "certificate signed by unknown authority", + // different go versions throw different errors, this is a generic catch all + wantErr: "create vsphere SOAP client: Post", }, } diff --git a/example/main.go b/example/main.go index 0010734..0a19145 100644 --- a/example/main.go +++ b/example/main.go @@ -2,15 +2,17 @@ package main import ( "context" + "errors" "os" "os/signal" "syscall" + "time" - "github.com/vmware/govmomi/event" - "github.com/vmware/govmomi/vim25/types" "go.uber.org/zap" + "go.uber.org/zap/zapcore" "github.com/embano1/vsphere/client" + "github.com/embano1/vsphere/event" "github.com/embano1/vsphere/logger" ) @@ -18,10 +20,14 @@ func main() { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt) defer cancel() - l, err := zap.NewDevelopment() + cfg := zap.NewDevelopmentConfig() + cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + + l, err := cfg.Build() if err != nil { panic("create logger: " + err.Error()) } + l = l.Named("demo-app") // inject and overwrite default logger ctx = logger.Set(ctx, l) @@ -29,21 +35,54 @@ func main() { l.Debug("creating vsphere client") c, err := client.New(ctx) if err != nil { - l.Fatal("create vsphere client", zap.Error(err)) + l.Fatal("could not create vsphere client", zap.Error(err)) } + defer c.Logout() - mgr := event.NewManager(c.SOAP.Client) - objs := []types.ManagedObjectReference{c.SOAP.ServiceContent.RootFolder} - handler := func(reference types.ManagedObjectReference, events []types.BaseEvent) error { - for _, e := range events { - l.Debug("new event", zap.Any("event", e)) - } - return nil + // show how to use filters + filterEvents := []string{"UserLoginSessionEvent", "VmPoweredOnEvent", "DrsVmPoweredOnEvent"} + filters := []event.Filter{event.WithEventTypeID(filterEvents)} + + // retrieve (filtered) events for all vCenter objects + root := c.SOAP.ServiceContent.RootFolder + collector, err := event.NewHistoryCollector(ctx, c.Events, root, filters...) + if err != nil { + l.Fatal("could not create event stream", zap.Error(err)) } - l.Debug("starting event stream") - if err = mgr.Events(ctx, objs, 10, true, false, handler); err != nil { - l.Fatal("stream events", zap.Error(err)) + const ( + batch = 10 + pollInterval = time.Second * 3 + ) + + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + l.Info("starting event stream", zap.Any("forEvents", filterEvents)) +LOOP: + for { + select { + case <-ctx.Done(): + l.Debug("shutting down") + break LOOP + + case <-ticker.C: + l.Debug("retrieving events") + events, err := collector.ReadNextEvents(ctx, batch) + if err != nil && !errors.Is(err, context.Canceled) { + l.Fatal("could not read events", zap.Error(err)) + } + + if len(events) == 0 { + l.Debug("no new events") + continue + } + + for _, e := range events { + l.Info("retrieved new event", zap.Any("event", e)) + } + } } + l.Debug("shutdown complete") }