github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/api/fs.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"time"
    12  )
    13  
    14  // AllocFileInfo holds information about a file inside the AllocDir
    15  type AllocFileInfo struct {
    16  	Name     string
    17  	IsDir    bool
    18  	Size     int64
    19  	FileMode string
    20  	ModTime  time.Time
    21  }
    22  
    23  // AllocFS is used to introspect an allocation directory on a Nomad client
    24  type AllocFS struct {
    25  	client *Client
    26  }
    27  
    28  // AllocFS returns an handle to the AllocFS endpoints
    29  func (c *Client) AllocFS() *AllocFS {
    30  	return &AllocFS{client: c}
    31  }
    32  
    33  // List is used to list the files at a given path of an allocation directory
    34  func (a *AllocFS) List(alloc *Allocation, path string, q *QueryOptions) ([]*AllocFileInfo, *QueryMeta, error) {
    35  	node, _, err := a.client.Nodes().Info(alloc.NodeID, &QueryOptions{})
    36  	if err != nil {
    37  		return nil, nil, err
    38  	}
    39  
    40  	if node.HTTPAddr == "" {
    41  		return nil, nil, fmt.Errorf("http addr of the node where alloc %q is running is not advertised", alloc.ID)
    42  	}
    43  	u := &url.URL{
    44  		Scheme: "http",
    45  		Host:   node.HTTPAddr,
    46  		Path:   fmt.Sprintf("/v1/client/fs/ls/%s", alloc.ID),
    47  	}
    48  	v := url.Values{}
    49  	v.Set("path", path)
    50  	u.RawQuery = v.Encode()
    51  	req := &http.Request{
    52  		Method: "GET",
    53  		URL:    u,
    54  	}
    55  	c := http.Client{}
    56  	resp, err := c.Do(req)
    57  	if err != nil {
    58  		return nil, nil, err
    59  	}
    60  	if resp.StatusCode != 200 {
    61  		return nil, nil, a.getErrorMsg(resp)
    62  	}
    63  	decoder := json.NewDecoder(resp.Body)
    64  	var files []*AllocFileInfo
    65  	if err := decoder.Decode(&files); err != nil {
    66  		return nil, nil, err
    67  	}
    68  	return files, nil, nil
    69  }
    70  
    71  // Stat is used to stat a file at a given path of an allocation directory
    72  func (a *AllocFS) Stat(alloc *Allocation, path string, q *QueryOptions) (*AllocFileInfo, *QueryMeta, error) {
    73  	node, _, err := a.client.Nodes().Info(alloc.NodeID, &QueryOptions{})
    74  	if err != nil {
    75  		return nil, nil, err
    76  	}
    77  
    78  	if node.HTTPAddr == "" {
    79  		return nil, nil, fmt.Errorf("http addr of the node where alloc %q is running is not advertised", alloc.ID)
    80  	}
    81  	u := &url.URL{
    82  		Scheme: "http",
    83  		Host:   node.HTTPAddr,
    84  		Path:   fmt.Sprintf("/v1/client/fs/stat/%s", alloc.ID),
    85  	}
    86  	v := url.Values{}
    87  	v.Set("path", path)
    88  	u.RawQuery = v.Encode()
    89  	req := &http.Request{
    90  		Method: "GET",
    91  		URL:    u,
    92  	}
    93  	c := http.Client{}
    94  	resp, err := c.Do(req)
    95  	if err != nil {
    96  		return nil, nil, err
    97  	}
    98  	if resp.StatusCode != 200 {
    99  		return nil, nil, a.getErrorMsg(resp)
   100  	}
   101  	decoder := json.NewDecoder(resp.Body)
   102  	var file *AllocFileInfo
   103  	if err := decoder.Decode(&file); err != nil {
   104  		return nil, nil, err
   105  	}
   106  	return file, nil, nil
   107  }
   108  
   109  // ReadAt is used to read bytes at a given offset until limit at the given path
   110  // in an allocation directory
   111  func (a *AllocFS) ReadAt(alloc *Allocation, path string, offset int64, limit int64, q *QueryOptions) (io.Reader, *QueryMeta, error) {
   112  	node, _, err := a.client.Nodes().Info(alloc.NodeID, &QueryOptions{})
   113  	if err != nil {
   114  		return nil, nil, err
   115  	}
   116  
   117  	if node.HTTPAddr == "" {
   118  		return nil, nil, fmt.Errorf("http addr of the node where alloc %q is running is not advertised", alloc.ID)
   119  	}
   120  	u := &url.URL{
   121  		Scheme: "http",
   122  		Host:   node.HTTPAddr,
   123  		Path:   fmt.Sprintf("/v1/client/fs/readat/%s", alloc.ID),
   124  	}
   125  	v := url.Values{}
   126  	v.Set("path", path)
   127  	v.Set("offset", strconv.FormatInt(offset, 10))
   128  	v.Set("limit", strconv.FormatInt(limit, 10))
   129  	u.RawQuery = v.Encode()
   130  	req := &http.Request{
   131  		Method: "GET",
   132  		URL:    u,
   133  	}
   134  	c := http.Client{}
   135  	resp, err := c.Do(req)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  	return resp.Body, nil, nil
   140  }
   141  
   142  // Cat is used to read contents of a file at the given path in an allocation
   143  // directory
   144  func (a *AllocFS) Cat(alloc *Allocation, path string, q *QueryOptions) (io.Reader, *QueryMeta, error) {
   145  	node, _, err := a.client.Nodes().Info(alloc.NodeID, &QueryOptions{})
   146  	if err != nil {
   147  		return nil, nil, err
   148  	}
   149  
   150  	if node.HTTPAddr == "" {
   151  		return nil, nil, fmt.Errorf("http addr of the node where alloc %q is running is not advertised", alloc.ID)
   152  	}
   153  	u := &url.URL{
   154  		Scheme: "http",
   155  		Host:   node.HTTPAddr,
   156  		Path:   fmt.Sprintf("/v1/client/fs/cat/%s", alloc.ID),
   157  	}
   158  	v := url.Values{}
   159  	v.Set("path", path)
   160  	u.RawQuery = v.Encode()
   161  	req := &http.Request{
   162  		Method: "GET",
   163  		URL:    u,
   164  	}
   165  	c := http.Client{}
   166  	resp, err := c.Do(req)
   167  	if err != nil {
   168  		return nil, nil, err
   169  	}
   170  	return resp.Body, nil, nil
   171  }
   172  
   173  func (a *AllocFS) getErrorMsg(resp *http.Response) error {
   174  	if errMsg, err := ioutil.ReadAll(resp.Body); err == nil {
   175  		return fmt.Errorf(string(errMsg))
   176  	} else {
   177  		return err
   178  	}
   179  }