package framework import ( "fmt" "os" "testing" capi "github.com/hashicorp/consul/api" napi "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/helper/uuid" vapi "github.com/hashicorp/vault/api" ) // ClusterInfo is a handle to a provisioned cluster, along with clients // a test run can use to connect to the cluster. type ClusterInfo struct { ID string Name string NomadClient *napi.Client ConsulClient *capi.Client VaultClient *vapi.Client } // SetupOptions defines options to be given to the Provisioner when // calling Setup* methods. type SetupOptions struct { Name string ExpectConsul bool // If true, fails if a Consul client can't be configured ExpectVault bool // If true, fails if a Vault client can't be configured } // Provisioner interface is used by the test framework to provision API // clients for a Nomad cluster, with the possibility of extending to provision // standalone clusters for each test case in the future. // // The Setup* methods are hooks that get run at the appropriate stage. They // return a ClusterInfo handle that helps TestCases isolate test state if // they use the ClusterInfo.ID as part of job IDs. // // The TearDown* methods are hooks to clean up provisioned cluster state // that isn't covered by the test case's implementation of AfterEachTest. type Provisioner interface { // SetupTestRun is called at the start of the entire test run. SetupTestRun(t *testing.T, opts SetupOptions) (*ClusterInfo, error) // SetupTestSuite is called at the start of each TestSuite. // TODO: no current provisioner implementation uses this, but we // could use it to provide each TestSuite with an entirely separate // Nomad cluster. SetupTestSuite(t *testing.T, opts SetupOptions) (*ClusterInfo, error) // SetupTestCase is called at the start of each TestCase in every TestSuite. SetupTestCase(t *testing.T, opts SetupOptions) (*ClusterInfo, error) // TODO: no current provisioner implementation uses any of these, // but it's the obvious need if we setup/teardown after each TestSuite // or TestCase. // TearDownTestCase is called after each TestCase in every TestSuite. TearDownTestCase(t *testing.T, clusterID string) error // TearDownTestSuite is called after every TestSuite. TearDownTestSuite(t *testing.T, clusterID string) error // TearDownTestRun is called at the end of the entire test run. TearDownTestRun(t *testing.T, clusterID string) error } // DefaultProvisioner is a Provisioner that doesn't deploy a Nomad cluster // (because that's handled by Terraform elsewhere), but build clients from // environment variables. var DefaultProvisioner Provisioner = new(singleClusterProvisioner) type singleClusterProvisioner struct{} // SetupTestRun in the default case is a no-op. func (p *singleClusterProvisioner) SetupTestRun(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { return &ClusterInfo{ID: "framework", Name: "framework"}, nil } // SetupTestSuite in the default case is a no-op. func (p *singleClusterProvisioner) SetupTestSuite(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { return &ClusterInfo{ ID: uuid.Generate()[:8], Name: opts.Name, }, nil } // SetupTestCase in the default case only creates new clients and embeds the // TestCase name into the ClusterInfo handle. func (p *singleClusterProvisioner) SetupTestCase(t *testing.T, opts SetupOptions) (*ClusterInfo, error) { // Build ID based off given name info := &ClusterInfo{ ID: uuid.Generate()[:8], Name: opts.Name, } // Build Nomad api client nomadClient, err := napi.NewClient(napi.DefaultConfig()) if err != nil { return nil, err } info.NomadClient = nomadClient if opts.ExpectConsul { consulClient, err := capi.NewClient(capi.DefaultConfig()) if err != nil { return nil, fmt.Errorf("expected Consul: %v", err) } info.ConsulClient = consulClient } if len(os.Getenv(vapi.EnvVaultAddress)) != 0 { vaultClient, err := vapi.NewClient(vapi.DefaultConfig()) if err != nil && opts.ExpectVault { return nil, err } info.VaultClient = vaultClient } else if opts.ExpectVault { return nil, fmt.Errorf("vault client expected but environment variable %s not set", vapi.EnvVaultAddress) } return info, err } // all TearDown* methods of the default provisioner leave the test environment in place func (p *singleClusterProvisioner) TearDownTestCase(_ *testing.T, _ string) error { return nil } func (p *singleClusterProvisioner) TearDownTestSuite(_ *testing.T, _ string) error { return nil } func (p *singleClusterProvisioner) TearDownTestRun(_ *testing.T, _ string) error { return nil }