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 }