blob: 69b5eea4d91f6a433dacbc2814745230e8c5837f [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"
)
// RemoteClient is a subset of methods that can be used by a gomote client.
type RemoteClient interface {
Close() error
Exec(ctx context.Context, cmd string, opts ExecOpts) (remoteErr, execErr error)
GetTar(ctx context.Context, dir string) (io.ReadCloser, error)
ListDir(ctx context.Context, dir string, opts ListDirOpts, fn func(DirEntry)) 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
ProxyTCP(port int) (io.ReadWriteCloser, error)
RemoteName() string
RemoveAll(ctx context.Context, paths ...string) error
WorkDir(ctx context.Context) (string, error)
}
// 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.
// This includes a number of coordinator-internal details; users outside the
// coordinator should use RemoteClient.
type Client interface {
RemoteClient
ConnectSSH(user, authorizedPubKey string) (net.Conn, error)
IPPort() string
InstanceName() string
IsBroken() bool
MarkBroken()
Name() string
ProxyRoundTripper() http.RoundTripper
SetDescription(v string)
SetDialer(dialer func(context.Context) (net.Conn, error))
SetHTTPClient(httpClient *http.Client)
SetInstanceName(v string)
SetName(name string)
SetOnHeartbeatFailure(fn func())
Status(ctx context.Context) (Status, error)
String() string
URL() string
}
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
}
// 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
}
// 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
}
// InstanceName gives the fake instance name.
func (fc *FakeClient) InstanceName() 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 {
return 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 == "" {
return 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)) {}
// SetInstanceName sets the GCE or EC2 instance name on a fake client.
func (fc *FakeClient) SetInstanceName(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 "/work", nil
}
// 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
}