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  }