github.com/vmware/govmomi@v0.37.1/eam/simulator/simulator_test.go (about)

     1  /*
     2  Copyright (c) 2021 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package simulator_test
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"os/signal"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/vmware/govmomi/eam"
    30  	"github.com/vmware/govmomi/eam/object"
    31  	"github.com/vmware/govmomi/eam/types"
    32  	"github.com/vmware/govmomi/find"
    33  	vimobject "github.com/vmware/govmomi/object"
    34  	"github.com/vmware/govmomi/session"
    35  	vcsim "github.com/vmware/govmomi/simulator"
    36  	"github.com/vmware/govmomi/vim25"
    37  	"github.com/vmware/govmomi/vim25/soap"
    38  	vim "github.com/vmware/govmomi/vim25/types"
    39  
    40  	// making sure the SDK endpoint is registered
    41  	_ "github.com/vmware/govmomi/eam/simulator"
    42  )
    43  
    44  const waitLoopMessage = `
    45  
    46  ################################################################################
    47  # When executed with the flags -powerOnVMs and -waitForExit, this test will    #
    48  # pause here and update the screen with the status of the agent VMs until      #
    49  # SIGINT is sent to this process.                                              #
    50  ################################################################################
    51  `
    52  
    53  var (
    54  	flagPowerOnVMs = flag.Bool("powerOnVMs", false, "Powers on the VMs in the test with Docker")
    55  	flagWaitToExit = flag.Bool("waitToExit", false, "Waits for user input to exit the test")
    56  )
    57  
    58  func TestSimulator(t *testing.T) {
    59  	vcsim.Test(func(ctx context.Context, vimClient *vim25.Client) {
    60  		// Create a finder that sets the default datacenter.
    61  		finder := find.NewFinder(vimClient, true)
    62  
    63  		// Get the datacenter to use when creating the agency.
    64  		datacenter, err := finder.DefaultDatacenter(ctx)
    65  		if err != nil {
    66  			t.Fatal(err)
    67  		}
    68  		finder.SetDatacenter(datacenter)
    69  
    70  		// Get the "vm" folder.
    71  		folder, err := finder.DefaultFolder(ctx)
    72  		if err != nil {
    73  			t.Fatal(err)
    74  		}
    75  
    76  		// Get the cluster to use when creating the agency.
    77  		computeResource, err := finder.ClusterComputeResourceOrDefault(ctx, "")
    78  		if err != nil {
    79  			t.Fatal(err)
    80  		}
    81  
    82  		// Get the resource pool to use when creating the agency.
    83  		pool, err := computeResource.ResourcePool(ctx)
    84  		if err != nil {
    85  			t.Fatal(err)
    86  		}
    87  
    88  		// Get the datastore to use when creating the agency.
    89  		datastore, err := finder.DatastoreOrDefault(ctx, "")
    90  		if err != nil {
    91  			t.Fatal(err)
    92  		}
    93  
    94  		// Get the network to use when creating the agency.
    95  		network, err := finder.NetworkOrDefault(ctx, "DVS0")
    96  		if err != nil {
    97  			t.Fatal(err)
    98  		}
    99  
   100  		// Get an EAM client.
   101  		eamClient := eam.NewClient(vimClient)
   102  
   103  		// Get the EAM root object.
   104  		mgr := object.NewEsxAgentManager(eamClient, eam.EsxAgentManager)
   105  
   106  		// Define a function that will list and print the agency MoRefs.
   107  		listAgencies := func() int {
   108  			t.Log("listing agencies")
   109  			agencies, err := mgr.Agencies(ctx)
   110  			if err != nil {
   111  				t.Fatal(err)
   112  			}
   113  			if len(agencies) == 0 {
   114  				t.Log("no agencies")
   115  				return 0
   116  			}
   117  			for _, obj := range agencies {
   118  				t.Logf("agency: %v", obj.Reference())
   119  				config, err := obj.Config(ctx)
   120  				if err != nil {
   121  					t.Fatal(err)
   122  				}
   123  				t.Logf("agency config: %+v", config)
   124  				runtime, err := obj.Runtime(ctx)
   125  				if err != nil {
   126  					t.Fatal(err)
   127  				}
   128  				t.Logf("agency runtime: %+v", runtime)
   129  
   130  				agents, err := obj.Agents(ctx)
   131  				if err != nil {
   132  					t.Fatal(err)
   133  				}
   134  				if len(agents) == 0 {
   135  					t.Log("no agents")
   136  				} else {
   137  					for _, a := range agents {
   138  						t.Logf("agent: %v", a.Reference())
   139  						config, err := a.Config(ctx)
   140  						if err != nil {
   141  							t.Fatal(err)
   142  						}
   143  						t.Logf("agent config: %+v", config)
   144  						runtime, err := a.Runtime(ctx)
   145  						if err != nil {
   146  							t.Fatal(err)
   147  						}
   148  						t.Logf("agent runtime: %+v", runtime)
   149  					}
   150  				}
   151  			}
   152  			return len(agencies)
   153  		}
   154  
   155  		// List and print the agency MoRefs. There are none.
   156  		if listAgencies() > 0 {
   157  			t.Fatal("no agencies expected")
   158  		}
   159  
   160  		// Create a new agency.
   161  		t.Log("creating a new agency")
   162  		agency, err := mgr.CreateAgency(
   163  			ctx,
   164  			&types.AgencyConfigInfo{
   165  				AgencyName: "nginx",
   166  				AgentVmDatastore: []vim.ManagedObjectReference{
   167  					datastore.Reference(),
   168  				},
   169  				Folders: []types.AgencyVMFolder{
   170  					{
   171  						FolderId:     folder.Reference(),
   172  						DatacenterId: datacenter.Reference(),
   173  					},
   174  				},
   175  				ResourcePools: []types.AgencyVMResourcePool{
   176  					{
   177  						ResourcePoolId:    pool.Reference(),
   178  						ComputeResourceId: computeResource.Reference(),
   179  					},
   180  				},
   181  				AgentVmNetwork: []vim.ManagedObjectReference{
   182  					network.Reference(),
   183  				},
   184  				AgentConfig: []types.AgentConfigInfo{
   185  					{
   186  						OvfPackageUrl: "nginx",
   187  					},
   188  					{
   189  						OvfPackageUrl: "nginx",
   190  					},
   191  				},
   192  			},
   193  			string(types.EamObjectRuntimeInfoGoalStateEnabled),
   194  		)
   195  		if err != nil {
   196  			if soap.IsSoapFault(err) {
   197  				fault := soap.ToSoapFault(err).VimFault()
   198  				t.Fatalf("%[1]T %[1]v", fault)
   199  			} else {
   200  				t.Fatalf("%[1]T %[1]v", err)
   201  			}
   202  		}
   203  		t.Logf("created agency: %v", agency.Reference())
   204  
   205  		// List the agencies again, and this time the newly created agency will be
   206  		// printed to the console.
   207  		if listAgencies() != 1 {
   208  			t.Fatal("one agency expected")
   209  		}
   210  
   211  		// Check whether or not we want to power on the VMs with Docker.
   212  		if *flagPowerOnVMs {
   213  			agencies, err := mgr.Agencies(ctx)
   214  			if err != nil {
   215  				t.Fatal(err)
   216  			}
   217  			if len(agencies) == 0 {
   218  				t.Fatal("no agencies")
   219  			}
   220  			agency := agencies[0]
   221  
   222  			// Wait for the agent VMs to have IP addresses
   223  			{
   224  				agents, err := agency.Agents(ctx)
   225  				if err != nil {
   226  					t.Fatal(err)
   227  				}
   228  				if len(agents) == 0 {
   229  					t.Fatal("no agents")
   230  				}
   231  
   232  				wait := make(chan struct{})
   233  				done := make(chan struct{})
   234  				errs := make(chan error)
   235  				msgs := make(chan string)
   236  				var wg sync.WaitGroup
   237  				wg.Add(len(agents))
   238  
   239  				for _, agent := range agents {
   240  					agent := agent
   241  					go func() {
   242  						var (
   243  							vmPowerState string
   244  							vmIp         string
   245  							once         sync.Once
   246  							vmon         = map[vim.ManagedObjectReference]struct{}{}
   247  						)
   248  						for {
   249  							runtime, err := agent.Runtime(ctx)
   250  							if err != nil {
   251  								errs <- err
   252  								return
   253  							}
   254  							if runtime.Vm == nil {
   255  								errs <- fmt.Errorf("vm is nil for agent %s", agent.Reference())
   256  								return
   257  							}
   258  							if _, ok := vmon[*runtime.Vm]; !ok {
   259  								vm := vimobject.NewVirtualMachine(vimClient, *runtime.Vm)
   260  								if _, err := vm.PowerOn(ctx); err != nil {
   261  									errs <- err
   262  									return
   263  								}
   264  								vmon[*runtime.Vm] = struct{}{}
   265  							}
   266  
   267  							if vmIp != runtime.VmIp || vmPowerState != string(runtime.VmPowerState) {
   268  								vmIp = runtime.VmIp
   269  								vmPowerState = string(runtime.VmPowerState)
   270  								msgs <- fmt.Sprintf(
   271  									"%v: name=%s, powerState=%s, ipAddr=%s",
   272  									*runtime.Vm,
   273  									runtime.VmName,
   274  									vmPowerState,
   275  									vmIp)
   276  							}
   277  							if vmIp != "" {
   278  								once.Do(func() {
   279  									wg.Done()
   280  								})
   281  							}
   282  							select {
   283  							case <-time.After(1 * time.Second):
   284  							case <-done:
   285  								return
   286  							}
   287  						}
   288  					}()
   289  				}
   290  
   291  				go func() {
   292  					wg.Wait()
   293  					if *flagWaitToExit {
   294  						<-wait
   295  					}
   296  					close(done)
   297  				}()
   298  
   299  				go func() {
   300  					defer close(wait)
   301  					if !*flagWaitToExit {
   302  						return
   303  					}
   304  					t.Log(waitLoopMessage)
   305  					c := make(chan os.Signal, 1)
   306  					signal.Notify(c, os.Interrupt)
   307  					for {
   308  						select {
   309  						case <-time.After(1 * time.Second):
   310  						case <-c:
   311  							return
   312  						}
   313  					}
   314  				}()
   315  
   316  				t.Log("waiting for the agent VMs to power on and get IP addresses")
   317  				timeout := time.After(10 * time.Minute)
   318  				func() {
   319  					for {
   320  						select {
   321  						case msg := <-msgs:
   322  							t.Log(msg)
   323  						case err := <-errs:
   324  							t.Fatal(err)
   325  						case <-done:
   326  							return
   327  						case <-timeout:
   328  							t.Fatal("timed out waiting for agent VMs to power on and get IP addresses")
   329  							return
   330  						}
   331  					}
   332  				}()
   333  			}
   334  		}
   335  
   336  		// Destroy the agency.
   337  		t.Log("destroying agency")
   338  		if err := agency.Destroy(ctx); err != nil {
   339  			t.Fatal(err)
   340  		}
   341  
   342  		if listAgencies() != 0 {
   343  			t.Fatal("no agencies expected")
   344  		}
   345  	})
   346  }
   347  
   348  func TestNotAuthenticated(t *testing.T) {
   349  	vcsim.Test(func(ctx context.Context, vimClient *vim25.Client) {
   350  
   351  		isNotAuthenticatedFault := func(fault vim.AnyType) bool {
   352  			switch fault.(type) {
   353  			case vim.NotAuthenticated, *vim.NotAuthenticated:
   354  				return true
   355  			default:
   356  				return false
   357  			}
   358  		}
   359  
   360  		isEamInvalidLoginFault := func(fault vim.AnyType) bool {
   361  			switch fault.(type) {
   362  			case types.EamInvalidLogin, *types.EamInvalidLogin:
   363  				return true
   364  			default:
   365  				return false
   366  			}
   367  		}
   368  
   369  		validateFault := func(
   370  			err error,
   371  			faultType string,
   372  			faultIsTypeFn func(vim.AnyType) bool) error {
   373  
   374  			if err == nil {
   375  				return fmt.Errorf("%s expected", faultType)
   376  			}
   377  
   378  			validateVimFault := func(vimFault vim.AnyType) error {
   379  				if !faultIsTypeFn(vimFault) {
   380  					return fmt.Errorf(
   381  						"err is not %[1]s: %[2]T %[2]v", faultType, vimFault)
   382  				}
   383  				return nil
   384  			}
   385  
   386  			if soap.IsSoapFault(err) {
   387  				return validateVimFault(soap.ToSoapFault(err).VimFault())
   388  			}
   389  
   390  			if soap.IsVimFault(err) {
   391  				return validateVimFault(soap.ToVimFault(err))
   392  			}
   393  
   394  			return fmt.Errorf(
   395  				"err is not a Soap or Vim fault: %[1]T %[1]v", err)
   396  		}
   397  
   398  		t.Run("TerminateSession", func(t *testing.T) {
   399  			// Terminate the session.
   400  			sessionManager := session.NewManager(vimClient)
   401  			if err := sessionManager.Logout(ctx); err != nil {
   402  				t.Fatalf("logout failed: %v", err)
   403  			}
   404  		})
   405  
   406  		t.Run("ValidateFaults", func(t *testing.T) {
   407  
   408  			t.Run("vim.NotAuthenticated", func(t *testing.T) {
   409  				t.Parallel()
   410  
   411  				// Create a finder to get the default datacenter.
   412  				finder := find.NewFinder(vimClient, true)
   413  
   414  				// Try to get the default datacenter, but receive a NotAuthenticated
   415  				// error.
   416  				_, err := finder.DefaultDatacenter(ctx)
   417  
   418  				if err := validateFault(
   419  					err,
   420  					"NotAuthenticated",
   421  					isNotAuthenticatedFault); err != nil {
   422  
   423  					t.Fatal(err)
   424  				}
   425  			})
   426  
   427  			t.Run("eam.EamInvalidLogin", func(t *testing.T) {
   428  				t.Parallel()
   429  
   430  				// Get an EAM client.
   431  				eamClient := eam.NewClient(vimClient)
   432  
   433  				// Get the EAM root object.
   434  				mgr := object.NewEsxAgentManager(eamClient, eam.EsxAgentManager)
   435  
   436  				// Try to list the agencies, but receive an EamInvalidLogin error.
   437  				_, err := mgr.Agencies(ctx)
   438  
   439  				if err := validateFault(
   440  					err,
   441  					"EamInvalidLogin",
   442  					isEamInvalidLoginFault); err != nil {
   443  
   444  					t.Fatal(err)
   445  				}
   446  			})
   447  		})
   448  
   449  	})
   450  }