github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/rpc/directory.go (about)

     1  package rpc
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"net/rpc"
     7  
     8  	"github.com/hashicorp/otto/directory"
     9  )
    10  
    11  // Directory is an implementatin of directory.Backend that communicates
    12  // over RPC.
    13  type Directory struct {
    14  	Broker *muxBroker
    15  	Client *rpc.Client
    16  	Name   string
    17  }
    18  
    19  func (d *Directory) PutBlob(key string, data *directory.BlobData) error {
    20  	// Serve the data
    21  	id := d.Broker.NextId()
    22  	doneCh := make(chan struct{})
    23  	go serveSingleCopy("putBlob: "+key, d.Broker, doneCh, id, nil, data.Data)
    24  
    25  	// Run it
    26  	var resp ErrorResponse
    27  	args := &DirPutBlobArgs{
    28  		Key: key,
    29  		Id:  id,
    30  	}
    31  	err := d.Client.Call(d.Name+".PutBlob", args, &resp)
    32  	if err != nil {
    33  		return err
    34  	}
    35  	if resp.Error != nil {
    36  		err = resp.Error
    37  		return err
    38  	}
    39  
    40  	// NOTE: We don't wait on the doneCh because the data is being read
    41  	// on the other side and the contract is that it will exaust the
    42  	// data on success.
    43  
    44  	return nil
    45  }
    46  
    47  func (d *Directory) GetBlob(key string) (*directory.BlobData, error) {
    48  	// Create the result
    49  	pr, pw := io.Pipe()
    50  	result := &directory.BlobData{
    51  		Key:  key,
    52  		Data: pr,
    53  	}
    54  
    55  	// Download the data
    56  	id := d.Broker.NextId()
    57  	doneCh := make(chan struct{})
    58  	go serveSingleCopy("getBlob: "+key, d.Broker, doneCh, id, pw, nil)
    59  	go func() {
    60  		// We wait for the data copying to be complete. When it is, we
    61  		// want to close the writer side of our pipe so that the result
    62  		// also gets an EOF.
    63  		<-doneCh
    64  		pw.Close()
    65  	}()
    66  
    67  	// Run it
    68  	var resp DirGetBlobResponse
    69  	args := &DirGetBlobArgs{
    70  		Key: key,
    71  		Id:  id,
    72  	}
    73  	err := d.Client.Call(d.Name+".GetBlob", args, &resp)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	if resp.Error != nil {
    78  		err = resp.Error
    79  		return nil, err
    80  	}
    81  	if !resp.Ok {
    82  		// No return value (blob key didn't exist)
    83  		result = nil
    84  	}
    85  
    86  	// NOTE: We don't wait on the doneCh because the data is put as a
    87  	// io.Reader into the BlobData. It is up to the end user to read
    88  	// until it is exausted.
    89  
    90  	return result, nil
    91  }
    92  
    93  func (d *Directory) PutInfra(v *directory.Infra) error {
    94  	var resp DirPutInfraResponse
    95  	err := d.Client.Call(d.Name+".PutInfra", v, &resp)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	if resp.Error != nil {
   100  		err = resp.Error
   101  		return err
   102  	}
   103  
   104  	*v = *resp.Value
   105  	return nil
   106  }
   107  
   108  func (d *Directory) GetInfra(v *directory.Infra) (*directory.Infra, error) {
   109  	var resp DirGetInfraResponse
   110  	err := d.Client.Call(d.Name+".GetInfra", v, &resp)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if resp.Error != nil {
   115  		err = resp.Error
   116  		return nil, err
   117  	}
   118  
   119  	return resp.Value, nil
   120  }
   121  
   122  func (d *Directory) PutDev(v *directory.Dev) error {
   123  	var resp DirPutDevResponse
   124  	err := d.Client.Call(d.Name+".PutDev", v, &resp)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if resp.Error != nil {
   129  		err = resp.Error
   130  		return err
   131  	}
   132  
   133  	*v = *resp.Value
   134  	return nil
   135  }
   136  
   137  func (d *Directory) GetDev(v *directory.Dev) (*directory.Dev, error) {
   138  	var resp DirGetDevResponse
   139  	err := d.Client.Call(d.Name+".GetDev", v, &resp)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	if resp.Error != nil {
   144  		err = resp.Error
   145  		return nil, err
   146  	}
   147  
   148  	return resp.Value, nil
   149  }
   150  
   151  func (d *Directory) DeleteDev(v *directory.Dev) error {
   152  	var resp ErrorResponse
   153  	err := d.Client.Call(d.Name+".DeleteDev", v, &resp)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	if resp.Error != nil {
   158  		err = resp.Error
   159  		return err
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func (d *Directory) PutBuild(v *directory.Build) error {
   166  	var resp DirPutBuildResponse
   167  	err := d.Client.Call(d.Name+".PutBuild", v, &resp)
   168  	if err != nil {
   169  		return err
   170  	}
   171  	if resp.Error != nil {
   172  		err = resp.Error
   173  		return err
   174  	}
   175  
   176  	*v = *resp.Value
   177  	return nil
   178  }
   179  
   180  func (d *Directory) GetBuild(v *directory.Build) (*directory.Build, error) {
   181  	var resp DirGetBuildResponse
   182  	err := d.Client.Call(d.Name+".GetBuild", v, &resp)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	if resp.Error != nil {
   187  		err = resp.Error
   188  		return nil, err
   189  	}
   190  
   191  	return resp.Value, nil
   192  }
   193  
   194  func (d *Directory) PutDeploy(v *directory.Deploy) error {
   195  	var resp DirPutDeployResponse
   196  	err := d.Client.Call(d.Name+".PutDeploy", v, &resp)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	if resp.Error != nil {
   201  		err = resp.Error
   202  		return err
   203  	}
   204  
   205  	*v = *resp.Value
   206  	return nil
   207  }
   208  
   209  func (d *Directory) GetDeploy(v *directory.Deploy) (*directory.Deploy, error) {
   210  	var resp DirGetDeployResponse
   211  	err := d.Client.Call(d.Name+".GetDeploy", v, &resp)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	if resp.Error != nil {
   216  		err = resp.Error
   217  		return nil, err
   218  	}
   219  
   220  	return resp.Value, nil
   221  }
   222  
   223  type DirPutBlobArgs struct {
   224  	Key string
   225  	Id  uint32
   226  }
   227  
   228  type DirGetBlobArgs struct {
   229  	Key string
   230  	Id  uint32
   231  }
   232  
   233  type DirGetBlobResponse struct {
   234  	Ok    bool
   235  	Error *BasicError
   236  }
   237  
   238  type DirGetInfraResponse struct {
   239  	Value *directory.Infra
   240  	Error *BasicError
   241  }
   242  
   243  type DirGetDevResponse struct {
   244  	Value *directory.Dev
   245  	Error *BasicError
   246  }
   247  
   248  type DirGetBuildResponse struct {
   249  	Value *directory.Build
   250  	Error *BasicError
   251  }
   252  
   253  type DirGetDeployResponse struct {
   254  	Value *directory.Deploy
   255  	Error *BasicError
   256  }
   257  
   258  type DirPutInfraResponse struct {
   259  	Value *directory.Infra
   260  	Error *BasicError
   261  }
   262  
   263  type DirPutDevResponse struct {
   264  	Value *directory.Dev
   265  	Error *BasicError
   266  }
   267  
   268  type DirPutBuildResponse struct {
   269  	Value *directory.Build
   270  	Error *BasicError
   271  }
   272  
   273  type DirPutDeployResponse struct {
   274  	Value *directory.Deploy
   275  	Error *BasicError
   276  }
   277  
   278  // DirectoryServer is a net/rpc compatible structure for serving
   279  // a directory backend. This should not be used directly.
   280  type DirectoryServer struct {
   281  	Broker    *muxBroker
   282  	Directory directory.Backend
   283  }
   284  
   285  func (s *DirectoryServer) PutBlob(
   286  	args *DirPutBlobArgs,
   287  	reply *ErrorResponse) error {
   288  	// Connect to the data stream
   289  	conn, err := s.Broker.Dial(args.Id)
   290  	if err != nil {
   291  		*reply = ErrorResponse{Error: NewBasicError(err)}
   292  		return nil
   293  	}
   294  	defer conn.Close()
   295  
   296  	err = s.Directory.PutBlob(args.Key, &directory.BlobData{
   297  		Data: conn,
   298  	})
   299  	*reply = ErrorResponse{
   300  		Error: NewBasicError(err),
   301  	}
   302  	return nil
   303  }
   304  
   305  func (s *DirectoryServer) GetBlob(
   306  	args *DirGetBlobArgs,
   307  	reply *DirGetBlobResponse) error {
   308  	// Connect to the data stream. We need to connect first because
   309  	// we need to at least connect and close the connection otherwise
   310  	// we'll leak a serveSingleCopy goroutine. This is why this cannot be
   311  	// below the GetBlob call below.
   312  	conn, err := s.Broker.Dial(args.Id)
   313  	if err != nil {
   314  		*reply = DirGetBlobResponse{Error: NewBasicError(err)}
   315  		return nil
   316  	}
   317  
   318  	// Get the blob. If we have an error we return right away
   319  	result, err := s.Directory.GetBlob(args.Key)
   320  	*reply = DirGetBlobResponse{
   321  		Ok:    result != nil,
   322  		Error: NewBasicError(err),
   323  	}
   324  	if err != nil {
   325  		conn.Close()
   326  		return nil
   327  	}
   328  	if result == nil {
   329  		// There is no data, so just close the data stream and return
   330  		conn.Close()
   331  		return nil
   332  	}
   333  
   334  	// No error! We need to copy the data from the blob into the
   335  	// resulting connection. We do this in a goroutine though since the
   336  	// data can be read at any speed.
   337  	go func() {
   338  		defer conn.Close()
   339  		defer result.Close()
   340  		if _, err := io.Copy(conn, result.Data); err != nil {
   341  			log.Printf(
   342  				"[ERR] rpc/directory: error copying getBlob data '%s': %s",
   343  				args.Key,
   344  				err)
   345  		}
   346  	}()
   347  
   348  	return nil
   349  }
   350  
   351  func (s *DirectoryServer) PutInfra(
   352  	args *directory.Infra,
   353  	reply *DirPutInfraResponse) error {
   354  	err := s.Directory.PutInfra(args)
   355  	*reply = DirPutInfraResponse{
   356  		Value: args,
   357  		Error: NewBasicError(err),
   358  	}
   359  	return nil
   360  }
   361  
   362  func (s *DirectoryServer) GetInfra(
   363  	args *directory.Infra,
   364  	reply *DirGetInfraResponse) error {
   365  	result, err := s.Directory.GetInfra(args)
   366  	*reply = DirGetInfraResponse{
   367  		Value: result,
   368  		Error: NewBasicError(err),
   369  	}
   370  	return nil
   371  }
   372  
   373  func (s *DirectoryServer) PutDev(
   374  	args *directory.Dev,
   375  	reply *DirPutDevResponse) error {
   376  	err := s.Directory.PutDev(args)
   377  	*reply = DirPutDevResponse{
   378  		Value: args,
   379  		Error: NewBasicError(err),
   380  	}
   381  	return nil
   382  }
   383  
   384  func (s *DirectoryServer) GetDev(
   385  	args *directory.Dev,
   386  	reply *DirGetDevResponse) error {
   387  	result, err := s.Directory.GetDev(args)
   388  	*reply = DirGetDevResponse{
   389  		Value: result,
   390  		Error: NewBasicError(err),
   391  	}
   392  	return nil
   393  }
   394  
   395  func (s *DirectoryServer) DeleteDev(
   396  	args *directory.Dev,
   397  	reply *ErrorResponse) error {
   398  	err := s.Directory.DeleteDev(args)
   399  	*reply = ErrorResponse{
   400  		Error: NewBasicError(err),
   401  	}
   402  	return nil
   403  }
   404  
   405  func (s *DirectoryServer) PutBuild(
   406  	args *directory.Build,
   407  	reply *DirPutBuildResponse) error {
   408  	err := s.Directory.PutBuild(args)
   409  	*reply = DirPutBuildResponse{
   410  		Value: args,
   411  		Error: NewBasicError(err),
   412  	}
   413  	return nil
   414  }
   415  
   416  func (s *DirectoryServer) GetBuild(
   417  	args *directory.Build,
   418  	reply *DirGetBuildResponse) error {
   419  	result, err := s.Directory.GetBuild(args)
   420  	*reply = DirGetBuildResponse{
   421  		Value: result,
   422  		Error: NewBasicError(err),
   423  	}
   424  	return nil
   425  }
   426  
   427  func (s *DirectoryServer) PutDeploy(
   428  	args *directory.Deploy,
   429  	reply *DirPutDeployResponse) error {
   430  	err := s.Directory.PutDeploy(args)
   431  	*reply = DirPutDeployResponse{
   432  		Value: args,
   433  		Error: NewBasicError(err),
   434  	}
   435  	return nil
   436  }
   437  
   438  func (s *DirectoryServer) GetDeploy(
   439  	args *directory.Deploy,
   440  	reply *DirGetDeployResponse) error {
   441  	result, err := s.Directory.GetDeploy(args)
   442  	*reply = DirGetDeployResponse{
   443  		Value: result,
   444  		Error: NewBasicError(err),
   445  	}
   446  	return nil
   447  }