github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/boskos/client/client.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package client 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "net/http" 24 "sync" 25 "time" 26 27 "k8s.io/test-infra/boskos/common" 28 ) 29 30 // Public Boskos client object 31 type Client struct { 32 owner string 33 url string 34 35 lock sync.Mutex 36 resources []string 37 } 38 39 // NewClient creates a boskos client, with boskos url and owner of the client. 40 func NewClient(owner string, url string) *Client { 41 42 client := &Client{ 43 url: url, 44 owner: owner, 45 } 46 47 return client 48 } 49 50 // public method 51 52 // Acquire asks boskos for a resource of certain type in certain state, and set the resource to dest state. 53 // Returns name of the resource on success. 54 func (c *Client) Acquire(rtype string, state string, dest string) (string, error) { 55 r, err := c.acquire(rtype, state, dest) 56 if err != nil { 57 return "", err 58 } 59 60 c.lock.Lock() 61 defer c.lock.Unlock() 62 if r != "" { 63 c.resources = append(c.resources, r) 64 } 65 66 return r, nil 67 } 68 69 // ReleaseAll returns all resource hold by the client back to boskos and set them to dest state. 70 func (c *Client) ReleaseAll(dest string) error { 71 c.lock.Lock() 72 73 if len(c.resources) == 0 { 74 c.lock.Unlock() 75 return fmt.Errorf("No holding resource") 76 } 77 c.lock.Unlock() 78 79 for { 80 r, ok := c.popResource() 81 if !ok { 82 break 83 } 84 85 if err := c.release(r, dest); err != nil { 86 return err 87 } 88 } 89 90 return nil 91 } 92 93 // ReleaseOne returns one of owned resource back to boskos and set it to dest state. 94 func (c *Client) ReleaseOne(name string, dest string) error { 95 c.lock.Lock() 96 defer c.lock.Unlock() 97 98 for idx, r := range c.resources { 99 if r == name { 100 c.resources[idx] = c.resources[len(c.resources)-1] 101 c.resources = c.resources[:len(c.resources)-1] 102 if err := c.release(r, dest); err != nil { 103 return err 104 } 105 return nil 106 } 107 } 108 109 return fmt.Errorf("No resource name %v", name) 110 } 111 112 // UpdateAll signals update for all resources hold by the client. 113 func (c *Client) UpdateAll(state string) error { 114 c.lock.Lock() 115 defer c.lock.Unlock() 116 117 if len(c.resources) == 0 { 118 return fmt.Errorf("No holding resource") 119 } 120 121 for _, r := range c.resources { 122 if err := c.update(r, state); err != nil { 123 return err 124 } 125 } 126 127 return nil 128 } 129 130 // UpdateOne signals update for one of the resource hold by the client. 131 func (c *Client) UpdateOne(name string, state string) error { 132 c.lock.Lock() 133 defer c.lock.Unlock() 134 135 for _, r := range c.resources { 136 if r == name { 137 if err := c.update(r, state); err != nil { 138 return err 139 } 140 return nil 141 } 142 } 143 144 return fmt.Errorf("No resource name %v", name) 145 } 146 147 // Reset will scan all boskos resources of type, in state, last updated before expire, and set them to dest state. 148 // Returns a map of {resourceName:owner} for further actions. 149 func (c *Client) Reset(rtype string, state string, expire time.Duration, dest string) (map[string]string, error) { 150 return c.reset(rtype, state, expire, dest) 151 } 152 153 // Metric will query current metric for target resource type. 154 // Return a common.Metric object on success. 155 func (c *Client) Metric(rtype string) (common.Metric, error) { 156 return c.metric(rtype) 157 } 158 159 // HasResource tells if current client holds any resources 160 func (c *Client) HasResource() bool { 161 return len(c.resources) > 0 162 } 163 164 // private methods 165 166 func (c *Client) popResource() (string, bool) { 167 c.lock.Lock() 168 defer c.lock.Unlock() 169 170 if len(c.resources) == 0 { 171 return "", false 172 } 173 174 r := c.resources[len(c.resources)-1] 175 c.resources = c.resources[:len(c.resources)-1] 176 return r, true 177 } 178 179 func (c *Client) acquire(rtype string, state string, dest string) (string, error) { 180 resp, err := http.Post(fmt.Sprintf("%v/acquire?type=%v&state=%v&dest=%v&owner=%v", c.url, rtype, state, dest, c.owner), "", nil) 181 if err != nil { 182 return "", err 183 } 184 defer resp.Body.Close() 185 186 if resp.StatusCode == http.StatusOK { 187 body, err := ioutil.ReadAll(resp.Body) 188 if err != nil { 189 return "", err 190 } 191 192 var res = new(common.Resource) 193 err = json.Unmarshal(body, &res) 194 if err != nil { 195 return "", err 196 } 197 return res.Name, nil 198 } else if resp.StatusCode == http.StatusNotFound { 199 return "", nil 200 } 201 202 return "", fmt.Errorf("Status %s, StatusCode %v", resp.Status, resp.StatusCode) 203 } 204 205 func (c *Client) release(name string, dest string) error { 206 resp, err := http.Post(fmt.Sprintf("%v/release?name=%v&dest=%v&owner=%v", c.url, name, dest, c.owner), "", nil) 207 if err != nil { 208 return err 209 } 210 defer resp.Body.Close() 211 212 if resp.StatusCode != http.StatusOK { 213 return fmt.Errorf("Status %s, StatusCode %v", resp.Status, resp.StatusCode) 214 } 215 return nil 216 } 217 218 func (c *Client) update(name string, state string) error { 219 resp, err := http.Post(fmt.Sprintf("%v/update?name=%v&owner=%v&state=%v", c.url, name, c.owner, state), "", nil) 220 if err != nil { 221 return err 222 } 223 defer resp.Body.Close() 224 225 if resp.StatusCode != http.StatusOK { 226 return fmt.Errorf("Status %s, StatusCode %v", resp.Status, resp.StatusCode) 227 } 228 return nil 229 } 230 231 func (c *Client) reset(rtype string, state string, expire time.Duration, dest string) (map[string]string, error) { 232 rmap := make(map[string]string) 233 resp, err := http.Post(fmt.Sprintf("%v/reset?type=%v&state=%v&expire=%v&dest=%v", c.url, rtype, state, expire.String(), dest), "", nil) 234 if err != nil { 235 return rmap, err 236 } 237 defer resp.Body.Close() 238 239 if resp.StatusCode == http.StatusOK { 240 body, err := ioutil.ReadAll(resp.Body) 241 if err != nil { 242 return rmap, err 243 } 244 245 err = json.Unmarshal(body, &rmap) 246 if err != nil { 247 return rmap, err 248 } 249 return rmap, nil 250 } 251 252 return rmap, fmt.Errorf("Status %s, StatusCode %v", resp.Status, resp.StatusCode) 253 } 254 255 func (c *Client) metric(rtype string) (common.Metric, error) { 256 var metric common.Metric 257 resp, err := http.Get(fmt.Sprintf("%v/metric?type=%v", c.url, rtype)) 258 if err != nil { 259 return metric, err 260 } 261 defer resp.Body.Close() 262 263 if resp.StatusCode != http.StatusOK { 264 return metric, fmt.Errorf("Status %s, StatusCode %v", resp.Status, resp.StatusCode) 265 } 266 267 body, err := ioutil.ReadAll(resp.Body) 268 if err != nil { 269 return metric, err 270 } 271 272 err = json.Unmarshal(body, &metric) 273 if err != nil { 274 return metric, err 275 } 276 277 return metric, nil 278 }