github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/base/caller.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package base
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"net/http"
    10  	"net/url"
    11  
    12  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
    13  	"github.com/juju/names/v5"
    14  	"gopkg.in/httprequest.v1"
    15  	"gopkg.in/macaroon.v2"
    16  )
    17  
    18  //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/caller_mock.go github.com/juju/juju/api/base APICaller,FacadeCaller
    19  
    20  // APICaller is implemented by the client-facing State object.
    21  // It defines the lowest level of API calls and is used by
    22  // the various API implementations to actually make
    23  // the calls to the API. It should not be used outside
    24  // of tests or the api/* hierarchy.
    25  type APICaller interface {
    26  	// APICall makes a call to the API server with the given object type,
    27  	// id, request and parameters. The response is filled in with the
    28  	// call's result if the call is successful.
    29  	APICall(objType string, version int, id, request string, params, response interface{}) error
    30  
    31  	// BestFacadeVersion returns the newest version of 'objType' that this
    32  	// client can use with the current API server.
    33  	BestFacadeVersion(facade string) int
    34  
    35  	// ModelTag returns the tag of the model the client is connected
    36  	// to if there is one. It returns false for a controller-only connection.
    37  	ModelTag() (names.ModelTag, bool)
    38  
    39  	// HTTPClient returns a httprequest.Client that can be used
    40  	// to make HTTP requests to the API. URLs passed to the client
    41  	// will be made relative to the API host and the current model.
    42  	//
    43  	// Note that the URLs in HTTP requests passed to the Client.Do
    44  	// method should not include a host part.
    45  	HTTPClient() (*httprequest.Client, error)
    46  
    47  	// RootHTTPClient returns an httprequest.Client pointing to
    48  	// the API server root path.
    49  	RootHTTPClient() (*httprequest.Client, error)
    50  
    51  	// BakeryClient returns the bakery client for this connection.
    52  	BakeryClient() MacaroonDischarger
    53  
    54  	// Context returns the standard context for this connection.
    55  	Context() context.Context
    56  
    57  	StreamConnector
    58  	ControllerStreamConnector
    59  }
    60  
    61  // MacaroonDischarger instances provide a method to discharge macaroons.
    62  type MacaroonDischarger interface {
    63  	// DischargeAll attempts to acquire discharge macaroons for all the
    64  	// third party caveats in m, and returns a slice containing all
    65  	// of them bound to m.
    66  	DischargeAll(ctx context.Context, m *bakery.Macaroon) (macaroon.Slice, error)
    67  }
    68  
    69  // StreamConnector is implemented by the client-facing State object.
    70  type StreamConnector interface {
    71  	// ConnectStream connects to the given HTTP websocket
    72  	// endpoint path (interpreted relative to the receiver's
    73  	// model) and returns the resulting connection.
    74  	// The given parameters are used as URL query values
    75  	// when making the initial HTTP request.
    76  	//
    77  	// The path must start with a "/".
    78  	ConnectStream(path string, attrs url.Values) (Stream, error)
    79  }
    80  
    81  // ControllerStreamConnector is implemented by the client-facing State object.
    82  type ControllerStreamConnector interface {
    83  	// ConnectControllerStream connects to the given HTTP websocket
    84  	// endpoint path and returns the resulting connection. The given
    85  	// values are used as URL query values when making the initial
    86  	// HTTP request. Headers passed in will be added to the HTTP
    87  	// request.
    88  	//
    89  	// The path must be absolute and can't start with "/model".
    90  	ConnectControllerStream(path string, attrs url.Values, headers http.Header) (Stream, error)
    91  }
    92  
    93  // Stream represents a streaming connection to the API.
    94  type Stream interface {
    95  	io.Closer
    96  
    97  	// NextReader is used to get direct access to the underlying Read methods
    98  	// on the websocket. Mostly just to read the initial error response.
    99  	NextReader() (messageType int, r io.Reader, err error)
   100  
   101  	// WriteJSON encodes the given value as JSON
   102  	// and writes it to the connection.
   103  	WriteJSON(v interface{}) error
   104  
   105  	// ReadJSON reads a JSON value from the stream
   106  	// and decodes it into the element pointed to by
   107  	// the given value, which should be a pointer.
   108  	ReadJSON(v interface{}) error
   109  }
   110  
   111  // FacadeCaller is a wrapper for the common paradigm that a given client just
   112  // wants to make calls on a facade using the best known version of the API. And
   113  // without dealing with an id parameter.
   114  type FacadeCaller interface {
   115  	// FacadeCall will place a request against the API using the requested
   116  	// Facade and the best version that the API server supports that is
   117  	// also known to the client.
   118  	FacadeCall(request string, params, response interface{}) error
   119  
   120  	// Name returns the facade name.
   121  	Name() string
   122  
   123  	// BestAPIVersion returns the API version that we were able to
   124  	// determine is supported by both the client and the API Server
   125  	BestAPIVersion() int
   126  
   127  	// RawAPICaller returns the wrapped APICaller. This can be used if you need
   128  	// to switch what Facade you are calling (such as Facades that return
   129  	// Watchers and then need to use the Watcher facade)
   130  	RawAPICaller() APICaller
   131  }
   132  
   133  type facadeCaller struct {
   134  	facadeName  string
   135  	bestVersion int
   136  	caller      APICaller
   137  }
   138  
   139  var _ FacadeCaller = facadeCaller{}
   140  
   141  // FacadeCall will place a request against the API using the requested
   142  // Facade and the best version that the API server supports that is
   143  // also known to the client. (id is always passed as the empty string.)
   144  func (fc facadeCaller) FacadeCall(request string, params, response interface{}) error {
   145  	return fc.caller.APICall(
   146  		fc.facadeName, fc.bestVersion, "",
   147  		request, params, response)
   148  }
   149  
   150  // Name returns the facade name.
   151  func (fc facadeCaller) Name() string {
   152  	return fc.facadeName
   153  }
   154  
   155  // BestAPIVersion returns the version of the Facade that is going to be used
   156  // for calls. It is determined using the algorithm defined in api
   157  // BestFacadeVersion. Callers can use this to determine what methods must be
   158  // used for compatibility.
   159  func (fc facadeCaller) BestAPIVersion() int {
   160  	return fc.bestVersion
   161  }
   162  
   163  // RawAPICaller returns the wrapped APICaller. This can be used if you need to
   164  // switch what Facade you are calling (such as Facades that return Watchers and
   165  // then need to use the Watcher facade)
   166  func (fc facadeCaller) RawAPICaller() APICaller {
   167  	return fc.caller
   168  }
   169  
   170  // NewFacadeCaller wraps an APICaller for a given facade name and the
   171  // best available version.
   172  func NewFacadeCaller(caller APICaller, facadeName string) FacadeCaller {
   173  	return NewFacadeCallerForVersion(caller, facadeName, caller.BestFacadeVersion(facadeName))
   174  }
   175  
   176  // NewFacadeCallerForVersion wraps an APICaller for a given facade
   177  // name and version.
   178  func NewFacadeCallerForVersion(caller APICaller, facadeName string, version int) FacadeCaller {
   179  	return facadeCaller{
   180  		facadeName:  facadeName,
   181  		bestVersion: version,
   182  		caller:      caller,
   183  	}
   184  }