github.com/abayer/test-infra@v0.0.5/prow/cmd/deck/tide.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 main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/sirupsen/logrus"
    28  
    29  	"k8s.io/test-infra/prow/config"
    30  	"k8s.io/test-infra/prow/tide"
    31  )
    32  
    33  type tideData struct {
    34  	Queries     []string
    35  	TideQueries []config.TideQuery
    36  	Pools       []tide.Pool
    37  }
    38  
    39  type tideAgent struct {
    40  	log          *logrus.Entry
    41  	path         string
    42  	updatePeriod func() time.Duration
    43  
    44  	// Config for hiding repos
    45  	hiddenRepos []string
    46  	hiddenOnly  bool
    47  
    48  	sync.Mutex
    49  	pools []tide.Pool
    50  }
    51  
    52  func (ta *tideAgent) start() {
    53  	startTime := time.Now()
    54  	if err := ta.update(); err != nil {
    55  		ta.log.WithError(err).Error("Updating pool for the first time.")
    56  	}
    57  	go func() {
    58  		for {
    59  			time.Sleep(time.Until(startTime.Add(ta.updatePeriod())))
    60  			startTime = time.Now()
    61  			if err := ta.update(); err != nil {
    62  				ta.log.WithError(err).Error("Updating pool.")
    63  			}
    64  		}
    65  	}()
    66  }
    67  
    68  func (ta *tideAgent) update() error {
    69  	var pools []tide.Pool
    70  	var resp *http.Response
    71  	var err error
    72  	for i := 0; i < 3; i++ {
    73  		if err != nil {
    74  			ta.log.WithError(err).Warning("Tide request failed. Retrying.")
    75  			time.Sleep(5 * time.Second)
    76  		}
    77  		resp, err = http.Get(ta.path)
    78  		if err == nil {
    79  			defer resp.Body.Close()
    80  			if resp.StatusCode < 200 || resp.StatusCode > 299 {
    81  				err = fmt.Errorf("response has status code %d", resp.StatusCode)
    82  				continue
    83  			}
    84  			if err := json.NewDecoder(resp.Body).Decode(&pools); err != nil {
    85  				return err
    86  			}
    87  			break
    88  		}
    89  	}
    90  	if err != nil {
    91  		return err
    92  	}
    93  	ta.Lock()
    94  	defer ta.Unlock()
    95  	ta.pools = pools
    96  	return nil
    97  }
    98  
    99  func (ta *tideAgent) filterHidden(tideQueries []config.TideQuery, pools []tide.Pool) ([]config.TideQuery, []tide.Pool) {
   100  	if len(ta.hiddenRepos) == 0 {
   101  		return tideQueries, pools
   102  	}
   103  
   104  	var filteredTideQueries []config.TideQuery
   105  	for _, qc := range tideQueries {
   106  		includesHidden := false
   107  		// This will exclude the query even if a single
   108  		// repo in the query is included in hiddenRepos.
   109  		for _, repo := range qc.Repos {
   110  			if matches(repo, ta.hiddenRepos) {
   111  				includesHidden = true
   112  				break
   113  			}
   114  		}
   115  		if (includesHidden && ta.hiddenOnly) ||
   116  			(!includesHidden && !ta.hiddenOnly) {
   117  			filteredTideQueries = append(filteredTideQueries, qc)
   118  		} else {
   119  			ta.log.Debugf("Ignoring query: %v", qc.Query())
   120  		}
   121  	}
   122  
   123  	var filteredPools []tide.Pool
   124  	for _, pool := range pools {
   125  		needsHide := matches(pool.Org+"/"+pool.Repo, ta.hiddenRepos)
   126  		if (needsHide && ta.hiddenOnly) ||
   127  			(!needsHide && !ta.hiddenOnly) {
   128  			filteredPools = append(filteredPools, pool)
   129  		} else {
   130  			ta.log.Debugf("Ignoring pool for %s", pool.Org+"/"+pool.Repo)
   131  		}
   132  	}
   133  
   134  	return filteredTideQueries, filteredPools
   135  }
   136  
   137  // matches returns whether the provided repo intersects
   138  // with repos. repo has always the "org/repo" format but
   139  // repos can include both orgs and repos.
   140  func matches(repo string, repos []string) bool {
   141  	org := strings.Split(repo, "/")[0]
   142  	for _, r := range repos {
   143  		if r == repo || r == org {
   144  			return true
   145  		}
   146  	}
   147  	return false
   148  }