1package framework 2 3import ( 4 "fmt" 5 "os" 6 "testing" 7 8 capi "github.com/hashicorp/consul/api" 9 napi "github.com/hashicorp/nomad/api" 10 "github.com/hashicorp/nomad/helper/uuid" 11 vapi "github.com/hashicorp/vault/api" 12) 13 14// ClusterInfo is a handle to a provisioned cluster, along with clients 15// a test run can use to connect to the cluster. 16type ClusterInfo struct { 17 ID string 18 Name string 19 NomadClient *napi.Client 20 ConsulClient *capi.Client 21 VaultClient *vapi.Client 22} 23 24// SetupOptions defines options to be given to the Provisioner when 25// calling Setup* methods. 26type SetupOptions struct { 27 Name string 28 ExpectConsul bool // If true, fails if a Consul client can't be configured 29 ExpectVault bool // If true, fails if a Vault client can't be configured 30} 31 32// Provisioner interface is used by the test framework to provision API 33// clients for a Nomad cluster, with the possibility of extending to provision 34// standalone clusters for each test case in the future. 35// 36// The Setup* methods are hooks that get run at the appropriate stage. They 37// return a ClusterInfo handle that helps TestCases isolate test state if 38// they use the ClusterInfo.ID as part of job IDs. 39// 40// The TearDown* methods are hooks to clean up provisioned cluster state 41// that isn't covered by the test case's implementation of AfterEachTest. 42type Provisioner interface { 43 // SetupTestRun is called at the start of the entire test run. 44 SetupTestRun(t *testing.T, opts SetupOptions) (*ClusterInfo, error) 45 46 // SetupTestSuite is called at the start of each TestSuite. 47 // TODO: no current provisioner implementation uses this, but we 48 // could use it to provide each TestSuite with an entirely separate 49 // Nomad cluster. 50 SetupTestSuite(t *testing.T, opts SetupOptions) (*ClusterInfo, error) 51 52 // SetupTestCase is called at the start of each TestCase in every TestSuite. 53 SetupTestCase(t *testing.T, opts SetupOptions) (*ClusterInfo, error) 54 55 // TODO: no current provisioner implementation uses any of these, 56 // but it's the obvious need if we setup/teardown after each TestSuite 57 // or TestCase. 58 59 // TearDownTestCase is called after each TestCase in every TestSuite. 60 TearDownTestCase(t *testing.T, clusterID string) error 61 62 // TearDownTestSuite is called after every TestSuite. 63 TearDownTestSuite(t *testing.T, clusterID string) error 64 65 // TearDownTestRun is called at the end of the entire test run. 66 TearDownTestRun(t *testing.T, clusterID string) error 67} 68 69// DefaultProvisioner is a Provisioner that doesn't deploy a Nomad cluster 70// (because that's handled by Terraform elsewhere), but build clients from 71// environment variables. 72var DefaultProvisioner Provisioner = new(singleClusterProvisioner) 73 74type singleClusterProvisioner struct{} 75 76// SetupTestRun in the default case is a no-op. 77func (p *singleClusterProvisioner) SetupTestRun(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { 78 return &ClusterInfo{ID: "framework", Name: "framework"}, nil 79} 80 81// SetupTestSuite in the default case is a no-op. 82func (p *singleClusterProvisioner) SetupTestSuite(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { 83 return &ClusterInfo{ 84 ID: uuid.Generate()[:8], 85 Name: opts.Name, 86 }, nil 87} 88 89// SetupTestCase in the default case only creates new clients and embeds the 90// TestCase name into the ClusterInfo handle. 91func (p *singleClusterProvisioner) SetupTestCase(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { 92 // Build ID based off given name 93 info := &ClusterInfo{ 94 ID: uuid.Generate()[:8], 95 Name: opts.Name, 96 } 97 98 // Build Nomad api client 99 nomadClient, err := napi.NewClient(napi.DefaultConfig()) 100 if err != nil { 101 return nil, err 102 } 103 info.NomadClient = nomadClient 104 105 if opts.ExpectConsul { 106 consulClient, err := capi.NewClient(capi.DefaultConfig()) 107 if err != nil { 108 return nil, fmt.Errorf("expected Consul: %v", err) 109 } 110 info.ConsulClient = consulClient 111 } 112 113 if len(os.Getenv(vapi.EnvVaultAddress)) != 0 { 114 vaultClient, err := vapi.NewClient(vapi.DefaultConfig()) 115 if err != nil && opts.ExpectVault { 116 return nil, err 117 } 118 info.VaultClient = vaultClient 119 } else if opts.ExpectVault { 120 return nil, fmt.Errorf("vault client expected but environment variable %s not set", 121 vapi.EnvVaultAddress) 122 } 123 124 return info, err 125} 126 127// all TearDown* methods of the default provisioner leave the test environment in place 128 129func (p *singleClusterProvisioner) TearDownTestCase(_ *testing.T, _ string) error { return nil } 130func (p *singleClusterProvisioner) TearDownTestSuite(_ *testing.T, _ string) error { return nil } 131func (p *singleClusterProvisioner) TearDownTestRun(_ *testing.T, _ string) error { return nil } 132