github.com/micro/go-micro/v2@v2.9.1/util/kubernetes/api/request.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "net/http" 11 "net/url" 12 13 "github.com/micro/go-micro/v2/logger" 14 ) 15 16 // Request is used to construct a http request for the k8s API. 17 type Request struct { 18 // the request context 19 context context.Context 20 client *http.Client 21 header http.Header 22 params url.Values 23 method string 24 host string 25 namespace string 26 27 resource string 28 resourceName *string 29 subResource *string 30 body io.Reader 31 32 err error 33 } 34 35 // Params is the object to pass in to set parameters 36 // on a request. 37 type Params struct { 38 LabelSelector map[string]string 39 Annotations map[string]string 40 Additional map[string]string 41 } 42 43 // verb sets method 44 func (r *Request) verb(method string) *Request { 45 r.method = method 46 return r 47 } 48 49 func (r *Request) Context(ctx context.Context) { 50 r.context = ctx 51 } 52 53 // Get request 54 func (r *Request) Get() *Request { 55 return r.verb("GET") 56 } 57 58 // Post request 59 func (r *Request) Post() *Request { 60 return r.verb("POST") 61 } 62 63 // Put request 64 func (r *Request) Put() *Request { 65 return r.verb("PUT") 66 } 67 68 // Patch request 69 func (r *Request) Patch() *Request { 70 return r.verb("PATCH") 71 } 72 73 // Delete request 74 func (r *Request) Delete() *Request { 75 return r.verb("DELETE") 76 } 77 78 // Namespace is to set the namespace to operate on 79 func (r *Request) Namespace(s string) *Request { 80 if len(s) > 0 { 81 r.namespace = s 82 } 83 return r 84 } 85 86 // Resource is the type of resource the operation is 87 // for, such as "services", "endpoints" or "pods" 88 func (r *Request) Resource(s string) *Request { 89 r.resource = s 90 return r 91 } 92 93 // SubResource sets a subresource on a resource, 94 // e.g. pods/log for pod logs 95 func (r *Request) SubResource(s string) *Request { 96 r.subResource = &s 97 return r 98 } 99 100 // Name is for targeting a specific resource by id 101 func (r *Request) Name(s string) *Request { 102 r.resourceName = &s 103 return r 104 } 105 106 // Body pass in a body to set, this is for POST, PUT and PATCH requests 107 func (r *Request) Body(in interface{}) *Request { 108 b := new(bytes.Buffer) 109 // if we're not sending YAML request, we encode to JSON 110 if r.header.Get("Content-Type") != "application/yaml" { 111 if err := json.NewEncoder(b).Encode(&in); err != nil { 112 r.err = err 113 return r 114 } 115 r.body = b 116 return r 117 } 118 119 // if application/yaml is set, we assume we get a raw bytes so we just copy over 120 body, ok := in.(io.Reader) 121 if !ok { 122 r.err = errors.New("invalid data") 123 return r 124 } 125 // copy over data to the bytes buffer 126 if _, err := io.Copy(b, body); err != nil { 127 r.err = err 128 return r 129 } 130 131 r.body = b 132 return r 133 } 134 135 // Params isused to set paramters on a request 136 func (r *Request) Params(p *Params) *Request { 137 for k, v := range p.LabelSelector { 138 // create new key=value pair 139 value := fmt.Sprintf("%s=%s", k, v) 140 // check if there's an existing value 141 if label := r.params.Get("labelSelector"); len(label) > 0 { 142 value = fmt.Sprintf("%s,%s", label, value) 143 } 144 // set and overwrite the value 145 r.params.Set("labelSelector", value) 146 } 147 for k, v := range p.Additional { 148 r.params.Set(k, v) 149 } 150 151 return r 152 } 153 154 // SetHeader sets a header on a request with 155 // a `key` and `value` 156 func (r *Request) SetHeader(key, value string) *Request { 157 r.header.Add(key, value) 158 return r 159 } 160 161 // request builds the http.Request from the options 162 func (r *Request) request() (*http.Request, error) { 163 var url string 164 switch r.resource { 165 case "namespace": 166 // /api/v1/namespaces/ 167 url = fmt.Sprintf("%s/api/v1/namespaces/", r.host) 168 case "deployment": 169 // /apis/apps/v1/namespaces/{namespace}/deployments/{name} 170 url = fmt.Sprintf("%s/apis/apps/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource) 171 default: 172 // /api/v1/namespaces/{namespace}/{resource} 173 url = fmt.Sprintf("%s/api/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource) 174 } 175 176 // append resourceName if it is present 177 if r.resourceName != nil { 178 url += *r.resourceName 179 if r.subResource != nil { 180 url += "/" + *r.subResource 181 } 182 } 183 184 // append any query params 185 if len(r.params) > 0 { 186 url += "?" + r.params.Encode() 187 } 188 189 var req *http.Request 190 var err error 191 192 // build request 193 if r.context != nil { 194 req, err = http.NewRequestWithContext(r.context, r.method, url, r.body) 195 } else { 196 req, err = http.NewRequest(r.method, url, r.body) 197 } 198 if err != nil { 199 return nil, err 200 } 201 202 // set headers on request 203 req.Header = r.header 204 return req, nil 205 } 206 207 // Do builds and triggers the request 208 func (r *Request) Do() *Response { 209 if r.err != nil { 210 return &Response{ 211 err: r.err, 212 } 213 } 214 215 req, err := r.request() 216 if err != nil { 217 return &Response{ 218 err: err, 219 } 220 } 221 222 logger.Debugf("[Kubernetes] %v %v", req.Method, req.URL.String()) 223 res, err := r.client.Do(req) 224 if err != nil { 225 return &Response{ 226 err: err, 227 } 228 } 229 230 // return res, err 231 return newResponse(res, err) 232 } 233 234 // Raw performs a Raw HTTP request to the Kubernetes API 235 func (r *Request) Raw() (*http.Response, error) { 236 req, err := r.request() 237 if err != nil { 238 return nil, err 239 } 240 241 res, err := r.client.Do(req) 242 if err != nil { 243 return nil, err 244 } 245 return res, nil 246 } 247 248 // Options ... 249 type Options struct { 250 Host string 251 Namespace string 252 BearerToken *string 253 Client *http.Client 254 } 255 256 // NewRequest creates a k8s api request 257 func NewRequest(opts *Options) *Request { 258 req := &Request{ 259 header: make(http.Header), 260 params: make(url.Values), 261 client: opts.Client, 262 namespace: opts.Namespace, 263 host: opts.Host, 264 } 265 266 if opts.BearerToken != nil { 267 req.SetHeader("Authorization", "Bearer "+*opts.BearerToken) 268 } 269 270 return req 271 }