1// Copyright 2019 Istio Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package framework 16 17import ( 18 "fmt" 19 "io" 20 "sync" 21 22 "github.com/hashicorp/go-multierror" 23 24 "istio.io/istio/pkg/test/framework/resource" 25 "istio.io/istio/pkg/test/scopes" 26) 27 28// scope hold resources in a particular scope. 29type scope struct { 30 // friendly name for the scope for debugging purposes. 31 id string 32 33 parent *scope 34 35 resources []resource.Resource 36 37 closers []io.Closer 38 39 children []*scope 40 41 closeChan chan struct{} 42 43 // Mutex to lock changes to resources, children, and closers that can be done concurrently 44 mu sync.Mutex 45} 46 47func newScope(id string, p *scope) *scope { 48 s := &scope{ 49 id: id, 50 parent: p, 51 closeChan: make(chan struct{}, 1), 52 } 53 54 if p != nil { 55 p.children = append(p.children, s) 56 } 57 58 return s 59} 60 61func (s *scope) add(r resource.Resource, id *resourceID) { 62 scopes.Framework.Debugf("Adding resource for tracking: %v", id) 63 s.mu.Lock() 64 defer s.mu.Unlock() 65 s.resources = append(s.resources, r) 66 67 if c, ok := r.(io.Closer); ok { 68 s.addCloser(c) 69 } 70} 71 72func (s *scope) addCloser(c io.Closer) { 73 s.closers = append(s.closers, c) 74} 75 76func (s *scope) done(nocleanup bool) error { 77 scopes.Framework.Debugf("Begin cleaning up scope: %v", s.id) 78 79 // First, wait for all of the children to be done. 80 for _, c := range s.children { 81 c.waitForDone() 82 } 83 84 // Upon returning, notify the parent that we're done. 85 defer func() { 86 close(s.closeChan) 87 }() 88 89 var err error 90 if !nocleanup { 91 92 // Do reverse walk for cleanup. 93 for i := len(s.closers) - 1; i >= 0; i-- { 94 c := s.closers[i] 95 96 name := "lambda" 97 if r, ok := c.(resource.Resource); ok { 98 name = fmt.Sprintf("resource %v", r.ID()) 99 } 100 101 scopes.Framework.Debugf("Begin cleaning up %s", name) 102 if e := c.Close(); e != nil { 103 scopes.Framework.Debugf("Error cleaning up %s: %v", name, e) 104 err = multierror.Append(err, e) 105 } 106 scopes.Framework.Debugf("Cleanup complete for %s", name) 107 } 108 } 109 s.mu.Lock() 110 s.resources = nil 111 s.closers = nil 112 s.mu.Unlock() 113 114 scopes.Framework.Debugf("Done cleaning up scope: %v", s.id) 115 return err 116} 117 118func (s *scope) waitForDone() { 119 <-s.closeChan 120} 121 122func (s *scope) dump() { 123 s.mu.Lock() 124 defer s.mu.Unlock() 125 for _, c := range s.children { 126 c.dump() 127 } 128 for _, c := range s.resources { 129 if d, ok := c.(resource.Dumper); ok { 130 d.Dump() 131 } 132 } 133} 134