Repository: vmware/govmomi (go)

Issue:
Vcsim - CloneVapp_Task seems not implemented
Hi, 
when I try to clone a VApp on vcsim I get the following response:

```
vcsim_1        | Request: <?xml version="1.0" encoding="utf-8"?>
                            <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                                <soap:Body>
                                    <CloneVApp_Task xmlns="urn:vim25">
                                        <_this type="VirtualApp">resgroup-v639</_this>
                                        <name>1_1</name>
                                        <target type="ResourcePool">resgroup-186</target>
                                        <spec>
                                            <location type="Datastore">datastore-187</location>
                                            <host type="HostSystem">host-183</host>
                                            <vmFolder type="Folder">group-v635</vmFolder>
                                            <networkMapping>
                                                <source type="Network">network-307</source>
                                                <destination type="Network">network-2</destination>
                                            </networkMapping>
                                        </spec>
                                    </CloneVApp_Task>
                                </soap:Body>
                            </soap:Envelope>
vcsim_1        | 2020/06/22 14:55:55 VirtualApp:resgroup-v639 does not implement: CloneVApp_Task
vcsim_1        | Response: <?xml version="1.0" encoding="UTF-8"?>
vcsim_1        |                <soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                                    <soapenv:Body>
                                        <soapenv:Fault>
                                            <faultcode>ServerFaultCode</faultcode>
                                            <faultstring>VirtualApp:resgroup-v639 does not implement: CloneVApp_Task</faultstring>
                                            <detail>
                                                <MethodNotFoundFault xmlns="urn:vim25" xsi:type="MethodNotFound">
                                                    <receiver type="VirtualApp">resgroup-v639</receiver>
                                                    <method>CloneVApp_Task</method>
                                                </MethodNotFoundFault>
                                            </detail>
                                        </soapenv:Fault>
                                    </soapenv:Body>
                                </soapenv:Envelope>
```
Can you help me on this issue?
Thanks




Pull request description:
vcsim: add CloneVApp_Task support
Fixes #2028

Patch:
diff --git a/object/virtual_app.go b/object/virtual_app.go
index 4811178f..b7311b3e 100644
--- a/object/virtual_app.go
+++ b/object/virtual_app.go
@@ -103,3 +103,19 @@ func (p VirtualApp) Suspend(ctx context.Context) (*Task, error) {
 
 	return NewTask(p.c, res.Returnval), nil
 }
+
+func (p VirtualApp) Clone(ctx context.Context, name string, target types.ManagedObjectReference, spec types.VAppCloneSpec) (*Task, error) {
+	req := types.CloneVApp_Task{
+		This:   p.Reference(),
+		Name:   name,
+		Target: target,
+		Spec:   spec,
+	}
+
+	res, err := methods.CloneVApp_Task(ctx, p.c, &req)
+	if err != nil {
+		return nil, err
+	}
+
+	return NewTask(p.c, res.Returnval), nil
+}
diff --git a/simulator/resource_pool.go b/simulator/resource_pool.go
index aefe9b7c..f5180427 100644
--- a/simulator/resource_pool.go
+++ b/simulator/resource_pool.go
@@ -361,6 +361,68 @@ func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Ta
 	return body
 }
 
+func (a *VirtualApp) CloneVAppTask(ctx *Context, req *types.CloneVApp_Task) soap.HasFault {
+	task := CreateTask(a, "cloneVapp", func(t *Task) (types.AnyType, types.BaseMethodFault) {
+		folder := req.Spec.VmFolder
+		if folder == nil {
+			folder = a.ParentFolder
+		}
+
+		rspec := req.Spec.ResourceSpec
+		if rspec == nil {
+			s := types.DefaultResourceConfigSpec()
+			rspec = &s
+		}
+
+		res := a.CreateVApp(&types.CreateVApp{
+			This:       a.Self,
+			Name:       req.Name,
+			ResSpec:    *rspec,
+			ConfigSpec: types.VAppConfigSpec{},
+			VmFolder:   folder,
+		})
+
+		if res.Fault() != nil {
+			return nil, res.Fault().VimFault().(types.BaseMethodFault)
+		}
+
+		target := res.(*methods.CreateVAppBody).Res.Returnval
+
+		for _, ref := range a.Vm {
+			vm := ctx.Map.Get(ref).(*VirtualMachine)
+
+			res := vm.CloneVMTask(ctx, &types.CloneVM_Task{
+				This:   ref,
+				Folder: *folder,
+				Name:   req.Name,
+				Spec: types.VirtualMachineCloneSpec{
+					Location: types.VirtualMachineRelocateSpec{
+						Pool: &target,
+						Host: req.Spec.Host,
+					},
+				},
+			})
+
+			ctask := Map.Get(res.(*methods.CloneVM_TaskBody).Res.Returnval).(*Task)
+			if ctask.Info.Error != nil {
+				return nil, ctask.Info.Error.Fault
+			}
+		}
+
+		return target, nil
+	})
+
+	return &methods.CloneVApp_TaskBody{
+		Res: &types.CloneVApp_TaskResponse{
+			Returnval: task.Run(),
+		},
+	}
+}
+
+func (a *VirtualApp) CreateVApp(req *types.CreateVApp) soap.HasFault {
+	return (&ResourcePool{ResourcePool: a.ResourcePool}).CreateVApp(req)
+}
+
 func (a *VirtualApp) DestroyTask(req *types.Destroy_Task) soap.HasFault {
 	return (&ResourcePool{ResourcePool: a.ResourcePool}).DestroyTask(req)
 }


Test patch:
diff --git a/simulator/resource_pool_test.go b/simulator/resource_pool_test.go
index 0996177c..4c19802b 100644
--- a/simulator/resource_pool_test.go
+++ b/simulator/resource_pool_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/google/uuid"
 	"github.com/vmware/govmomi/find"
 	"github.com/vmware/govmomi/object"
+	"github.com/vmware/govmomi/property"
 	"github.com/vmware/govmomi/simulator/esx"
 	"github.com/vmware/govmomi/vim25/mo"
 	"github.com/vmware/govmomi/vim25/soap"
@@ -206,8 +207,8 @@ func TestCreateVAppVPX(t *testing.T) {
 
 	c := m.Service.client
 
-	parent := object.NewResourcePool(c, Map.Any("ResourcePool").Reference())
-
+	pool := Map.Any("ResourcePool")
+	parent := object.NewResourcePool(c, pool.Reference())
 	rspec := types.DefaultResourceConfigSpec()
 	vspec := NewVAppConfigSpec()
 
@@ -258,7 +259,44 @@ func TestCreateVAppVPX(t *testing.T) {
 		t.Errorf("FindChild(%s)==nil", spec.Name)
 	}
 
-	task, err := vapp.Destroy(ctx)
+	ref := Map.Get(Map.getEntityDatacenter(pool).VmFolder).Reference()
+	folder, err := object.NewFolder(c, ref).CreateFolder(ctx, "myapp-clone")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cspec := types.VAppCloneSpec{
+		VmFolder: types.NewReference(folder.Reference()),
+	}
+
+	task, err := vapp.Clone(ctx, "myapp-clone", parent.Reference(), cspec)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	res, err := task.WaitForResult(ctx, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var apps []mo.VirtualApp
+	refs := []types.ManagedObjectReference{vapp.Reference(), res.Result.(types.ManagedObjectReference)}
+	err = property.DefaultCollector(c).Retrieve(ctx, refs, []string{"vm"}, &apps)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(apps) != 2 {
+		t.Errorf("apps=%d", len(apps))
+	}
+
+	for _, app := range apps {
+		if len(app.Vm) != 1 {
+			t.Errorf("app %s vm=%d", app.Reference(), len(app.Vm))
+		}
+	}
+
+	task, err = vapp.Destroy(ctx)
 	if err != nil {
 		t.Fatal(err)
 	}