mirror of
https://github.com/goharbor/harbor
synced 2024-09-20 15:45:41 +00:00
Update table scan_report and extract cvss_v3_score from vendor attribute (#18854)
For better performance when query cve information, add summary information to scan_report Extract cve_score from vendor attribute in vulnerability_record SQL migrate script for the update Signed-off-by: stonezdj <daojunz@vmware.com>
This commit is contained in:
parent
7435c8c5ab
commit
d84b1d07d2
|
@ -5,3 +5,72 @@ UPDATE execution SET vendor_id = (extra_attrs -> 'artifact' ->> 'id')::integer
|
||||||
WHERE jsonb_path_exists(extra_attrs::jsonb, '$.artifact.id')
|
WHERE jsonb_path_exists(extra_attrs::jsonb, '$.artifact.id')
|
||||||
AND vendor_id IN (SELECT id FROM scanner_registration)
|
AND vendor_id IN (SELECT id FROM scanner_registration)
|
||||||
AND vendor_type = 'IMAGE_SCAN';
|
AND vendor_type = 'IMAGE_SCAN';
|
||||||
|
|
||||||
|
/* extract score from vendor attribute */
|
||||||
|
UPDATE vulnerability_record
|
||||||
|
SET cvss_score_v3 = (vendor_attributes->'CVSS'->'nvd'->>'V3Score')::double precision
|
||||||
|
WHERE jsonb_path_exists(vendor_attributes::jsonb, '$.CVSS.nvd.V3Score');
|
||||||
|
|
||||||
|
/* add summary information in scan_report */
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS critical_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS high_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS medium_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS low_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS none_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS unknown_cnt BIGINT;
|
||||||
|
ALTER TABLE scan_report ADD COLUMN IF NOT EXISTS fixable_cnt BIGINT;
|
||||||
|
|
||||||
|
/* extract summary information for previous scan_report */
|
||||||
|
DO
|
||||||
|
$$
|
||||||
|
DECLARE
|
||||||
|
report RECORD;
|
||||||
|
v RECORD;
|
||||||
|
critical_count BIGINT;
|
||||||
|
high_count BIGINT;
|
||||||
|
none_count BIGINT;
|
||||||
|
medium_count BIGINT;
|
||||||
|
low_count BIGINT;
|
||||||
|
unknown_count BIGINT;
|
||||||
|
fixable_count BIGINT;
|
||||||
|
BEGIN
|
||||||
|
FOR report IN SELECT uuid FROM scan_report
|
||||||
|
LOOP
|
||||||
|
critical_count := 0;
|
||||||
|
high_count := 0;
|
||||||
|
medium_count := 0;
|
||||||
|
none_count := 0;
|
||||||
|
low_count := 0;
|
||||||
|
unknown_count := 0;
|
||||||
|
FOR v IN SELECT vr.severity, vr.fixed_version
|
||||||
|
FROM report_vulnerability_record rvr,
|
||||||
|
vulnerability_record vr
|
||||||
|
WHERE rvr.report_uuid = report.uuid
|
||||||
|
AND rvr.vuln_record_id = vr.id
|
||||||
|
LOOP
|
||||||
|
IF v.severity = 'Critical' THEN
|
||||||
|
critical_count = critical_count + 1;
|
||||||
|
ELSIF v.severity = 'High' THEN
|
||||||
|
high_count = high_count + 1;
|
||||||
|
ELSIF v.severity = 'Medium' THEN
|
||||||
|
medium_count = medium_count + 1;
|
||||||
|
ELSIF v.severity = 'Low' THEN
|
||||||
|
low_count = low_count + 1;
|
||||||
|
ELSIF v.severity = 'None' THEN
|
||||||
|
none_count = none_count + 1;
|
||||||
|
ELSIF v.severity = 'Unknown' THEN
|
||||||
|
unknown_count = unknown_count + 1;
|
||||||
|
ELSIF v.fixed_version IS NOT NULL THEN
|
||||||
|
fixable_count = fixable_count + 1;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
UPDATE scan_report
|
||||||
|
SET critical_cnt = critical_count,
|
||||||
|
high_cnt = high_count,
|
||||||
|
medium_cnt = medium_count,
|
||||||
|
low_cnt = low_count,
|
||||||
|
unknown_cnt = unknown_count
|
||||||
|
WHERE uuid = report.uuid;
|
||||||
|
END LOOP;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
|
@ -28,7 +28,13 @@ type Report struct {
|
||||||
RegistrationUUID string `orm:"column(registration_uuid)"`
|
RegistrationUUID string `orm:"column(registration_uuid)"`
|
||||||
MimeType string `orm:"column(mime_type)"`
|
MimeType string `orm:"column(mime_type)"`
|
||||||
Report string `orm:"column(report);type(json)"`
|
Report string `orm:"column(report);type(json)"`
|
||||||
|
CriticalCnt int64 `orm:"column(critical_cnt)"`
|
||||||
|
HighCnt int64 `orm:"column(high_cnt)"`
|
||||||
|
MediumCnt int64 `orm:"column(medium_cnt)"`
|
||||||
|
LowCnt int64 `orm:"column(low_cnt)"`
|
||||||
|
UnknownCnt int64 `orm:"column(unknown_cnt)"`
|
||||||
|
NoneCnt int64 `orm:"column(none_cnt)"`
|
||||||
|
FixableCnt int64 `orm:"column(fixable_cnt)"`
|
||||||
Status string `orm:"-"`
|
Status string `orm:"-"`
|
||||||
StartTime time.Time `orm:"-"`
|
StartTime time.Time `orm:"-"`
|
||||||
EndTime time.Time `orm:"-"`
|
EndTime time.Time `orm:"-"`
|
||||||
|
|
|
@ -36,6 +36,8 @@ type DAO interface {
|
||||||
List(ctx context.Context, query *q.Query) ([]*Report, error)
|
List(ctx context.Context, query *q.Query) ([]*Report, error)
|
||||||
// UpdateReportData only updates the `report` column with conditions matched.
|
// UpdateReportData only updates the `report` column with conditions matched.
|
||||||
UpdateReportData(ctx context.Context, uuid string, report string) error
|
UpdateReportData(ctx context.Context, uuid string, report string) error
|
||||||
|
// Update update report
|
||||||
|
Update(ctx context.Context, r *Report, cols ...string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns an instance of the default DAO
|
// New returns an instance of the default DAO
|
||||||
|
@ -97,3 +99,14 @@ func (d *dao) UpdateReportData(ctx context.Context, uuid string, report string)
|
||||||
_, err = qt.Filter("uuid", uuid).Update(data)
|
_, err = qt.Filter("uuid", uuid).Update(data)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *dao) Update(ctx context.Context, r *Report, cols ...string) error {
|
||||||
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := o.Update(r, cols...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/scan/report"
|
||||||
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
"github.com/goharbor/harbor/src/pkg/scan/vuln"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -151,7 +152,7 @@ func (c *nativeToRelationalSchemaConverter) toSchema(ctx context.Context, report
|
||||||
var newRecords []*scan.VulnerabilityRecord
|
var newRecords []*scan.VulnerabilityRecord
|
||||||
for _, v := range vulnReport.Vulnerabilities {
|
for _, v := range vulnReport.Vulnerabilities {
|
||||||
if !s.Exists(v.Key()) {
|
if !s.Exists(v.Key()) {
|
||||||
newRecords = append(newRecords, toVulnerabilityRecord(v, registrationUUID))
|
newRecords = append(newRecords, toVulnerabilityRecord(ctx, v, registrationUUID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +231,7 @@ func (c *nativeToRelationalSchemaConverter) getNativeV1ReportFromResolvedData(ct
|
||||||
return report, nil
|
return report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toVulnerabilityRecord(item *vuln.VulnerabilityItem, registrationUUID string) *scan.VulnerabilityRecord {
|
func toVulnerabilityRecord(ctx context.Context, item *vuln.VulnerabilityItem, registrationUUID string) *scan.VulnerabilityRecord {
|
||||||
record := new(scan.VulnerabilityRecord)
|
record := new(scan.VulnerabilityRecord)
|
||||||
|
|
||||||
record.CVEID = item.ID
|
record.CVEID = item.ID
|
||||||
|
@ -261,6 +262,12 @@ func toVulnerabilityRecord(item *vuln.VulnerabilityItem, registrationUUID string
|
||||||
if err == nil {
|
if err == nil {
|
||||||
record.VendorAttributes = string(vendorAttributes)
|
record.VendorAttributes = string(vendorAttributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse the NVD score from the vendor attributes
|
||||||
|
nvdScore := parseScoreFromVendorAttribute(ctx, string(vendorAttributes))
|
||||||
|
if record.CVE3Score == nil {
|
||||||
|
record.CVE3Score = &nvdScore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return record
|
return record
|
||||||
|
@ -290,3 +297,78 @@ func toVulnerabilityItem(record *scan.VulnerabilityRecord, artifactDigest string
|
||||||
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateReport updates the report summary with the vulnerability counts
|
||||||
|
func (c *nativeToRelationalSchemaConverter) updateReport(ctx context.Context, vulnerabilities []*vuln.VulnerabilityItem, reportUUID string) error {
|
||||||
|
log.G(ctx).WithFields(log.Fields{"reportUUID": reportUUID}).Debugf("Update report summary for report")
|
||||||
|
CriticalCnt := int64(0)
|
||||||
|
HighCnt := int64(0)
|
||||||
|
MediumCnt := int64(0)
|
||||||
|
LowCnt := int64(0)
|
||||||
|
NoneCnt := int64(0)
|
||||||
|
UnknownCnt := int64(0)
|
||||||
|
FixableCnt := int64(0)
|
||||||
|
|
||||||
|
for _, v := range vulnerabilities {
|
||||||
|
v.Severity = vuln.ParseSeverityVersion3(v.Severity.String())
|
||||||
|
switch v.Severity {
|
||||||
|
case vuln.Critical:
|
||||||
|
CriticalCnt++
|
||||||
|
case vuln.High:
|
||||||
|
HighCnt++
|
||||||
|
case vuln.Medium:
|
||||||
|
MediumCnt++
|
||||||
|
case vuln.Low:
|
||||||
|
LowCnt++
|
||||||
|
case vuln.None:
|
||||||
|
NoneCnt++
|
||||||
|
case vuln.Unknown:
|
||||||
|
UnknownCnt++
|
||||||
|
}
|
||||||
|
if len(v.FixVersion) > 0 {
|
||||||
|
FixableCnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reports, err := report.Mgr.List(ctx, q.New(q.KeyWords{"uuid": reportUUID}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(reports) == 0 {
|
||||||
|
return errors.New(nil).WithMessage("report not found, uuid:%v", reportUUID)
|
||||||
|
}
|
||||||
|
r := reports[0]
|
||||||
|
|
||||||
|
r.CriticalCnt = CriticalCnt
|
||||||
|
r.HighCnt = HighCnt
|
||||||
|
r.MediumCnt = MediumCnt
|
||||||
|
r.LowCnt = LowCnt
|
||||||
|
r.NoneCnt = NoneCnt
|
||||||
|
r.FixableCnt = FixableCnt
|
||||||
|
r.UnknownCnt = UnknownCnt
|
||||||
|
|
||||||
|
return report.Mgr.Update(ctx, r, "CriticalCnt", "HighCnt", "MediumCnt", "LowCnt", "NoneCnt", "UnknownCnt", "FixableCnt")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CVSS ...
|
||||||
|
type CVSS struct {
|
||||||
|
NVD Nvd `json:"nvd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nvd ...
|
||||||
|
type Nvd struct {
|
||||||
|
V3Score float64 `json:"V3Score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScoreFromVendorAttribute(ctx context.Context, vendorAttribute string) (NvdV3Score float64) {
|
||||||
|
var data map[string]CVSS
|
||||||
|
err := json.Unmarshal([]byte(vendorAttribute), &data)
|
||||||
|
if err != nil {
|
||||||
|
log.G(ctx).Errorf("failed to parse vendor_attribute, error %v", err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if cvss, ok := data["CVSS"]; ok {
|
||||||
|
return cvss.NVD.V3Score
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package postprocessors
|
package postprocessors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -294,6 +295,7 @@ type TestReportConverterSuite struct {
|
||||||
vulnerabilityRecordDao scan.VulnerabilityRecordDao
|
vulnerabilityRecordDao scan.VulnerabilityRecordDao
|
||||||
reportDao scan.DAO
|
reportDao scan.DAO
|
||||||
registrationID string
|
registrationID string
|
||||||
|
nc *nativeToRelationalSchemaConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupTest prepares env for test cases.
|
// SetupTest prepares env for test cases.
|
||||||
|
@ -318,6 +320,7 @@ func TestReportConverterTests(t *testing.T) {
|
||||||
|
|
||||||
// SetupSuite sets up the report converter suite test cases
|
// SetupSuite sets up the report converter suite test cases
|
||||||
func (suite *TestReportConverterSuite) SetupSuite() {
|
func (suite *TestReportConverterSuite) SetupSuite() {
|
||||||
|
suite.nc = &nativeToRelationalSchemaConverter{dao: scan.NewVulnerabilityRecordDao()}
|
||||||
suite.rc = NewNativeToRelationalSchemaConverter()
|
suite.rc = NewNativeToRelationalSchemaConverter()
|
||||||
suite.Suite.SetupSuite()
|
suite.Suite.SetupSuite()
|
||||||
suite.vulnerabilityRecordDao = scan.NewVulnerabilityRecordDao()
|
suite.vulnerabilityRecordDao = scan.NewVulnerabilityRecordDao()
|
||||||
|
@ -510,3 +513,76 @@ func (suite *TestReportConverterSuite) validateReportSummary(summary string, raw
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
assert.Equal(suite.T(), string(data), summary)
|
assert.Equal(suite.T(), string(data), summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *TestReportConverterSuite) TestUpdateReport() {
|
||||||
|
ctx := suite.Context()
|
||||||
|
vuls := []*vuln.VulnerabilityItem{
|
||||||
|
{
|
||||||
|
Severity: "Critical", FixVersion: "2.9.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "Critical",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "High",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "Medium",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "Low",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "None",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Severity: "Unknown",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rp := &scan.Report{
|
||||||
|
Digest: "d1001",
|
||||||
|
RegistrationUUID: "ruuid",
|
||||||
|
MimeType: v1.MimeTypeGenericVulnerabilityReport,
|
||||||
|
Report: sampleReportWithMixedSeverity,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
EndTime: time.Now().Add(1000),
|
||||||
|
UUID: "reportUUID3",
|
||||||
|
}
|
||||||
|
id, err := suite.reportDao.Create(ctx, rp)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.True(id > 0)
|
||||||
|
err = suite.nc.updateReport(ctx, vuls, rp.UUID)
|
||||||
|
suite.NoError(err)
|
||||||
|
rpts, err := suite.reportDao.List(ctx, q.New(q.KeyWords{"UUID": rp.UUID}))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(1, len(rpts))
|
||||||
|
suite.Equal(int64(2), rpts[0].CriticalCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].HighCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].MediumCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].LowCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].NoneCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].UnknownCnt)
|
||||||
|
suite.Equal(int64(1), rpts[0].FixableCnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseScoreFromVendorAttribute(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
vendorAttribute string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantNvdV3Score float64
|
||||||
|
}{
|
||||||
|
{"normal", args{`{"CVSS":{"nvd":{"V2Score":4.3,"V2Vector":"AV:N/AC:M/Au:N/C:N/I:N/A:P","V3Score":6.5,"V3Vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"}}}`}, 6.5},
|
||||||
|
{"both", args{`{"CVSS":{"nvd":{"V3Score":5.5,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"},"redhat":{"V3Score":6.2,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}}}`}, 5.5},
|
||||||
|
{"both2", args{`{"CVSS":{"nvd":{"V2Score":7.2,"V2Vector":"AV:L/AC:L/Au:N/C:C/I:C/A:C","V3Score":7.8,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"},"redhat":{"V3Score":7.8,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 7.8},
|
||||||
|
{"none", args{`{"CVSS":{"nvd":{"V2Score":7.2,"V2Vector":"AV:L/AC:L/Au:N/C:C/I:C/A:C","V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"},"redhat":{"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 0},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotNvdV3Score := parseScoreFromVendorAttribute(context.Background(), tt.args.vendorAttribute)
|
||||||
|
assert.Equalf(t, tt.wantNvdV3Score, gotNvdV3Score, "parseScoreFromVendorAttribute(%v)", tt.args.vendorAttribute)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -101,6 +101,9 @@ type Manager interface {
|
||||||
// []*scan.Report : report list
|
// []*scan.Report : report list
|
||||||
// error : non nil error if any errors occurred
|
// error : non nil error if any errors occurred
|
||||||
List(ctx context.Context, query *q.Query) ([]*scan.Report, error)
|
List(ctx context.Context, query *q.Query) ([]*scan.Report, error)
|
||||||
|
|
||||||
|
// Update update report information
|
||||||
|
Update(ctx context.Context, r *scan.Report, cols ...string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// basicManager is a default implementation of report manager.
|
// basicManager is a default implementation of report manager.
|
||||||
|
@ -219,3 +222,7 @@ func (bm *basicManager) DeleteByDigests(ctx context.Context, digests ...string)
|
||||||
func (bm *basicManager) List(ctx context.Context, query *q.Query) ([]*scan.Report, error) {
|
func (bm *basicManager) List(ctx context.Context, query *q.Query) ([]*scan.Report, error) {
|
||||||
return bm.dao.List(ctx, query)
|
return bm.dao.List(ctx, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bm *basicManager) Update(ctx context.Context, r *scan.Report, cols ...string) error {
|
||||||
|
return bm.dao.Update(ctx, r, cols...)
|
||||||
|
}
|
||||||
|
|
|
@ -127,6 +127,27 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*scan.Report, er
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update provides a mock function with given fields: ctx, r, cols
|
||||||
|
func (_m *Manager) Update(ctx context.Context, r *scan.Report, cols ...string) error {
|
||||||
|
_va := make([]interface{}, len(cols))
|
||||||
|
for _i := range cols {
|
||||||
|
_va[_i] = cols[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, r)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *scan.Report, ...string) error); ok {
|
||||||
|
r0 = rf(ctx, r, cols...)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateReportData provides a mock function with given fields: ctx, uuid, _a2
|
// UpdateReportData provides a mock function with given fields: ctx, uuid, _a2
|
||||||
func (_m *Manager) UpdateReportData(ctx context.Context, uuid string, _a2 string) error {
|
func (_m *Manager) UpdateReportData(ctx context.Context, uuid string, _a2 string) error {
|
||||||
ret := _m.Called(ctx, uuid, _a2)
|
ret := _m.Called(ctx, uuid, _a2)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user