github.com/hernad/nomad@v1.6.112/e2e/v3/cluster3/cluster3.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package cluster3
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	consulapi "github.com/hashicorp/consul/api"
    14  	nomadapi "github.com/hernad/nomad/api"
    15  	vaultapi "github.com/hashicorp/vault/api"
    16  	"github.com/shoenig/test/must"
    17  	"github.com/shoenig/test/wait"
    18  )
    19  
    20  type Cluster struct {
    21  	t *testing.T
    22  
    23  	consulClient *consulapi.Client
    24  	nomadClient  *nomadapi.Client
    25  	vaultClient  *vaultapi.Client
    26  
    27  	timeout        time.Duration
    28  	leaderReady    bool
    29  	consulReady    bool
    30  	vaultReady     bool
    31  	linuxClients   int
    32  	windowsClients int
    33  }
    34  
    35  func (c *Cluster) wait() {
    36  	errCh := make(chan error)
    37  
    38  	statusAPI := c.nomadClient.Status()
    39  	nodesAPI := c.nomadClient.Nodes()
    40  	consulStatusAPI := c.consulClient.Status()
    41  	vaultSysAPI := c.vaultClient.Sys()
    42  
    43  	waitLeader := wait.InitialSuccess(
    44  		wait.Timeout(c.timeout),
    45  		wait.Gap(1*time.Second),
    46  		wait.TestFunc(func() (bool, error) {
    47  			if !c.leaderReady {
    48  				return true, nil
    49  			}
    50  			result, err := statusAPI.Leader()
    51  			return result != "", err
    52  		}),
    53  	)
    54  
    55  	waitLinuxClients := wait.InitialSuccess(
    56  		wait.Timeout(c.timeout),
    57  		wait.Gap(1*time.Second),
    58  		wait.ErrorFunc(func() error {
    59  			if c.linuxClients <= 0 {
    60  				return nil
    61  			}
    62  			queryOpts := &nomadapi.QueryOptions{
    63  				Filter: `Attributes["kernel.name"] == "linux"`,
    64  			}
    65  			nodes, _, err := nodesAPI.List(queryOpts)
    66  			if err != nil {
    67  				return err
    68  			}
    69  			eligible := len(nodes)
    70  			if eligible < c.linuxClients {
    71  				return fmt.Errorf("not enough linux clients, want %d, got %d", c.linuxClients, eligible)
    72  			}
    73  			return nil
    74  		}),
    75  	)
    76  
    77  	waitWindowsClients := wait.InitialSuccess(
    78  		wait.Timeout(c.timeout),
    79  		wait.Gap(1*time.Second),
    80  		wait.ErrorFunc(func() error {
    81  			if c.windowsClients <= 0 {
    82  				return nil
    83  			}
    84  			return errors.New("todo: windows")
    85  		}),
    86  	)
    87  
    88  	waitConsul := wait.InitialSuccess(
    89  		wait.Timeout(c.timeout),
    90  		wait.Gap(1*time.Second),
    91  		wait.TestFunc(func() (bool, error) {
    92  			if !c.consulReady {
    93  				return true, nil
    94  			}
    95  			result, err := consulStatusAPI.Leader()
    96  			return result != "", err
    97  		}),
    98  	)
    99  
   100  	waitVault := wait.InitialSuccess(
   101  		wait.Timeout(c.timeout),
   102  		wait.Gap(1*time.Second),
   103  		wait.TestFunc(func() (bool, error) {
   104  			if !c.vaultReady {
   105  				return true, nil
   106  			}
   107  			result, err := vaultSysAPI.Leader()
   108  			if err != nil {
   109  				return false, fmt.Errorf("failed to find vault leader: %w", err)
   110  			}
   111  			if result == nil {
   112  				return false, errors.New("empty response for vault leader")
   113  			}
   114  			return result.ActiveTime.String() != "", nil
   115  		}),
   116  	)
   117  
   118  	// todo: generalize
   119  
   120  	go func() {
   121  		err := waitLeader.Run()
   122  		errCh <- err
   123  	}()
   124  
   125  	go func() {
   126  		err := waitLinuxClients.Run()
   127  		errCh <- err
   128  	}()
   129  
   130  	go func() {
   131  		err := waitWindowsClients.Run()
   132  		errCh <- err
   133  	}()
   134  
   135  	go func() {
   136  		err := waitConsul.Run()
   137  		errCh <- err
   138  	}()
   139  
   140  	go func() {
   141  		err := waitVault.Run()
   142  		errCh <- err
   143  	}()
   144  
   145  	for i := 0; i < 5; i++ {
   146  		err := <-errCh
   147  		must.NoError(c.t, err)
   148  	}
   149  }
   150  
   151  type Option func(c *Cluster)
   152  
   153  func Establish(t *testing.T, opts ...Option) {
   154  	c := &Cluster{
   155  		t:       t,
   156  		timeout: 10 * time.Second,
   157  	}
   158  	for _, opt := range opts {
   159  		opt(c)
   160  	}
   161  	c.setClients()
   162  	c.wait()
   163  }
   164  
   165  func (c *Cluster) setClients() {
   166  	nomadClient, nomadErr := nomadapi.NewClient(nomadapi.DefaultConfig())
   167  	must.NoError(c.t, nomadErr, must.Sprint("failed to create nomad api client"))
   168  	c.nomadClient = nomadClient
   169  
   170  	consulClient, consulErr := consulapi.NewClient(consulapi.DefaultConfig())
   171  	must.NoError(c.t, consulErr, must.Sprint("failed to create consul api client"))
   172  	c.consulClient = consulClient
   173  
   174  	vConfig := vaultapi.DefaultConfig()
   175  	if os.Getenv("VAULT_ADDR") == "" {
   176  		vConfig.Address = "http://localhost:8200"
   177  	}
   178  	vaultClient, vaultErr := vaultapi.NewClient(vConfig)
   179  	must.NoError(c.t, vaultErr, must.Sprint("failed to create vault api client"))
   180  	c.vaultClient = vaultClient
   181  }
   182  
   183  func Timeout(timeout time.Duration) Option {
   184  	return func(c *Cluster) {
   185  		c.timeout = timeout
   186  	}
   187  }
   188  
   189  func LinuxClients(count int) Option {
   190  	return func(c *Cluster) {
   191  		c.linuxClients = count
   192  	}
   193  }
   194  
   195  func WindowsClients(count int) Option {
   196  	panic("not yet implemented")
   197  	// return func(c *Cluster) {
   198  	// c.windowsClients = count
   199  	// }
   200  }
   201  
   202  func Leader() Option {
   203  	return func(c *Cluster) {
   204  		c.leaderReady = true
   205  	}
   206  }
   207  
   208  func Consul() Option {
   209  	return func(c *Cluster) {
   210  		c.consulReady = true
   211  	}
   212  }
   213  
   214  func Vault() Option {
   215  	return func(c *Cluster) {
   216  		c.vaultReady = true
   217  	}
   218  }