github.com/joelanford/operator-sdk@v0.8.2/internal/pkg/scorecard/helpers.go (about)

     1  // Copyright 2019 The Operator-SDK 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  
    15  package scorecard
    16  
    17  import (
    18  	"fmt"
    19  
    20  	scapiv1alpha1 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha1"
    21  
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  )
    24  
    25  // These functions should be in the public test definitions file, but they are not complete/stable,
    26  // so we'll keep these here until they get fully implemented
    27  
    28  // ResultsPassFail combines multiple test results and returns a single test results
    29  // with 1 maximum point and either 0 or 1 earned points
    30  func ResultsPassFail(results []TestResult) (TestResult, error) {
    31  	var name string
    32  	finalResult := TestResult{}
    33  	if len(results) > 0 {
    34  		name = results[0].Test.GetName()
    35  		// all results have the same test
    36  		finalResult.Test = results[0].Test
    37  		finalResult.MaximumPoints = 1
    38  		finalResult.EarnedPoints = 1
    39  	}
    40  	for _, result := range results {
    41  		if result.Test.IsCumulative() {
    42  			return finalResult, fmt.Errorf("cumulative test passed to ResultsPassFail: name (%s)", result.Test.GetName())
    43  		}
    44  		if result.Test.GetName() != name {
    45  			return finalResult, fmt.Errorf("test name mismatch in ResultsPassFail: %s != %s", result.Test.GetName(), name)
    46  		}
    47  		if result.EarnedPoints != result.MaximumPoints {
    48  			finalResult.EarnedPoints = 0
    49  		}
    50  		finalResult.Suggestions = append(finalResult.Suggestions, result.Suggestions...)
    51  		finalResult.Errors = append(finalResult.Errors, result.Errors...)
    52  	}
    53  	return finalResult, nil
    54  }
    55  
    56  // ResultsCumulative takes multiple TestResults and returns a single TestResult with MaximumPoints
    57  // equal to the sum of the MaximumPoints of the input and EarnedPoints as the sum of EarnedPoints
    58  // of the input
    59  func ResultsCumulative(results []TestResult) (TestResult, error) {
    60  	var name string
    61  	finalResult := TestResult{}
    62  	if len(results) > 0 {
    63  		name = results[0].Test.GetName()
    64  		// all results have the same test
    65  		finalResult.Test = results[0].Test
    66  	}
    67  	for _, result := range results {
    68  		if !result.Test.IsCumulative() {
    69  			return finalResult, fmt.Errorf("non-cumulative test passed to ResultsCumulative: name (%s)", result.Test.GetName())
    70  		}
    71  		if result.Test.GetName() != name {
    72  			return finalResult, fmt.Errorf("test name mismatch in ResultsCumulative: %s != %s", result.Test.GetName(), name)
    73  		}
    74  		finalResult.EarnedPoints += result.EarnedPoints
    75  		finalResult.MaximumPoints += result.MaximumPoints
    76  		finalResult.Suggestions = append(finalResult.Suggestions, result.Suggestions...)
    77  		finalResult.Errors = append(finalResult.Errors, result.Errors...)
    78  	}
    79  	return finalResult, nil
    80  }
    81  
    82  // CalculateResult returns a ScorecardSuiteResult with the state and Tests fields set based on a slice of ScorecardTestResults
    83  func CalculateResult(tests []scapiv1alpha1.ScorecardTestResult) scapiv1alpha1.ScorecardSuiteResult {
    84  	scorecardSuiteResult := scapiv1alpha1.ScorecardSuiteResult{}
    85  	scorecardSuiteResult.Tests = tests
    86  	scorecardSuiteResult = UpdateSuiteStates(scorecardSuiteResult)
    87  	return scorecardSuiteResult
    88  }
    89  
    90  // TestSuitesToScorecardOutput takes an array of test suites and generates a v1alpha1 ScorecardOutput object with the
    91  // provided name, description, and log
    92  func TestSuitesToScorecardOutput(suites []TestSuite, log string) scapiv1alpha1.ScorecardOutput {
    93  	test := scapiv1alpha1.ScorecardOutput{
    94  		TypeMeta: metav1.TypeMeta{
    95  			Kind:       "ScorecardOutput",
    96  			APIVersion: "osdk.openshift.io/v1alpha1",
    97  		},
    98  		Log: log,
    99  	}
   100  	scorecardSuiteResults := []scapiv1alpha1.ScorecardSuiteResult{}
   101  	for _, suite := range suites {
   102  		results := []scapiv1alpha1.ScorecardTestResult{}
   103  		for _, testResult := range suite.TestResults {
   104  			results = append(results, TestResultToScorecardTestResult(testResult))
   105  		}
   106  		scorecardSuiteResult := CalculateResult(results)
   107  		scorecardSuiteResult.TotalScore = suite.TotalScore()
   108  		scorecardSuiteResult.Name = suite.GetName()
   109  		scorecardSuiteResult.Description = suite.GetDescription()
   110  		scorecardSuiteResult.Log = suite.Log
   111  		scorecardSuiteResults = append(scorecardSuiteResults, scorecardSuiteResult)
   112  	}
   113  	test.Results = scorecardSuiteResults
   114  	return test
   115  }
   116  
   117  // TestResultToScorecardTestResult is a helper function for converting from the TestResult type to the ScorecardTestResult type
   118  func TestResultToScorecardTestResult(tr TestResult) scapiv1alpha1.ScorecardTestResult {
   119  	sctr := scapiv1alpha1.ScorecardTestResult{}
   120  	sctr.State = tr.State
   121  	sctr.Name = tr.Test.GetName()
   122  	sctr.Description = tr.Test.GetDescription()
   123  	sctr.EarnedPoints = tr.EarnedPoints
   124  	sctr.MaximumPoints = tr.MaximumPoints
   125  	sctr.Suggestions = tr.Suggestions
   126  	if sctr.Suggestions == nil {
   127  		sctr.Suggestions = []string{}
   128  	}
   129  	stringErrors := []string{}
   130  	for _, err := range tr.Errors {
   131  		stringErrors = append(stringErrors, err.Error())
   132  	}
   133  	sctr.Errors = stringErrors
   134  	return sctr
   135  }
   136  
   137  // UpdateState updates the state of a TestResult.
   138  func UpdateState(res scapiv1alpha1.ScorecardTestResult) scapiv1alpha1.ScorecardTestResult {
   139  	if res.State == scapiv1alpha1.ErrorState {
   140  		return res
   141  	}
   142  	if res.EarnedPoints == 0 {
   143  		res.State = scapiv1alpha1.FailState
   144  	} else if res.EarnedPoints < res.MaximumPoints {
   145  		res.State = scapiv1alpha1.PartialPassState
   146  	} else if res.EarnedPoints == res.MaximumPoints {
   147  		res.State = scapiv1alpha1.PassState
   148  	}
   149  	return res
   150  	// TODO: decide what to do if a Test incorrectly sets points (Earned > Max)
   151  }
   152  
   153  // UpdateSuiteStates update the state of each test in a suite and updates the count to the suite's states to match
   154  func UpdateSuiteStates(suite scapiv1alpha1.ScorecardSuiteResult) scapiv1alpha1.ScorecardSuiteResult {
   155  	suite.TotalTests = len(suite.Tests)
   156  	// reset all state values
   157  	suite.Error = 0
   158  	suite.Fail = 0
   159  	suite.PartialPass = 0
   160  	suite.Pass = 0
   161  	for idx, test := range suite.Tests {
   162  		suite.Tests[idx] = UpdateState(test)
   163  		switch test.State {
   164  		case scapiv1alpha1.ErrorState:
   165  			suite.Error++
   166  		case scapiv1alpha1.PassState:
   167  			suite.Pass++
   168  		case scapiv1alpha1.PartialPassState:
   169  			suite.PartialPass++
   170  		case scapiv1alpha1.FailState:
   171  			suite.Fail++
   172  		}
   173  	}
   174  	return suite
   175  }
   176  
   177  func CombineScorecardOutput(outputs []scapiv1alpha1.ScorecardOutput, log string) scapiv1alpha1.ScorecardOutput {
   178  	output := scapiv1alpha1.ScorecardOutput{
   179  		TypeMeta: metav1.TypeMeta{
   180  			Kind:       "ScorecardOutput",
   181  			APIVersion: "osdk.openshift.io/v1alpha1",
   182  		},
   183  		Log: log,
   184  	}
   185  	for _, item := range outputs {
   186  		output.Results = append(output.Results, item.Results...)
   187  	}
   188  	return output
   189  }