golang.org/x/build@v0.0.0-20240506185731-218518f32b70/buildlet/fakebuildletclient.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package buildlet
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"net"
    13  	"net/http"
    14  	"os"
    15  	"strings"
    16  )
    17  
    18  // RemoteClient is a subset of methods that can be used by a gomote client.
    19  type RemoteClient interface {
    20  	Close() error
    21  	Exec(ctx context.Context, cmd string, opts ExecOpts) (remoteErr, execErr error)
    22  	GetTar(ctx context.Context, dir string) (io.ReadCloser, error)
    23  	ListDir(ctx context.Context, dir string, opts ListDirOpts, fn func(DirEntry)) error
    24  	Put(ctx context.Context, r io.Reader, path string, mode os.FileMode) error
    25  	PutTar(ctx context.Context, r io.Reader, dir string) error
    26  	PutTarFromURL(ctx context.Context, tarURL, dir string) error
    27  	ProxyTCP(port int) (io.ReadWriteCloser, error)
    28  	RemoteName() string
    29  	RemoveAll(ctx context.Context, paths ...string) error
    30  	WorkDir(ctx context.Context) (string, error)
    31  }
    32  
    33  // Client is an interface that represent the methods exposed by client. The
    34  // fake buildlet client should be used instead of client when testing things that
    35  // use the client interface.
    36  // This includes a number of coordinator-internal details; users outside the
    37  // coordinator should use RemoteClient.
    38  type Client interface {
    39  	RemoteClient
    40  	ConnectSSH(user, authorizedPubKey string) (net.Conn, error)
    41  	IPPort() string
    42  	InstanceName() string
    43  	IsBroken() bool
    44  	MarkBroken()
    45  	Name() string
    46  	ProxyRoundTripper() http.RoundTripper
    47  	SetDescription(v string)
    48  	SetDialer(dialer func(context.Context) (net.Conn, error))
    49  	SetHTTPClient(httpClient *http.Client)
    50  	SetInstanceName(v string)
    51  	SetName(name string)
    52  	SetOnHeartbeatFailure(fn func())
    53  	Status(ctx context.Context) (Status, error)
    54  	String() string
    55  	URL() string
    56  }
    57  
    58  var errUnimplemented = errors.New("unimplemented function")
    59  
    60  var _ Client = (*FakeClient)(nil)
    61  
    62  // FakeClient is a fake buildlet client used for testing. Not all functions are implemented.
    63  type FakeClient struct {
    64  	closeFuncs   []func()
    65  	instanceName string
    66  	name         string
    67  }
    68  
    69  // Close is a fake client closer.
    70  func (fc *FakeClient) Close() error {
    71  	for _, f := range fc.closeFuncs {
    72  		f()
    73  	}
    74  	return nil
    75  }
    76  
    77  // ConnectSSH connects to a fake SSH server.
    78  func (fc *FakeClient) ConnectSSH(user, authorizedPubKey string) (net.Conn, error) {
    79  	return nil, errUnimplemented
    80  }
    81  
    82  // Exec fakes the execution.
    83  func (fc *FakeClient) Exec(ctx context.Context, cmd string, opts ExecOpts) (remoteErr, execErr error) {
    84  	if cmd == "" {
    85  		return nil, errors.New("invalid command")
    86  	}
    87  	if opts.Output == nil {
    88  		return nil, nil
    89  	}
    90  	out := []byte("<this is a song that never ends>")
    91  	for it := 0; it < 3; it++ {
    92  		if n, err := opts.Output.Write(out); n != len(out) || err != nil {
    93  			return nil, fmt.Errorf("Output.Write(...) = %d, %q; want %d, no error", n, err, len(out))
    94  		}
    95  	}
    96  	return nil, nil
    97  }
    98  
    99  // InstanceName gives the fake instance name.
   100  func (fc *FakeClient) InstanceName() string { return fc.instanceName }
   101  
   102  // GetTar gives a vake tar zipped directory.
   103  func (fc *FakeClient) GetTar(ctx context.Context, dir string) (io.ReadCloser, error) {
   104  	r := strings.NewReader("the gopher goes to the sea and fights the kraken")
   105  	return io.NopCloser(r), nil
   106  }
   107  
   108  // IPPort provides a fake ip and port pair.
   109  func (fc *FakeClient) IPPort() string { return "" }
   110  
   111  // IsBroken returns a fake broken response.
   112  func (fc *FakeClient) IsBroken() bool { return false }
   113  
   114  // ListDir lists a directory on a fake buildlet.
   115  func (fc *FakeClient) ListDir(ctx context.Context, dir string, opts ListDirOpts, fn func(DirEntry)) error {
   116  	if dir == "" || fn == nil {
   117  		return errors.New("invalid arguments")
   118  	}
   119  	var lsOutput = `drwxr-xr-x      gocache/
   120  drwxr-xr-x      tmp/`
   121  	lines := strings.Split(lsOutput, "\n")
   122  	for _, line := range lines {
   123  		fn(DirEntry{Line: line})
   124  	}
   125  	return nil
   126  }
   127  
   128  // MarkBroken marks the fake client as broken.
   129  func (fc *FakeClient) MarkBroken() {}
   130  
   131  // Name is the name of the fake client.
   132  func (fc *FakeClient) Name() string { return fc.name }
   133  
   134  // ProxyRoundTripper provides a fake proxy.
   135  func (fc *FakeClient) ProxyRoundTripper() http.RoundTripper { return nil }
   136  
   137  // ProxyTCP provides a fake proxy.
   138  func (fc *FakeClient) ProxyTCP(port int) (io.ReadWriteCloser, error) { return nil, errUnimplemented }
   139  
   140  // Put places a file on a fake buildlet.
   141  func (fc *FakeClient) Put(ctx context.Context, r io.Reader, path string, mode os.FileMode) error {
   142  	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
   143  	if path == "" {
   144  		return errors.New("invalid argument")
   145  	}
   146  	return nil
   147  }
   148  
   149  // PutTar fakes putting  a tar zipped file on a buildldet.
   150  func (fc *FakeClient) PutTar(ctx context.Context, r io.Reader, dir string) error {
   151  	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
   152  	return errUnimplemented
   153  }
   154  
   155  // PutTarFromURL fakes putting a tar zipped file on a builelt.
   156  func (fc *FakeClient) PutTarFromURL(ctx context.Context, tarURL, dir string) error {
   157  	return nil
   158  }
   159  
   160  // RemoteName gives the remote name of the fake buildlet.
   161  func (fc *FakeClient) RemoteName() string { return "" }
   162  
   163  // SetDescription sets the description on a fake client.
   164  func (fc *FakeClient) SetDescription(v string) {}
   165  
   166  // SetDialer sets the function that creates a new connection to the fake buildlet.
   167  func (fc *FakeClient) SetDialer(dialer func(context.Context) (net.Conn, error)) {}
   168  
   169  // SetInstanceName sets the GCE or EC2 instance name on a fake client.
   170  func (fc *FakeClient) SetInstanceName(v string) {
   171  	fc.instanceName = v
   172  }
   173  
   174  // SetHTTPClient sets the HTTP client on a fake client.
   175  func (fc *FakeClient) SetHTTPClient(httpClient *http.Client) {}
   176  
   177  // SetName sets the name on a fake client.
   178  func (fc *FakeClient) SetName(name string) {
   179  	fc.name = name
   180  }
   181  
   182  // SetOnHeartbeatFailure sets a function to be called when heartbeats against this fake buildlet fail.
   183  func (fc *FakeClient) SetOnHeartbeatFailure(fn func()) {}
   184  
   185  // Status provides a status on the fake client.
   186  func (fc *FakeClient) Status(ctx context.Context) (Status, error) { return Status{}, errUnimplemented }
   187  
   188  // String provides a fake string representation of the client.
   189  func (fc *FakeClient) String() string { return "" }
   190  
   191  // URL is the URL for a fake buildlet.
   192  func (fc *FakeClient) URL() string { return "" }
   193  
   194  // WorkDir is the working directory for the fake buildlet.
   195  func (fc *FakeClient) WorkDir(ctx context.Context) (string, error) {
   196  	return "/work", nil
   197  }
   198  
   199  // RemoveAll deletes the provided paths, relative to the work directory for a fake buildlet.
   200  func (fc *FakeClient) RemoveAll(ctx context.Context, paths ...string) error {
   201  	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
   202  	return nil
   203  }