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 }