Skip to content

Commit

Permalink
add sonar metrics in scanning job
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Zhao <zhaoyu@koderover.com>
  • Loading branch information
PetrusZ committed Jul 4, 2024
1 parent 49e5098 commit 4d7e9bb
Show file tree
Hide file tree
Showing 21 changed files with 495 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
Copyright 2022 The KodeRover 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.
*/

package scanning

import (
"context"
"fmt"
"os"
"path/filepath"
"time"

"gopkg.in/yaml.v2"

"github.com/koderover/zadig/v2/pkg/cli/zadig-agent/helper/log"
"github.com/koderover/zadig/v2/pkg/cli/zadig-agent/internal/common/types"
"github.com/koderover/zadig/v2/pkg/setting"
"github.com/koderover/zadig/v2/pkg/tool/sonar"
"github.com/koderover/zadig/v2/pkg/types/step"
"github.com/koderover/zadig/v2/pkg/util"
)

type SonarGetMetrics struct {
spec *step.StepSonarGetMetricsSpec
envs []string
secretEnvs []string
workspace string
dirs *types.AgentWorkDirs
Logger *log.JobLogger
}

func NewSonarGetMetricsStep(spec interface{}, dirs *types.AgentWorkDirs, envs, secretEnvs []string, logger *log.JobLogger) (*SonarGetMetrics, error) {
sonarCheckStep := &SonarGetMetrics{dirs: dirs, workspace: dirs.Workspace, envs: envs, secretEnvs: secretEnvs}
yamlBytes, err := yaml.Marshal(spec)
if err != nil {
return sonarCheckStep, fmt.Errorf("marshal spec %+v failed", spec)
}
if err := yaml.Unmarshal(yamlBytes, &sonarCheckStep.spec); err != nil {
return sonarCheckStep, fmt.Errorf("unmarshal spec %s to shell spec failed", yamlBytes)
}
return sonarCheckStep, nil
}

func (s *SonarGetMetrics) Run(ctx context.Context) error {
s.Logger.Infof("Start get Sonar scanning metrics.")
sonarWorkDir := sonar.GetSonarWorkDir(s.spec.Parameter)
if sonarWorkDir == "" {
sonarWorkDir = ".scannerwork"
}
if !filepath.IsAbs(sonarWorkDir) {
sonarWorkDir = filepath.Join(s.workspace, s.spec.CheckDir, sonarWorkDir)
}
taskReportDir := filepath.Join(sonarWorkDir, "report-task.txt")
bytes, err := os.ReadFile(taskReportDir)
if err != nil {
s.Logger.Errorf("read sonar task report file: %s error :%v", time.Now().Format(setting.WorkflowTimeFormat), taskReportDir, err)
return nil
}
taskReportContent := string(bytes)
ceTaskID := sonar.GetSonarCETaskID(taskReportContent)
if ceTaskID == "" {
s.Logger.Errorf("can not get sonar ce task ID")
return nil
}

outputFileName := filepath.Join(s.dirs.JobOutputsDir, setting.WorkflowScanningJobOutputKey)
err = util.AppendToFile(outputFileName, ceTaskID)
if err != nil {
err = fmt.Errorf("append sonar ce task ID %s to output file %s error: %v", ceTaskID, outputFileName, err)
s.Logger.Errorf(err.Error())
return nil
}

return nil
}
5 changes: 5 additions & 0 deletions pkg/cli/zadig-agent/internal/agent/step/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func RunStep(ctx context.Context, jobCtx *jobctl.JobContext, step *commonmodels.
if err != nil {
return err
}
case "sonar_get_metrics":
stepInstance, err = scanning.NewSonarGetMetricsStep(step.Spec, dirs, envs, secretEnvs, logger)
if err != nil {
return err
}
case "tools":
return nil
case "debug_before":
Expand Down
1 change: 1 addition & 0 deletions pkg/microservice/aslan/config/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ const (
StepHtmlReport StepType = "html_report"
StepTarArchive StepType = "tar_archive"
StepSonarCheck StepType = "sonar_check"
StepSonarGetMetrics StepType = "sonar_get_metrics"
StepDistributeImage StepType = "distribute_image"
StepDebugBefore StepType = "debug_before"
StepDebugAfter StepType = "debug_after"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ func (e *Events) Error(message string) {
type StepTask struct {
Name string `bson:"name" json:"name" yaml:"name"`
JobName string `bson:"job_name" json:"job_name" yaml:"job_name"`
JobKey string `bson:"job_key" json:"job_key" yaml:"job_key"`
Error string `bson:"error" json:"error" yaml:"error"`
StepType config.StepType `bson:"type" json:"type" yaml:"type"`
Onfailure bool `bson:"on_failure" json:"on_failure" yaml:"on_failure"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ func instantiateStepCtl(step *commonmodels.StepTask, workflowCtx *commonmodels.W
case config.StepTarArchive:
stepCtl, err = NewTarArchiveCtl(step, logger)
case config.StepSonarCheck:
stepCtl, err = NewSonarCheckCtl(step, logger)
stepCtl, err = NewSonarCheckCtl(step, workflowCtx, logger)
case config.StepSonarGetMetrics:
stepCtl, err = NewSonarGetMetricsCtl(step, workflowCtx, logger)
case config.StepDistributeImage:
stepCtl, err = NewDistributeCtl(step, workflowCtx, jobName, logger)
case config.StepDebugBefore, config.StepDebugAfter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ type sonarCheckCtl struct {
step *commonmodels.StepTask
sonarCheckSpec *step.StepSonarCheckSpec
log *zap.SugaredLogger
workflowCtx *commonmodels.WorkflowTaskCtx
}

func NewSonarCheckCtl(stepTask *commonmodels.StepTask, log *zap.SugaredLogger) (*sonarCheckCtl, error) {
func NewSonarCheckCtl(stepTask *commonmodels.StepTask, workflowCtx *commonmodels.WorkflowTaskCtx, log *zap.SugaredLogger) (*sonarCheckCtl, error) {
yamlString, err := yaml.Marshal(stepTask.Spec)
if err != nil {
return nil, fmt.Errorf("marshal sonar check spec error: %v", err)
Expand All @@ -43,7 +44,7 @@ func NewSonarCheckCtl(stepTask *commonmodels.StepTask, log *zap.SugaredLogger) (
return nil, fmt.Errorf("unmarshal sonar check error: %v", err)
}
stepTask.Spec = sonarCheckSpec
return &sonarCheckCtl{sonarCheckSpec: sonarCheckSpec, log: log, step: stepTask}, nil
return &sonarCheckCtl{sonarCheckSpec: sonarCheckSpec, log: log, step: stepTask, workflowCtx: workflowCtx}, nil
}

func (s *sonarCheckCtl) PreRun(ctx context.Context) error {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2022 The KodeRover 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.
*/

package stepcontroller

import (
"context"
"fmt"
"time"

"go.uber.org/zap"
"gopkg.in/yaml.v2"

commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models"
"github.com/koderover/zadig/v2/pkg/setting"
"github.com/koderover/zadig/v2/pkg/tool/log"
"github.com/koderover/zadig/v2/pkg/tool/sonar"
"github.com/koderover/zadig/v2/pkg/types/job"
"github.com/koderover/zadig/v2/pkg/types/step"
)

type sonarGetMetricsCtl struct {
step *commonmodels.StepTask
sonarGetMetricsSpec *step.StepSonarGetMetricsSpec
log *zap.SugaredLogger
workflowCtx *commonmodels.WorkflowTaskCtx
}

func NewSonarGetMetricsCtl(stepTask *commonmodels.StepTask, workflowCtx *commonmodels.WorkflowTaskCtx, log *zap.SugaredLogger) (*sonarGetMetricsCtl, error) {
yamlString, err := yaml.Marshal(stepTask.Spec)
if err != nil {
return nil, fmt.Errorf("marshal sonar check spec error: %v", err)
}
sonarGetMetricsSpec := &step.StepSonarGetMetricsSpec{}
if err := yaml.Unmarshal(yamlString, &sonarGetMetricsSpec); err != nil {
return nil, fmt.Errorf("unmarshal sonar check error: %v", err)
}
stepTask.Spec = sonarGetMetricsSpec
return &sonarGetMetricsCtl{sonarGetMetricsSpec: sonarGetMetricsSpec, log: log, step: stepTask, workflowCtx: workflowCtx}, nil
}

func (s *sonarGetMetricsCtl) PreRun(ctx context.Context) error {
return nil
}

func (s *sonarGetMetricsCtl) AfterRun(ctx context.Context) error {
key := job.GetJobOutputKey(s.step.JobKey, setting.WorkflowScanningJobOutputKey)
id, ok := s.workflowCtx.GlobalContextGet(key)
if !ok {
err := fmt.Errorf("sonar check job output %s not found", key)
log.Error(err)
return err
}

client := sonar.NewSonarClient(s.sonarGetMetricsSpec.SonarServer, s.sonarGetMetricsSpec.SonarToken)
resp, err := client.GetComponentMeasures(s.sonarGetMetricsSpec.ProjectKey)
if err != nil {
err = fmt.Errorf("get component measures error: %v", err)
log.Error(err)
return err
}
s.sonarGetMetricsSpec.SonarMetrics = &step.SonarMetrics{}
for _, mesures := range resp.Component.Measures {
switch mesures.Metric {
case "coverage":
s.sonarGetMetricsSpec.SonarMetrics.Coverage = mesures.Value
case "bugs":
s.sonarGetMetricsSpec.SonarMetrics.Bugs = mesures.Value
case "vulnerabilities":
s.sonarGetMetricsSpec.SonarMetrics.Vulnerabilities = mesures.Value
case "code_smells":
s.sonarGetMetricsSpec.SonarMetrics.CodeSmells = mesures.Value
case "ncloc":
s.sonarGetMetricsSpec.SonarMetrics.Ncloc = mesures.Value
default:
log.Warnf("unknown metric: %s: %s", mesures.Metric, mesures.Value)
}
}

if s.sonarGetMetricsSpec.CheckQualityGate {
client := sonar.NewSonarClient(s.sonarGetMetricsSpec.SonarServer, s.sonarGetMetricsSpec.SonarToken)
analysisID, err := client.WaitForCETaskTobeDone(id, time.Minute*10)
if err != nil {
log.Error(err)
return err
}
gateInfo, err := client.GetQualityGateInfo(analysisID)
if err != nil {
log.Error(err)
return err
}
s.sonarGetMetricsSpec.SonarMetrics.QualityGateStatus = gateInfo.ProjectStatus.Status
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ func (j *ScanningJob) GetOutPuts(log *zap.SugaredLogger) []string {
}
}
}
resp = append(resp, getOutputKey(jobKey, scanningInfo.Outputs)...)
resp = append(resp, getOutputKey(jobKey, ensureScanningOutputs(scanningInfo.Outputs))...)
}
return resp
}
Expand Down Expand Up @@ -390,7 +390,7 @@ func (j *ScanningJob) toJobTask(scanning *commonmodels.ScanningModule, taskID in
JobType: string(config.JobZadigScanning),
Spec: jobTaskSpec,
Timeout: timeout,
Outputs: scanningInfo.Outputs,
Outputs: ensureScanningOutputs(scanningInfo.Outputs),
Infrastructure: scanningInfo.Infrastructure,
VMLabels: scanningInfo.VMLabels,
}
Expand Down Expand Up @@ -520,7 +520,7 @@ func (j *ScanningJob) toJobTask(scanning *commonmodels.ScanningModule, taskID in
StepType: config.StepDebugBefore,
}
jobTaskSpec.Steps = append(jobTaskSpec.Steps, debugBeforeStep)
// init shell step
// init script step
if scanningInfo.ScannerType == types.ScanningTypeSonar {
scriptStep := &commonmodels.StepTask{
JobName: jobTask.Name,
Expand Down Expand Up @@ -555,8 +555,8 @@ func (j *ScanningJob) toJobTask(scanning *commonmodels.ScanningModule, taskID in

}

projectKey := sonar.GetSonarProjectKeyFromConfig(scanningInfo.Parameter)
resultAddr, err := sonar.GetSonarAddressWithProjectKey(sonarInfo.ServerAddress, renderEnv(projectKey, jobTaskSpec.Properties.Envs))
projectKey := renderEnv(sonar.GetSonarProjectKeyFromConfig(scanningInfo.Parameter), jobTaskSpec.Properties.Envs)
resultAddr, err := sonar.GetSonarAddressWithProjectKey(sonarInfo.ServerAddress, projectKey)
if err != nil {
log.Errorf("failed to get sonar address with project key, error: %s", err)
}
Expand Down Expand Up @@ -633,14 +633,33 @@ func (j *ScanningJob) toJobTask(scanning *commonmodels.ScanningModule, taskID in
}

jobTaskSpec.Steps = append(jobTaskSpec.Steps, sonarScriptStep)

}

sonarGetMetricsStep := &commonmodels.StepTask{
Name: scanning.Name + "-sonar-get-metrics",
JobName: jobTask.Name,
JobKey: jobTask.Key,
StepType: config.StepSonarGetMetrics,
Spec: &step.StepSonarGetMetricsSpec{
ProjectKey: projectKey,
Parameter: scanningInfo.Parameter,
CheckDir: repoName,
SonarToken: sonarInfo.Token,
SonarServer: sonarInfo.ServerAddress,
CheckQualityGate: scanningInfo.CheckQualityGate,
},
}
jobTaskSpec.Steps = append(jobTaskSpec.Steps, sonarGetMetricsStep)

if scanningInfo.CheckQualityGate {
sonarChekStep := &commonmodels.StepTask{
Name: scanning.Name + "-sonar-check",
JobName: jobTask.Name,
JobKey: jobTask.Key,
StepType: config.StepSonarCheck,
Spec: &step.StepSonarCheckSpec{
ProjectKey: projectKey,
Parameter: scanningInfo.Parameter,
CheckDir: repoName,
SonarToken: sonarInfo.Token,
Expand Down Expand Up @@ -818,3 +837,16 @@ func fillScanningDetail(moduleScanning *commonmodels.Scanning) error {
func getScanningJobCacheObjectPath(workflowName, scanningName string) string {
return fmt.Sprintf("%s/cache/%s", workflowName, scanningName)
}

func ensureScanningOutputs(outputs []*commonmodels.Output) []*commonmodels.Output {
keyMap := map[string]struct{}{}
for _, output := range outputs {
keyMap[output.Name] = struct{}{}
}
if _, ok := keyMap[setting.WorkflowScanningJobOutputKey]; !ok {
outputs = append(outputs, &commonmodels.Output{
Name: setting.WorkflowScanningJobOutputKey,
})
}
return outputs
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ type ZadigScanningJobSpec struct {
ServiceName string `bson:"service_name" json:"service_name"`
ServiceModule string `bson:"service_module" json:"service_module"`
Repos []*types.Repository `bson:"repos" json:"repos"`
SonarMetrics *step.SonarMetrics `bson:"sonar_metrics" json:"sonar_metrics"`
LinkURL string `bson:"link_url" json:"link_url"`
ScanningName string `bson:"scanning_name" json:"scanning_name"`
}
Expand Down Expand Up @@ -1193,6 +1194,12 @@ func jobsToJobPreviews(jobs []*commonmodels.JobTask, context map[string]string,
spec.Repos = stepSpec.Repos
continue
}
if step.StepType == config.StepSonarGetMetrics {
stepSpec := &stepspec.StepSonarGetMetricsSpec{}
commonmodels.IToi(step.Spec, &stepSpec)
spec.SonarMetrics = stepSpec.SonarMetrics
continue
}
}
for _, arg := range taskJobSpec.Properties.Envs {
if arg.Key == "SONAR_LINK" {
Expand Down
Loading

0 comments on commit 4d7e9bb

Please sign in to comment.