216 lines
5.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package singleprocess
import (
"bytes"
"context"
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/go-hclog"
"github.com/imdario/mergo"
"github.com/mitchellh/go-testing-interface"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/require"
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"github.com/hashicorp/vagrant/internal/server"
pb "github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
"github.com/hashicorp/vagrant/internal/server/singleprocess/state"
"github.com/hashicorp/vagrant/internal/serverclient"
)
// TestServer starts a singleprocess server and returns the connected client.
// We use t.Cleanup to ensure resources are automatically cleaned up.
func TestServer(t testing.T, opts ...Option) *serverclient.VagrantClient {
return server.TestServer(t, TestImpl(t, opts...))
}
// TestImpl returns the vagrant server implementation. This can be used
// with server.TestServer. It is easier to just use TestServer directly.
func TestImpl(t testing.T, opts ...Option) pb.VagrantServer {
var buf bytes.Buffer
l := hclog.New(&hclog.LoggerOptions{
Name: "test",
Level: hclog.Trace,
Output: &buf,
IncludeLocation: true,
})
impl, err := New(append(
[]Option{WithDB(state.TestDB(t)), WithLogger(l)},
opts...,
)...)
t.Cleanup(func() {
t.Log(buf.String())
})
require.NoError(t, err)
return impl
}
// // TestWithURLService is an Option for testing only that creates an
// // in-memory URL service server. This requires access to an external
// // postgres server.
// //
// // If out is non-nil, it will be written to with the DevSetup info.
// func TestWithURLService(t testing.T, out *hzntest.DevSetup) Option {
// // Create the test server. On test end we close the channel which quits
// // the Horizon test server.
// setupCh := make(chan *hzntest.DevSetup, 1)
// closeCh := make(chan struct{})
// t.Cleanup(func() { close(closeCh) })
// go hzntest.Dev(t, func(setup *hzntest.DevSetup) {
// hubclient, err := hznhub.NewHub(hclog.L(), setup.ControlClient, setup.HubToken)
// require.NoError(t, err)
// go hubclient.Run(context.Background(), setup.ClientListener)
// setupCh <- setup
// <-closeCh
// })
// setup := <-setupCh
// // Make our test registration API
// wphzndata := wphzn.TestServer(t,
// wphzn.WithNamespace("/"),
// wphzn.WithHznControl(setup.MgmtClient),
// )
// // Get our account token.
// wpaccountResp, err := wphzndata.Client.RegisterGuestAccount(
// context.Background(),
// &wphznpb.RegisterGuestAccountRequest{
// ServerId: "A",
// },
// )
// require.NoError(t, err)
// // We need to get the account since that is what we need to query with
// tokenInfo, err := setup.MgmtClient.GetTokenPublicKey(context.Background(), &hznpb.Noop{})
// require.NoError(t, err)
// token, err := hzntoken.CheckTokenED25519(wpaccountResp.Token, tokenInfo.PublicKey)
// require.NoError(t, err)
// setup.Account = token.Account()
// // Copy our setup config
// if out != nil {
// *out = *setup
// }
// return func(s *service, cfg *config) error {
// if cfg.serverConfig == nil {
// cfg.serverConfig = &serverconfig.Config{}
// }
// cfg.serverConfig.URL = &serverconfig.URL{
// Enabled: true,
// APIAddress: wphzndata.Addr,
// APIInsecure: true,
// APIToken: wpaccountResp.Token,
// ControlAddress: fmt.Sprintf("dev://%s", setup.HubAddr),
// AutomaticAppHostname: true,
// }
// return nil
// }
// }
// TestRunner registers a runner and returns the ID and a function to
// deregister the runner. This uses t.Cleanup so that the runner will always
// be deregistered on test completion.
func TestRunner(t testing.T, client pb.VagrantClient, r *pb.Runner) (string, func()) {
require := require.New(t)
ctx := context.Background()
// Get the runner
if r == nil {
r = &pb.Runner{}
}
id, err := server.Id()
require.NoError(err)
require.NoError(mergo.Merge(r, &pb.Runner{Id: id}))
// Open the config stream
stream, err := client.RunnerConfig(ctx)
require.NoError(err)
t.Cleanup(func() { stream.CloseSend() })
// Register
require.NoError(err)
require.NoError(stream.Send(&pb.RunnerConfigRequest{
Event: &pb.RunnerConfigRequest_Open_{
Open: &pb.RunnerConfigRequest_Open{
Runner: r,
},
},
}))
// Wait for first message to confirm we're registered
_, err = stream.Recv()
require.NoError(err)
return id, func() { stream.CloseSend() }
}
// TestBasis creates the basis in the DB.
func TestBasis(t testing.T, client pb.VagrantClient, ref *pb.Basis) *pb.Basis {
if ref == nil {
ref = &pb.Basis{}
}
td := testTempDir(t)
defaultBasis := &pb.Basis{
Name: filepath.Base(td),
Path: td,
}
require.NoError(t, mergo.Merge(ref, defaultBasis))
resp, err := client.UpsertBasis(context.Background(), &pb.UpsertBasisRequest{
Basis: ref,
})
require.NoError(t, err)
return resp.Basis
}
func TestJob(t testing.T, client pb.VagrantClient, ref *pb.Job) *pb.Job {
j := testJobProto(t, client, ref)
resp, err := client.QueueJob(context.Background(), &pb.QueueJobRequest{Job: j})
require.Nil(t, err)
j.Id = resp.JobId
return j
}
func testJobProto(t testing.T, client pb.VagrantClient, ref *pb.Job) *pb.Job {
var job pb.Job
if ref == nil {
ref = &pb.Job{}
}
err := mapstructure.Decode(ref, &job)
require.NoError(t, err)
if job.Scope == nil {
basis := TestBasis(t, client, nil)
job.Scope = &pb.Job_Basis{
Basis: &vagrant_plugin_sdk.Ref_Basis{
ResourceId: basis.ResourceId,
},
}
}
return state.TestJobProto(t, &job)
}
func testTempDir(t testing.T) string {
dir, err := ioutil.TempDir("", "vagrant-test")
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(dir) })
return dir
}