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 }