Repository: vmware/govmomi (go)

Issue:
[BUG] vcsim: ExtensionManager:ExtensionManager does not implement: FindExtension
Hello

From a Java VSphere client I'm trying to connect to a mocked VCenter I'm getting this exception:

```
com.vmware.vim25.MethodNotFound
```

On vcsim output the message is:

```
yyyy/mm/dd HH:MM:SS ExtensionManager:ExtensionManager does not implement: FindExtension
```

My code use

```
com.vmware.vim25.mo.ExtensionManager em ;
com.vmware.vim25.Extension extension  = em.findExtension("com.vmware.vcDr");
```

Something wrong somewhere ?


Pull request description:
vcsim: add ExtensionManager.FindExtension method
d9af2a2 added simulator.ExtensionManager but did not included this method.

govc: use ExtensionManager.FindExtension when given a single KEY arg, rather than fetch the entire list.

Closes #2644


Patch:
diff --git a/govc/extension/info.go b/govc/extension/info.go
index de88b43d..1a15efaf 100644
--- a/govc/extension/info.go
+++ b/govc/extension/info.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved.
+Copyright (c) 2015-2024 VMware, Inc. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import (
 	"flag"
 	"fmt"
 	"io"
-	"os"
 	"text/tabwriter"
 
 	"github.com/vmware/govmomi/govc/cli"
@@ -72,30 +71,39 @@ func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
 		return err
 	}
 
-	list, err := m.List(ctx)
-	if err != nil {
-		return err
-	}
-
 	var res infoResult
+	exts := make(map[string]types.Extension)
 
-	if f.NArg() == 0 {
-		res.Extensions = list
+	if f.NArg() == 1 {
+		e, err := m.Find(ctx, f.Arg(0))
+		if err != nil {
+			return err
+		}
+		if e != nil {
+			exts[f.Arg(0)] = *e
+		}
 	} else {
-		exts := make(map[string]types.Extension)
-		for _, e := range list {
-			exts[e.Key] = e
+		list, err := m.List(ctx)
+		if err != nil {
+			return err
 		}
-
-		for _, key := range f.Args() {
-			if e, ok := exts[key]; ok {
-				res.Extensions = append(res.Extensions, e)
-			} else {
-				return fmt.Errorf("extension %s not found", key)
+		if f.NArg() == 0 {
+			res.Extensions = list
+		} else {
+			for _, e := range list {
+				exts[e.Key] = e
 			}
 		}
 	}
 
+	for _, key := range f.Args() {
+		if e, ok := exts[key]; ok {
+			res.Extensions = append(res.Extensions, e)
+		} else {
+			return fmt.Errorf("extension %s not found", key)
+		}
+	}
+
 	return cmd.WriteResult(&res)
 }
 
@@ -104,7 +112,7 @@ type infoResult struct {
 }
 
 func (r *infoResult) Write(w io.Writer) error {
-	tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
+	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
 
 	for _, e := range r.Extensions {
 		fmt.Fprintf(tw, "Name:\t%s\n", e.Key)
diff --git a/simulator/extension_manager.go b/simulator/extension_manager.go
index 60a46c41..6b72ae0f 100644
--- a/simulator/extension_manager.go
+++ b/simulator/extension_manager.go
@@ -67,6 +67,21 @@ func (m *ExtensionManager) init(r *Registry) {
 	}
 }
 
+func (m *ExtensionManager) FindExtension(ctx *Context, req *types.FindExtension) soap.HasFault {
+	body := &methods.FindExtensionBody{
+		Res: new(types.FindExtensionResponse),
+	}
+
+	for _, x := range m.ExtensionList {
+		if x.Key == req.ExtensionKey {
+			body.Res.Returnval = &x
+			break
+		}
+	}
+
+	return body
+}
+
 func (m *ExtensionManager) RegisterExtension(ctx *Context, req *types.RegisterExtension) soap.HasFault {
 	body := &methods.RegisterExtensionBody{}
 


Test patch:
diff --git a/govc/test/extension.bats b/govc/test/extension.bats
index 0c20b819..8612e831 100755
--- a/govc/test/extension.bats
+++ b/govc/test/extension.bats
@@ -8,10 +8,10 @@ load test_helper
   run govc extension.info enoent
   assert_failure
 
-  id=$(new_id)
+  run govc extension.info
+  assert_success
 
-  result=$(govc extension.info | grep $id | wc -l)
-  [ $result -eq 0 ]
+  id=$(new_id)
 
   # register extension
   run govc extension.register $id <<EOS
@@ -54,9 +54,12 @@ EOS
   # remove generated cert and key
   rm ${id}.{crt,key}
 
+  run govc extension.info $(govc extension.info -json | jq -r .extensions[].key)
+  assert_success
+
   run govc extension.unregister $id
   assert_success
 
-  result=$(govc extension.info | grep $id | wc -l)
-  [ $result -eq 0 ]
+  run govc extension.info $id
+  assert_failure
 }