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 }