Skip to content

Commit

Permalink
Separate gcs and testgrid into its own packages (knative#196)
Browse files Browse the repository at this point in the history
* Separate gcs and testgrid into its own packages

* Return string instead of fields array in checkLog

* Address review comments
  • Loading branch information
srinivashegde86 authored and knative-prow-robot committed Oct 18, 2018
1 parent 01c6f8a commit 16bceb4
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 143 deletions.
63 changes: 55 additions & 8 deletions tools/apicoverage/apicoverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,20 @@ import (
"log"
"os"
"reflect"
"strconv"
"strings"

"github.com/knative/serving/pkg/apis/serving/v1alpha1"
"github.com/knative/test-infra/tools/gcs"
"github.com/knative/test-infra/tools/testgrid"
)

const (
logDir = "logs/ci-knative-serving-continuous/"
buildFile = "build-log.txt"
apiCoverage = "api_coverage"
overallRoute = "OverallRoute"
overallConfig = "OverallConfiguration"
overallService = "OverallService"
)

// ResourceObjects defines the resource objects in knative-serving
Expand Down Expand Up @@ -168,6 +178,45 @@ func initCoverage() *OverallAPICoverage {
return &coverage
}

func getRelevantLogs(fields []string) *string {
// I0727 16:23:30.055] 2018-10-12T18:18:06.835-0700 info TestRouteCreation test/configuration.go:34 resource {<resource_name>: <val>}"}
if len(fields) == 8 && fields[3] == "info" && fields[6] == "resource" {
s := strings.Join(fields[7:], " ")
return &s
}
return nil
}

func createCases(tcName string, covered map[string]int, notCovered map[string]int) []testgrid.TestCase {
var tc []testgrid.TestCase

var percentCovered = float32(100 * len(covered) / (len(covered) + len(notCovered)))
tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: percentCovered}}
tc = append(tc, testgrid.TestCase{Name: tcName, Properties: testgrid.TestProperties{Property: tp}, Fail: false})

for key, value := range covered {
tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: float32(value)}}
tc = append(tc, testgrid.TestCase{Name: tcName + "/" + key, Properties: testgrid.TestProperties{Property: tp}, Fail: false})
}

for key, value := range notCovered {
tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: float32(value)}}
tc = append(tc, testgrid.TestCase{Name: tcName + "/" + key, Properties: testgrid.TestProperties{Property: tp}, Fail: true})
}
return tc
}

func createTestgridXML(coverage *OverallAPICoverage, artifactsDir string) {
tc := createCases(overallRoute, coverage.RouteAPICovered, coverage.RouteAPINotCovered)
tc = append(tc, createCases(overallConfig, coverage.ConfigurationAPICovered, coverage.ConfigurationAPINotCovered)...)
tc = append(tc, createCases(overallService, coverage.ServiceAPICovered, coverage.ServiceAPINotCovered)...)
ts := testgrid.TestSuite{TestCases: tc}

if err := testgrid.CreateXMLOutput(ts, artifactsDir); err != nil {
log.Fatalf("Cannot create the xml output file: %v", err)
}
}

func main() {

artifactsDir := flag.String("artifacts-dir", "./artifacts", "Directory to store the generated XML file")
Expand All @@ -176,18 +225,16 @@ func main() {

// Read the latest-build.txt file to get the latest build number
ctx := context.Background()
contents, err := readGcsFile(ctx, logDir+sourceDir+"/latest-build.txt", *serviceAccount)
if err != nil {
log.Fatalf("Cannot get latest build number. %s: %v", contents, err)
}
latestBuild, err := strconv.Atoi(string(contents))
num, err := gcs.GetLatestBuildNumber(ctx, logDir, *serviceAccount)
if err != nil {
log.Fatalf("Cannot convert %s to string to get latest build %v", string(contents), err)
log.Fatalf("Cannot get latest build number: %v", err)
}

// Calculate coverage
coverage := initCoverage()
calculateCoverage(parseLog(ctx, fmt.Sprintf("%s/%d", sourceDir, latestBuild), false, coverage), coverage)
calculateCoverage(
gcs.ParseLog(ctx, fmt.Sprintf("%s/%d/%s", logDir, num, buildFile), getRelevantLogs),
coverage)

// Write the testgrid xml to artifacts
createTestgridXML(coverage, *artifactsDir)
Expand Down
115 changes: 0 additions & 115 deletions tools/apicoverage/testgrid.go

This file was deleted.

51 changes: 31 additions & 20 deletions tools/apicoverage/gcs.go → tools/gcs/gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,26 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// gcs.go defines functions on gcs
// gcs.go defines functions to use GCS

package main
package gcs

import (
"bufio"
"context"
"fmt"
"io/ioutil"
"log"
"strconv"
"strings"

"cloud.google.com/go/storage"
"google.golang.org/api/option"
)

const (
logDir = "logs/"
sourceDir = "ci-knative-serving-continuous"
bucketName = "knative-prow"
latest = "latest-build.txt"
)

var client *storage.Client
Expand All @@ -48,7 +48,22 @@ func createStorageObject(filename string) *storage.ObjectHandle {
return client.Bucket(bucketName).Object(filename)
}

func readGcsFile(ctx context.Context, filename string, sa string) ([]byte, error) {
// GetLatestBuildNumber gets the latest build number for the specified log directory
func GetLatestBuildNumber(ctx context.Context, logDir string, sa string) (int, error) {
contents, err := ReadGcsFile(ctx, logDir+latest, sa)
if err != nil {
return 0, err
}
latestBuild, err := strconv.Atoi(string(contents))
if err != nil {
return 0, err
}

return latestBuild, nil
}

//ReadGcsFile reads the specified file using the provided service account
func ReadGcsFile(ctx context.Context, filename string, sa string) ([]byte, error) {
// Create a new GCS client
if err := createStorageClient(ctx, sa); err != nil {
log.Fatalf("Failed to create GCS client: %v", err)
Expand All @@ -69,33 +84,29 @@ func readGcsFile(ctx context.Context, filename string, sa string) ([]byte, error
return contents, nil
}

func parseLog(ctx context.Context, dir string, isLocal bool, coverage *OverallAPICoverage) []string {
var covLogs []string
// ParseLog parses the log and returns the lines where the checkLog func does not return an empty slice.
// checkLog function should take in the log statement and return a part from that statement that should be in the log output.
func ParseLog(ctx context.Context, filename string, checkLog func(s []string) *string) []string {
var logs []string

buildDir := logDir + dir
log.Printf("Parsing '%s'", buildDir)
logFile := buildDir + "/build-log.txt"
log.Printf("Parsing '%s'", logFile)
o := createStorageObject(logFile)
log.Printf("Parsing '%s'", filename)
o := createStorageObject(filename)
if _, err := o.Attrs(ctx); err != nil {
log.Printf("Cannot get attributes of '%s', assuming not ready yet: %v", logFile, err)
log.Printf("Cannot get attributes of '%s', assuming not ready yet: %v", filename, err)
return nil
}
f, err := o.NewReader(ctx)
if err != nil {
log.Fatalf("Error opening '%s': %v", logFile, err)
log.Fatalf("Error opening '%s': %v", filename, err)
}
defer f.Close()

scanner := bufio.NewScanner(f)

for scanner.Scan() {
fields := strings.Fields(scanner.Text())

// I0727 16:23:30.055] info TestRouteCreation test/configuration.go:34 resource {<resource_name>: <val>}"}
if len(fields) == 7 && fields[2] == "info" && fields[5] == "resource" {
covLogs = append(covLogs, strings.Join(fields[6:], " "))
if s := checkLog(strings.Fields(scanner.Text())); s != nil {
logs = append(logs, *s)
}
}
return covLogs
return logs
}
69 changes: 69 additions & 0 deletions tools/testgrid/testgrid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright 2018 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// testgrid.go provides methods to perform action on testgrid.

package testgrid

import (
"encoding/xml"
"os"
)

// TestProperty defines a property of the test
type TestProperty struct {
Name string `xml:"name,attr"`
Value float32 `xml:"value,attr"`
}

// TestProperties is an array of test properties
type TestProperties struct {
Property []TestProperty `xml:"property"`
}

// TestCase defines a test case that was executed
type TestCase struct {
ClassName string `xml:"class_name,attr"`
Name string `xml:"name,attr"`
Time int `xml:"time,attr"`
Properties TestProperties `xml:"properties"`
Fail bool `xml:"failure,omitempty"`
}

// TestSuite defines the set of relevant test cases
type TestSuite struct {
XMLName xml.Name `xml:"testsuite"`
TestCases []TestCase `xml:"testcase"`
}

// CreateXMLOutput creates the junit xml file in the provided artifacts directory
func CreateXMLOutput(ts TestSuite, artifactsDir string) error {
op, err := xml.MarshalIndent(ts, "", " ")
if err != nil {
return err
}

outputFile := artifactsDir + "/junit_bazel.xml"
f, err := os.Create(outputFile)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString(string(op) + "\n"); err != nil {
return err
}
return nil
}

0 comments on commit 16bceb4

Please sign in to comment.