github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/replica/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/Sirupsen/logrus"
    15  	"github.com/rancher/longhorn/replica/rest"
    16  	"github.com/rancher/longhorn/sync/agent"
    17  )
    18  
    19  type ReplicaClient struct {
    20  	address    string
    21  	syncAgent  string
    22  	host       string
    23  	httpClient *http.Client
    24  }
    25  
    26  func NewReplicaClient(address string) (*ReplicaClient, error) {
    27  	if strings.HasPrefix(address, "tcp://") {
    28  		address = address[6:]
    29  	}
    30  
    31  	if !strings.HasPrefix(address, "http") {
    32  		address = "http://" + address
    33  	}
    34  
    35  	if !strings.HasSuffix(address, "/v1") {
    36  		address += "/v1"
    37  	}
    38  
    39  	u, err := url.Parse(address)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	parts := strings.Split(u.Host, ":")
    45  	if len(parts) < 2 {
    46  		return nil, fmt.Errorf("Invalid address %s, must have a port in it", address)
    47  	}
    48  
    49  	port, err := strconv.Atoi(parts[1])
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	syncAgent := strings.Replace(address, fmt.Sprintf(":%d", port), fmt.Sprintf(":%d", port+2), -1)
    55  
    56  	timeout := time.Duration(30 * time.Second)
    57  	client := &http.Client{
    58  		Timeout: timeout,
    59  	}
    60  
    61  	return &ReplicaClient{
    62  		host:       parts[0],
    63  		address:    address,
    64  		syncAgent:  syncAgent,
    65  		httpClient: client,
    66  	}, nil
    67  }
    68  
    69  func (c *ReplicaClient) Create(size string) error {
    70  	r, err := c.GetReplica()
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	return c.post(r.Actions["create"], rest.CreateInput{
    76  		Size: size,
    77  	}, nil)
    78  }
    79  
    80  func (c *ReplicaClient) Revert(name string) error {
    81  	r, err := c.GetReplica()
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	return c.post(r.Actions["revert"], rest.RevertInput{
    87  		Name: name,
    88  	}, nil)
    89  }
    90  
    91  func (c *ReplicaClient) Close() error {
    92  	r, err := c.GetReplica()
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	return c.post(r.Actions["close"], nil, nil)
    98  }
    99  
   100  func (c *ReplicaClient) SetRebuilding(rebuilding bool) error {
   101  	r, err := c.GetReplica()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	return c.post(r.Actions["setrebuilding"], &rest.RebuildingInput{
   107  		Rebuilding: rebuilding,
   108  	}, nil)
   109  }
   110  
   111  func (c *ReplicaClient) RemoveDisk(disk string, markOnly bool) error {
   112  	r, err := c.GetReplica()
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	return c.post(r.Actions["removedisk"], &rest.RemoveDiskInput{
   118  		Name:     disk,
   119  		MarkOnly: markOnly,
   120  	}, nil)
   121  }
   122  
   123  func (c *ReplicaClient) PrepareRemoveDisk(disk string) (rest.PrepareRemoveDiskOutput, error) {
   124  	var output rest.PrepareRemoveDiskOutput
   125  	r, err := c.GetReplica()
   126  	if err != nil {
   127  		return output, err
   128  	}
   129  
   130  	err = c.post(r.Actions["prepareremovedisk"], &rest.PrepareRemoveDiskInput{
   131  		Name: disk,
   132  	}, &output)
   133  	return output, err
   134  }
   135  
   136  func (c *ReplicaClient) OpenReplica() error {
   137  	r, err := c.GetReplica()
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	return c.post(r.Actions["open"], nil, nil)
   143  }
   144  
   145  func (c *ReplicaClient) GetReplica() (rest.Replica, error) {
   146  	var replica rest.Replica
   147  
   148  	err := c.get(c.address+"/replicas/1", &replica)
   149  	return replica, err
   150  }
   151  
   152  func (c *ReplicaClient) ReloadReplica() (rest.Replica, error) {
   153  	var replica rest.Replica
   154  
   155  	err := c.post(c.address+"/replicas/1?action=reload", map[string]string{}, &replica)
   156  	return replica, err
   157  }
   158  
   159  func (c *ReplicaClient) LaunchReceiver() (string, int, error) {
   160  	var running agent.Process
   161  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   162  		ProcessType: "sync",
   163  	}, &running)
   164  	if err != nil {
   165  		return "", 0, err
   166  	}
   167  
   168  	return c.host, running.Port, nil
   169  }
   170  
   171  func (c *ReplicaClient) Coalesce(from, to string) error {
   172  	var running agent.Process
   173  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   174  		ProcessType: "fold",
   175  		SrcFile:     from,
   176  		DestFile:    to,
   177  	}, &running)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	start := 250 * time.Millisecond
   183  	for {
   184  		err := c.get(running.Links["self"], &running)
   185  		if err != nil {
   186  			return err
   187  		}
   188  
   189  		switch running.ExitCode {
   190  		case -2:
   191  			time.Sleep(start)
   192  			start = start * 2
   193  			if start > 1*time.Second {
   194  				start = 1 * time.Second
   195  			}
   196  		case 0:
   197  			return nil
   198  		default:
   199  			return fmt.Errorf("ExitCode: %d", running.ExitCode)
   200  		}
   201  	}
   202  }
   203  
   204  func (c *ReplicaClient) SendFile(from, to, host string, port int) error {
   205  	var running agent.Process
   206  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   207  		ProcessType: "sync",
   208  		Host:        host,
   209  		SrcFile:     from,
   210  		DestFile:    to,
   211  		Port:        port,
   212  	}, &running)
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	start := 250 * time.Millisecond
   218  	for {
   219  		err := c.get(running.Links["self"], &running)
   220  		if err != nil {
   221  			return err
   222  		}
   223  
   224  		switch running.ExitCode {
   225  		case -2:
   226  			time.Sleep(start)
   227  			start = start * 2
   228  			if start > 1*time.Second {
   229  				start = 1 * time.Second
   230  			}
   231  		case 0:
   232  			return nil
   233  		default:
   234  			return fmt.Errorf("ExitCode: %d", running.ExitCode)
   235  		}
   236  	}
   237  }
   238  
   239  func (c *ReplicaClient) CreateBackup(snapshot, dest, volume string) (string, error) {
   240  	var running agent.Process
   241  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   242  		ProcessType: "backup",
   243  		SrcFile:     snapshot,
   244  		DestFile:    dest,
   245  		Host:        volume,
   246  	}, &running)
   247  	if err != nil {
   248  		return "", err
   249  	}
   250  
   251  	start := 250 * time.Millisecond
   252  	for {
   253  		err := c.get(running.Links["self"], &running)
   254  		if err != nil {
   255  			return "", err
   256  		}
   257  
   258  		switch running.ExitCode {
   259  		case -2:
   260  			time.Sleep(start)
   261  			start = start * 2
   262  			if start > 1*time.Second {
   263  				start = 1 * time.Second
   264  			}
   265  		case 0:
   266  			return running.Output, nil
   267  		default:
   268  			return "", fmt.Errorf("ExitCode: %d, output: %v",
   269  				running.ExitCode, running.Output)
   270  		}
   271  	}
   272  }
   273  
   274  func (c *ReplicaClient) RmBackup(backup string) error {
   275  	var running agent.Process
   276  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   277  		ProcessType: "rmbackup",
   278  		SrcFile:     backup,
   279  	}, &running)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	start := 250 * time.Millisecond
   285  	for {
   286  		err := c.get(running.Links["self"], &running)
   287  		if err != nil {
   288  			return err
   289  		}
   290  
   291  		switch running.ExitCode {
   292  		case -2:
   293  			time.Sleep(start)
   294  			start = start * 2
   295  			if start > 1*time.Second {
   296  				start = 1 * time.Second
   297  			}
   298  		case 0:
   299  			return nil
   300  		default:
   301  			return fmt.Errorf("ExitCode: %d, output: %v",
   302  				running.ExitCode, running.Output)
   303  		}
   304  	}
   305  }
   306  
   307  func (c *ReplicaClient) RestoreBackup(backup, snapshotFile string) error {
   308  	var running agent.Process
   309  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   310  		ProcessType: "restore",
   311  		SrcFile:     backup,
   312  		DestFile:    snapshotFile,
   313  	}, &running)
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	start := 250 * time.Millisecond
   319  	for {
   320  		err := c.get(running.Links["self"], &running)
   321  		if err != nil {
   322  			return err
   323  		}
   324  
   325  		switch running.ExitCode {
   326  		case -2:
   327  			time.Sleep(start)
   328  			start = start * 2
   329  			if start > 1*time.Second {
   330  				start = 1 * time.Second
   331  			}
   332  		case 0:
   333  			return nil
   334  		default:
   335  			return fmt.Errorf("ExitCode: %d, output: %v",
   336  				running.ExitCode, running.Output)
   337  		}
   338  	}
   339  }
   340  
   341  func (c *ReplicaClient) InspectBackup(backup string) (string, error) {
   342  	var running agent.Process
   343  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   344  		ProcessType: "inspectbackup",
   345  		SrcFile:     backup,
   346  	}, &running)
   347  	if err != nil {
   348  		return "", err
   349  	}
   350  
   351  	start := 250 * time.Millisecond
   352  	for {
   353  		err := c.get(running.Links["self"], &running)
   354  		if err != nil {
   355  			return "", err
   356  		}
   357  
   358  		switch running.ExitCode {
   359  		case -2:
   360  			time.Sleep(start)
   361  			start = start * 2
   362  			if start > 1*time.Second {
   363  				start = 1 * time.Second
   364  			}
   365  		case 0:
   366  			return running.Output, nil
   367  		default:
   368  			return "", fmt.Errorf("ExitCode: %d, output: %v",
   369  				running.ExitCode, running.Output)
   370  		}
   371  	}
   372  }
   373  
   374  func (c *ReplicaClient) get(url string, obj interface{}) error {
   375  	if !strings.HasPrefix(url, "http") {
   376  		url = c.address + url
   377  	}
   378  
   379  	resp, err := c.httpClient.Get(url)
   380  	if err != nil {
   381  		return err
   382  	}
   383  	defer resp.Body.Close()
   384  
   385  	return json.NewDecoder(resp.Body).Decode(obj)
   386  }
   387  
   388  func (c *ReplicaClient) post(path string, req, resp interface{}) error {
   389  	b, err := json.Marshal(req)
   390  	if err != nil {
   391  		return err
   392  	}
   393  
   394  	bodyType := "application/json"
   395  	url := path
   396  	if !strings.HasPrefix(url, "http") {
   397  		url = c.address + path
   398  	}
   399  
   400  	logrus.Debugf("POST %s", url)
   401  
   402  	httpResp, err := c.httpClient.Post(url, bodyType, bytes.NewBuffer(b))
   403  	if err != nil {
   404  		return err
   405  	}
   406  	defer httpResp.Body.Close()
   407  
   408  	if httpResp.StatusCode >= 300 {
   409  		content, _ := ioutil.ReadAll(httpResp.Body)
   410  		return fmt.Errorf("Bad response: %d %s: %s", httpResp.StatusCode, httpResp.Status, content)
   411  	}
   412  
   413  	if resp == nil {
   414  		return nil
   415  	}
   416  
   417  	return json.NewDecoder(httpResp.Body).Decode(resp)
   418  }
   419  
   420  func (c *ReplicaClient) HardLink(from, to string) error {
   421  	var running agent.Process
   422  	err := c.post(c.syncAgent+"/processes", &agent.Process{
   423  		ProcessType: "hardlink",
   424  		SrcFile:     from,
   425  		DestFile:    to,
   426  	}, &running)
   427  	if err != nil {
   428  		return err
   429  	}
   430  
   431  	start := 250 * time.Millisecond
   432  	for {
   433  		err := c.get(running.Links["self"], &running)
   434  		if err != nil {
   435  			return err
   436  		}
   437  
   438  		switch running.ExitCode {
   439  		case -2:
   440  			time.Sleep(start)
   441  			start = start * 2
   442  			if start > 1*time.Second {
   443  				start = 1 * time.Second
   444  			}
   445  		case 0:
   446  			return nil
   447  		default:
   448  			return fmt.Errorf("ExitCode: %d", running.ExitCode)
   449  		}
   450  	}
   451  }