github.com/vmware/govmomi@v0.51.0/vim25/retry_test.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package vim25_test
     6  
     7  import (
     8  	"context"
     9  	"net/http"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/vmware/govmomi/find"
    14  	"github.com/vmware/govmomi/simulator"
    15  	"github.com/vmware/govmomi/vim25"
    16  	"github.com/vmware/govmomi/vim25/soap"
    17  	"github.com/vmware/govmomi/vim25/types"
    18  )
    19  
    20  type tempError struct{}
    21  
    22  func (tempError) Error() string   { return "tempError" }
    23  func (tempError) Timeout() bool   { return true }
    24  func (tempError) Temporary() bool { return true }
    25  
    26  type nonTempError struct{}
    27  
    28  func (nonTempError) Error() string   { return "nonTempError" }
    29  func (nonTempError) Timeout() bool   { return false }
    30  func (nonTempError) Temporary() bool { return false }
    31  
    32  type fakeRoundTripper struct {
    33  	errs []error
    34  }
    35  
    36  func (f *fakeRoundTripper) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
    37  	err := f.errs[0]
    38  	f.errs = f.errs[1:]
    39  	return err
    40  }
    41  
    42  func TestRetry(t *testing.T) {
    43  	var tcs = []struct {
    44  		errs     []error
    45  		expected error
    46  	}{
    47  		{
    48  			errs:     []error{nil},
    49  			expected: nil,
    50  		},
    51  		{
    52  			errs:     []error{tempError{}, nil},
    53  			expected: nil,
    54  		},
    55  		{
    56  			errs:     []error{tempError{}, tempError{}},
    57  			expected: tempError{},
    58  		},
    59  		{
    60  			errs:     []error{nonTempError{}},
    61  			expected: nonTempError{},
    62  		},
    63  		{
    64  			errs:     []error{tempError{}, nonTempError{}},
    65  			expected: nonTempError{},
    66  		},
    67  	}
    68  
    69  	for _, tc := range tcs {
    70  		var rt soap.RoundTripper = &fakeRoundTripper{errs: tc.errs}
    71  		rt = vim25.Retry(rt, vim25.RetryTemporaryNetworkError, 2)
    72  
    73  		err := rt.RoundTrip(context.TODO(), nil, nil)
    74  		if err != tc.expected {
    75  			t.Errorf("Expected: %s, got: %s", tc.expected, err)
    76  		}
    77  	}
    78  }
    79  
    80  func TestRetryNetworkError(t *testing.T) {
    81  	simulator.Test(func(ctx context.Context, c *vim25.Client) {
    82  		c.RoundTripper = vim25.Retry(c.Client, vim25.RetryTemporaryNetworkError, 2)
    83  
    84  		vm, err := find.NewFinder(c).VirtualMachine(ctx, "DC0_H0_VM0")
    85  		if err != nil {
    86  			t.Fatal(err)
    87  		}
    88  
    89  		// Tell vcsim to respond with 502 on the 1st request
    90  		simulator.StatusSDK = http.StatusBadGateway
    91  
    92  		state, err := vm.PowerState(ctx)
    93  		if err != nil {
    94  			t.Fatal(err)
    95  		}
    96  
    97  		if state != types.VirtualMachinePowerStatePoweredOn {
    98  			t.Errorf("state=%s", state)
    99  		}
   100  
   101  		retry := func(err error) (bool, time.Duration) {
   102  			simulator.StatusSDK = http.StatusBadGateway // Tell vcsim to respond with 502 on every request
   103  			return vim25.IsTemporaryNetworkError(err), 0
   104  		}
   105  		c.RoundTripper = vim25.Retry(c.Client, retry, 2)
   106  
   107  		simulator.StatusSDK = http.StatusBadGateway
   108  		// beyond max retry attempts, should result in an erro
   109  		for i := 0; i < 3; i++ {
   110  			_, err = vm.PowerState(ctx)
   111  		}
   112  
   113  		if err == nil {
   114  			t.Error("expected error")
   115  		}
   116  
   117  		if !vim25.IsTemporaryNetworkError(err) {
   118  			t.Errorf("unexpected error=%s", err)
   119  		}
   120  		simulator.StatusSDK = http.StatusOK
   121  	})
   122  }