github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/spyglass/spyglass.go (about)

     1  /*
     2  Copyright 2018 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 spyglass creates views for Prow job artifacts.
    18  package spyglass
    19  
    20  import (
    21  	"fmt"
    22  	"path"
    23  	"sort"
    24  	"strings"
    25  
    26  	"cloud.google.com/go/storage"
    27  	"github.com/sirupsen/logrus"
    28  
    29  	"k8s.io/test-infra/prow/config"
    30  	"k8s.io/test-infra/prow/deck/jobs"
    31  	"k8s.io/test-infra/prow/kube"
    32  	"k8s.io/test-infra/prow/spyglass/lenses"
    33  )
    34  
    35  // Key types specify the way Spyglass will fetch artifact handles
    36  const (
    37  	gcsKeyType  = "gcs"
    38  	prowKeyType = "prowjob"
    39  )
    40  
    41  // Spyglass records which sets of artifacts need views for a Prow job. The metaphor
    42  // can be understood as follows: A spyglass receives light from a source through
    43  // an eyepiece, which has a lens that ultimately presents a view of the light source
    44  // to the observer. Spyglass receives light (artifacts) via a
    45  // source (src) through the eyepiece (Eyepiece) and presents the view (what you see
    46  // in your browser) via a lens (Lens).
    47  type Spyglass struct {
    48  	// JobAgent contains information about the current jobs in deck
    49  	JobAgent *jobs.JobAgent
    50  
    51  	// ConfigAgent contains information about the prow configuration
    52  	ConfigAgent configAgent
    53  
    54  	*GCSArtifactFetcher
    55  	*PodLogArtifactFetcher
    56  }
    57  
    58  // This interface matches config.Agent and exists for the purpose of unit tests.
    59  type configAgent interface {
    60  	Config() *config.Config
    61  }
    62  
    63  // LensRequest holds data sent by a view
    64  type LensRequest struct {
    65  	Source    string   `json:"src"`
    66  	Artifacts []string `json:"artifacts"`
    67  }
    68  
    69  // New constructs a Spyglass object from a JobAgent, a config.Agent, and a storage Client.
    70  func New(ja *jobs.JobAgent, conf configAgent, c *storage.Client) *Spyglass {
    71  	return &Spyglass{
    72  		JobAgent:              ja,
    73  		ConfigAgent:           conf,
    74  		PodLogArtifactFetcher: NewPodLogArtifactFetcher(ja),
    75  		GCSArtifactFetcher:    NewGCSArtifactFetcher(c),
    76  	}
    77  }
    78  
    79  // Lenses gets all views of all artifact files matching each regexp with a registered lens
    80  func (s *Spyglass) Lenses(matchCache map[string][]string) []lenses.Lens {
    81  	ls := []lenses.Lens{}
    82  	for lensName, matches := range matchCache {
    83  		if len(matches) == 0 {
    84  			continue
    85  		}
    86  		lens, err := lenses.GetLens(lensName)
    87  		if err != nil {
    88  			logrus.WithField("lensName", lens).WithError(err).Error("Could not find artifact lens")
    89  		} else {
    90  			ls = append(ls, lens)
    91  		}
    92  	}
    93  	// Make sure lenses are rendered in order by ascending priority
    94  	sort.Slice(ls, func(i, j int) bool {
    95  		iname := ls[i].Name()
    96  		jname := ls[j].Name()
    97  		pi := ls[i].Priority()
    98  		pj := ls[j].Priority()
    99  		if pi == pj {
   100  			return iname < jname
   101  		}
   102  		return pi < pj
   103  	})
   104  	return ls
   105  }
   106  
   107  // JobPath returns a link to the GCS directory for the job specified in src
   108  func (s *Spyglass) JobPath(src string) (string, error) {
   109  	src = strings.TrimSuffix(src, "/")
   110  	keyType, key, err := splitSrc(src)
   111  	if err != nil {
   112  		return "", fmt.Errorf("error parsing src: %v", src)
   113  	}
   114  	split := strings.Split(key, "/")
   115  	switch keyType {
   116  	case gcsKeyType:
   117  		if len(split) < 4 {
   118  			return "", fmt.Errorf("invalid key %s: expected <bucket-name>/<log-type>/.../<job-name>/<build-id>", key)
   119  		}
   120  		// see https://github.com/kubernetes/test-infra/tree/master/gubernator
   121  		bktName := split[0]
   122  		logType := split[1]
   123  		jobName := split[len(split)-2]
   124  		if logType == "logs" {
   125  			return path.Dir(key), nil
   126  		} else if logType == "pr-logs" {
   127  			return path.Join(bktName, "pr-logs/directory", jobName), nil
   128  		}
   129  		return "", fmt.Errorf("unrecognized GCS key: %s", key)
   130  	case prowKeyType:
   131  		if len(split) < 2 {
   132  			return "", fmt.Errorf("invalid key %s: expected <job-name>/<build-id>", key)
   133  		}
   134  		jobName := split[0]
   135  		buildID := split[1]
   136  		job, err := s.jobAgent.GetProwJob(jobName, buildID)
   137  		if err != nil {
   138  			return "", fmt.Errorf("failed to get prow job from src %q: %v", key, err)
   139  		}
   140  		if job.Spec.DecorationConfig == nil {
   141  			return "", fmt.Errorf("failed to locate GCS upload bucket for %s: job is undecorated", jobName)
   142  		}
   143  		if job.Spec.DecorationConfig.GCSConfiguration == nil {
   144  			return "", fmt.Errorf("failed to locate GCS upload bucket for %s: missing GCS configuration", jobName)
   145  		}
   146  		bktName := job.Spec.DecorationConfig.GCSConfiguration.Bucket
   147  		if job.Spec.Type == kube.PresubmitJob {
   148  			return path.Join(bktName, "pr-logs/directory", jobName), nil
   149  		}
   150  		return path.Join(bktName, "logs", jobName), nil
   151  	default:
   152  		return "", fmt.Errorf("unrecognized key type for src: %v", src)
   153  	}
   154  }