github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/boskos/mason/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 mason
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"k8s.io/test-infra/boskos/common"
    24  
    25  	"github.com/hashicorp/go-multierror"
    26  	"github.com/sirupsen/logrus"
    27  )
    28  
    29  // Client extends boskos client with support with resource with leased resources
    30  type Client struct {
    31  	basic     boskosClient
    32  	resources map[string]common.Resource
    33  	lock      sync.RWMutex
    34  }
    35  
    36  // NewClient creates a new client from a boskosClient interface
    37  func NewClient(boskosClient boskosClient) *Client {
    38  	return &Client{
    39  		basic:     boskosClient,
    40  		resources: map[string]common.Resource{},
    41  	}
    42  }
    43  
    44  // Acquire gets a resource with associated leased resources
    45  func (c *Client) Acquire(rtype, state, dest string) (*common.Resource, error) {
    46  	var resourcesToRelease []common.Resource
    47  	releaseOnFailure := func() {
    48  		for _, r := range resourcesToRelease {
    49  			if err := c.basic.ReleaseOne(r.Name, common.Dirty); err != nil {
    50  				logrus.WithError(err).Warningf("failed to release resource %s", r.Name)
    51  			}
    52  		}
    53  	}
    54  	res, err := c.basic.Acquire(rtype, state, dest)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	var leasedResources common.LeasedResources
    59  	if err = res.UserData.Extract(LeasedResources, &leasedResources); err != nil {
    60  		if _, ok := err.(*common.UserDataNotFound); !ok {
    61  			logrus.WithError(err).Errorf("cannot parse %s from User Data", LeasedResources)
    62  			return nil, err
    63  		}
    64  	}
    65  	resourcesToRelease = append(resourcesToRelease, *res)
    66  	resources, err := c.basic.AcquireByState(res.Name, dest, leasedResources)
    67  	if err != nil {
    68  		releaseOnFailure()
    69  		return nil, err
    70  	}
    71  	resourcesToRelease = append(resourcesToRelease, resources...)
    72  	c.updateResource(*res)
    73  	return res, nil
    74  }
    75  
    76  // ReleaseOne will release a resource as well as leased resources associated to it
    77  func (c *Client) ReleaseOne(name, dest string) (allErrors error) {
    78  	res, err := c.getResource(name)
    79  	if err != nil {
    80  		allErrors = err
    81  		return
    82  	}
    83  	resourceNames := []string{name}
    84  	var leasedResources common.LeasedResources
    85  	if err := res.UserData.Extract(LeasedResources, &leasedResources); err != nil {
    86  		if _, ok := err.(*common.UserDataNotFound); !ok {
    87  			logrus.WithError(err).Errorf("cannot parse %s from User Data", LeasedResources)
    88  			allErrors = multierror.Append(allErrors, err)
    89  			if err := c.basic.ReleaseOne(name, dest); err != nil {
    90  				logrus.WithError(err).Warningf("failed to release resource %s", name)
    91  				allErrors = multierror.Append(allErrors, err)
    92  			}
    93  			return
    94  		}
    95  	}
    96  	resourceNames = append(resourceNames, leasedResources...)
    97  	for _, n := range resourceNames {
    98  		if err := c.basic.ReleaseOne(n, dest); err != nil {
    99  			logrus.WithError(err).Warningf("failed to release resource %s", n)
   100  			allErrors = multierror.Append(allErrors, err)
   101  		}
   102  	}
   103  	c.deleteResource(name)
   104  	return
   105  }
   106  
   107  // UpdateAll updates all the acquired resources with a given state
   108  func (c *Client) UpdateAll(state string) error {
   109  	return c.basic.UpdateAll(state)
   110  }
   111  
   112  func (c *Client) updateResource(r common.Resource) {
   113  	c.lock.Lock()
   114  	defer c.lock.Unlock()
   115  	c.resources[r.Name] = r
   116  }
   117  
   118  func (c *Client) getResource(name string) (*common.Resource, error) {
   119  	c.lock.Lock()
   120  	defer c.lock.Unlock()
   121  	res, ok := c.resources[name]
   122  	if !ok {
   123  		return nil, fmt.Errorf("resource %s not found", name)
   124  	}
   125  	return &res, nil
   126  }
   127  
   128  func (c *Client) deleteResource(name string) {
   129  	c.lock.Lock()
   130  	defer c.lock.Unlock()
   131  	delete(c.resources, name)
   132  }