blob: 9850386a19d6113542643f8022807337fe826f59 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package buildlet
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"golang.org/x/oauth2"
)
// Client is an interface that represent the methods exposed by client. The
// fake buildlet client should be used instead of client when testing things that
// use the client interface.
type Client interface {
AddCloseFunc(fn func())
Close() error
ConnectSSH(user, authorizedPubKey string) (net.Conn, error)
DestroyVM(ts oauth2.TokenSource, proj, zone, instance string) error
Exec(ctx context.Context, cmd string, opts ExecOpts) (remoteErr, execErr error)
GCEInstanceName() string
GetTar(ctx context.Context, dir string) (io.ReadCloser, error)
IPPort() string
IsBroken() bool
ListDir(ctx context.Context, dir string, opts ListDirOpts, fn func(DirEntry)) error
MarkBroken()
Name() string
ProxyRoundTripper() http.RoundTripper
ProxyTCP(port int) (io.ReadWriteCloser, error)
Put(ctx context.Context, r io.Reader, path string, mode os.FileMode) error
PutTar(ctx context.Context, r io.Reader, dir string) error
PutTarFromURL(ctx context.Context, tarURL, dir string) error
RemoteName() string
RemoveAll(ctx context.Context, paths ...string) error
SetDescription(v string)
SetDialer(dialer func(context.Context) (net.Conn, error))
SetGCEInstanceName(v string)
SetHTTPClient(httpClient *http.Client)
SetName(name string)
SetOnHeartbeatFailure(fn func())
Status(ctx context.Context) (Status, error)
String() string
URL() string
WorkDir(ctx context.Context) (string, error)
}
var errUnimplemented = errors.New("unimplemented function")
var _ Client = (*FakeClient)(nil)
// FakeClient is a fake buildlet client used for testing. Not all functions are implemented.
type FakeClient struct {
closeFuncs []func()
instanceName string
name string
}
// AddCloseFunc adds optional extra code to run on close for the fake buildlet.
func (fc *FakeClient) AddCloseFunc(fn func()) {
fc.closeFuncs = append(fc.closeFuncs, fn)
}
// Close is a fake client closer.
func (fc *FakeClient) Close() error {
for _, f := range fc.closeFuncs {
f()
}
return nil
}
// ConnectSSH connects to a fake SSH server.
func (fc *FakeClient) ConnectSSH(user, authorizedPubKey string) (net.Conn, error) {
return nil, errUnimplemented
}
// DestroyVM destroys a fake VM.
func (fc *FakeClient) DestroyVM(ts oauth2.TokenSource, proj, zone, instance string) error {
return errUnimplemented
}
// Exec fakes the execution.
func (fc *FakeClient) Exec(ctx context.Context, cmd string, opts ExecOpts) (remoteErr, execErr error) {
if cmd == "" {
return nil, errors.New("invalid command")
}
if opts.Output == nil {
return nil, nil
}
out := []byte("<this is a song that never ends>")
for it := 0; it < 3; it++ {
if n, err := opts.Output.Write(out); n != len(out) || err != nil {
return nil, fmt.Errorf("Output.Write(...) = %d, %q; want %d, no error", n, err, len(out))
}
}
return nil, nil
}
// GCEInstanceName gives the fake instance name.
func (fc *FakeClient) GCEInstanceName() string { return fc.instanceName }
// GetTar gives a vake tar zipped directory.
func (fc *FakeClient) GetTar(ctx context.Context, dir string) (io.ReadCloser, error) {
r := strings.NewReader("the gopher goes to the sea and fights the kraken")
return io.NopCloser(r), nil
}
// IPPort provides a fake ip and port pair.
func (fc *FakeClient) IPPort() string { return "" }
// IsBroken returns a fake broken response.
func (fc *FakeClient) IsBroken() bool { return false }
// ListDir lists a directory on a fake buildlet.
func (fc *FakeClient) ListDir(ctx context.Context, dir string, opts ListDirOpts, fn func(DirEntry)) error {
if dir == "" || fn == nil {
errors.New("invalid arguments")
}
var lsOutput = `drwxr-xr-x gocache/
drwxr-xr-x tmp/`
lines := strings.Split(lsOutput, "\n")
for _, line := range lines {
fn(DirEntry{Line: line})
}
return nil
}
// MarkBroken marks the fake client as broken.
func (fc *FakeClient) MarkBroken() {}
// Name is the name of the fake client.
func (fc *FakeClient) Name() string { return fc.name }
// ProxyRoundTripper provides a fake proxy.
func (fc *FakeClient) ProxyRoundTripper() http.RoundTripper { return nil }
// ProxyTCP provides a fake proxy.
func (fc *FakeClient) ProxyTCP(port int) (io.ReadWriteCloser, error) { return nil, errUnimplemented }
// Put places a file on a fake buildlet.
func (fc *FakeClient) Put(ctx context.Context, r io.Reader, path string, mode os.FileMode) error {
// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
if path == "" {
errors.New("invalid argument")
}
return nil
}
// PutTar fakes putting a tar zipped file on a buildldet.
func (fc *FakeClient) PutTar(ctx context.Context, r io.Reader, dir string) error {
// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
return errUnimplemented
}
// PutTarFromURL fakes putting a tar zipped file on a builelt.
func (fc *FakeClient) PutTarFromURL(ctx context.Context, tarURL, dir string) error {
return nil
}
// RemoteName gives the remote name of the fake buildlet.
func (fc *FakeClient) RemoteName() string { return "" }
// SetDescription sets the description on a fake client.
func (fc *FakeClient) SetDescription(v string) {}
// SetDialer sets the function that creates a new connection to the fake buildlet.
func (fc *FakeClient) SetDialer(dialer func(context.Context) (net.Conn, error)) {}
// SetGCEInstanceName sets the GCE instance name on a fake client.
func (fc *FakeClient) SetGCEInstanceName(v string) {
fc.instanceName = v
}
// SetHTTPClient sets the HTTP client on a fake client.
func (fc *FakeClient) SetHTTPClient(httpClient *http.Client) {}
// SetName sets the name on a fake client.
func (fc *FakeClient) SetName(name string) {
fc.name = name
}
// SetOnHeartbeatFailure sets a function to be called when heartbeats against this fake buildlet fail.
func (fc *FakeClient) SetOnHeartbeatFailure(fn func()) {}
// Status provides a status on the fake client.
func (fc *FakeClient) Status(ctx context.Context) (Status, error) { return Status{}, errUnimplemented }
// String provides a fake string representation of the client.
func (fc *FakeClient) String() string { return "" }
// URL is the URL for a fake buildlet.
func (fc *FakeClient) URL() string { return "" }
// WorkDir is the working directory for the fake buildlet.
func (fc *FakeClient) WorkDir(ctx context.Context) (string, error) { return "", errUnimplemented }
// RemoveAll deletes the provided paths, relative to the work directory for a fake buildlet.
func (fc *FakeClient) RemoveAll(ctx context.Context, paths ...string) error {
// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
return nil
}