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 }