dashboard: start of cmd/gomote buildlet client, more packification
Change-Id: I874f4f5ef253cf7f1d6d5073d7c81e76fa1de863
Reviewed-on: https://go-review.googlesource.com/2981
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/buildlet/buildletclient.go b/buildlet/buildletclient.go
index 3ab091f..d9ecbd1 100644
--- a/buildlet/buildletclient.go
+++ b/buildlet/buildletclient.go
@@ -18,42 +18,44 @@
"strings"
)
-// KeyPair is the TLS public certificate PEM file and its associated
-// private key PEM file that a builder will use for its HTTPS
-// server. The zero value means no HTTPs, which is used by the
-// coordinator for machines running within a firewall.
-type KeyPair struct {
- CertPEM string
- KeyPEM string
-}
-
-// NoKeyPair is used by the coordinator to speak http directly to buildlets,
-// inside their firewall, without TLS.
-var NoKeyPair = KeyPair{}
-
// NewClient returns a *Client that will manipulate ipPort,
// authenticated using the provided keypair.
//
// This constructor returns immediately without testing the host or auth.
-func NewClient(ipPort string, tls KeyPair) *Client {
+func NewClient(ipPort string, kp KeyPair) *Client {
return &Client{
- ipPort: ipPort,
- tls: tls,
+ ipPort: ipPort,
+ tls: kp,
+ password: kp.Password(),
+ httpClient: &http.Client{
+ Transport: &http.Transport{
+ DialTLS: kp.tlsDialer(),
+ },
+ },
}
}
// A Client interacts with a single buildlet.
type Client struct {
- ipPort string
- tls KeyPair
+ ipPort string
+ tls KeyPair
+ password string // basic auth password or empty for none
+ httpClient *http.Client
}
// URL returns the buildlet's URL prefix, without a trailing slash.
func (c *Client) URL() string {
- if c.tls != NoKeyPair {
- return "http://" + strings.TrimSuffix(c.ipPort, ":80")
+ if !c.tls.IsZero() {
+ return "https://" + strings.TrimSuffix(c.ipPort, ":443")
}
- return "https://" + strings.TrimSuffix(c.ipPort, ":443")
+ return "http://" + strings.TrimSuffix(c.ipPort, ":80")
+}
+
+func (c *Client) do(req *http.Request) (*http.Response, error) {
+ if c.password != "" {
+ req.SetBasicAuth("gomote", c.password)
+ }
+ return c.httpClient.Do(req)
}
// PutTarball writes files to the remote buildlet.
@@ -63,7 +65,7 @@
if err != nil {
return err
}
- res, err := http.DefaultClient.Do(req)
+ res, err := c.do(req)
if err != nil {
return err
}
@@ -95,7 +97,15 @@
// seen to completition. If execErr is non-nil, the remoteErr is
// meaningless.
func (c *Client) Exec(cmd string, opts ExecOpts) (remoteErr, execErr error) {
- res, err := http.PostForm(c.URL()+"/exec", url.Values{"cmd": {cmd}})
+ form := url.Values{
+ "cmd": {cmd},
+ }
+ req, err := http.NewRequest("POST", c.URL()+"/exec", strings.NewReader(form.Encode()))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ res, err := c.do(req)
if err != nil {
return nil, err
}
@@ -130,6 +140,24 @@
return nil, nil
}
+// Destroy shuts down the buildlet, destroying all state immediately.
+func (c *Client) Destroy() error {
+ req, err := http.NewRequest("POST", c.URL()+"/halt", nil)
+ if err != nil {
+ return err
+ }
+ res, err := c.do(req)
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 4<<10))
+ return fmt.Errorf("buildlet: HTTP status %v: %s", res.Status, slurp)
+ }
+ return nil
+}
+
func condRun(fn func()) {
if fn != nil {
fn()