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