Skip to content

Commit

Permalink
Add version comparison functionality for package_version (#169)
Browse files Browse the repository at this point in the history
* Add version comparison functionality for package_version
  • Loading branch information
rtorrero authored and nelsonkopliku committed May 26, 2023
1 parent 55f7dd1 commit 495750d
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 24 deletions.
83 changes: 73 additions & 10 deletions internal/factsengine/gatherers/packageversion.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
package gatherers

import (
"strconv"
"strings"

log "github.com/sirupsen/logrus"
"github.com/trento-project/agent/pkg/factsengine/entities"
"github.com/trento-project/agent/pkg/utils"
)

const (
PackageVersionGathererName = "package_version"
invalidVersionCompare = -2
)

// nolint:gochecknoglobals
var (
PackageVersionCommandError = entities.FactGatheringError{
Type: "package-version-cmd-error",
Message: "error getting version of package",
PackageVersionRpmCommandError = entities.FactGatheringError{
Type: "package-version-rpm-cmd-error",
Message: "error while fetching package version",
}

PackageVersionZypperCommandError = entities.FactGatheringError{
Type: "package-version-zypper-cmd-error",
Message: "error while executing zypper",
}

PackageVersionMissingArgument = entities.FactGatheringError{
Expand Down Expand Up @@ -50,19 +59,73 @@ func (g *PackageVersionGatherer) Gather(factsRequests []entities.FactRequest) ([
continue
}

version, err := g.executor.Exec(
"rpm", "-q", "--qf", "%{VERSION}", factReq.Argument)
packageName := factReq.Argument
requestedVersion := ""
if strings.Contains(factReq.Argument, ",") {
arguments := strings.SplitN(factReq.Argument, ",", 2)
packageName = arguments[0]
requestedVersion = arguments[1]
}

installedVersion, err := executeRpmVersionRetrieveCommand(g.executor, packageName)
if err != nil {
gatheringError := PackageVersionCommandError.Wrap(factReq.Argument)
log.Error(gatheringError)
fact = entities.NewFactGatheredWithError(factReq, gatheringError)
} else {
fact = entities.NewFactGatheredWithRequest(factReq, &entities.FactValueString{Value: (string(version))})
fact = entities.NewFactGatheredWithError(factReq, err)
facts = append(facts, fact)
continue
}

if requestedVersion != "" {
comparisonResult, err := executeZypperVersionCmpCommand(g.executor, installedVersion, requestedVersion)
if err != nil {
fact = entities.NewFactGatheredWithError(factReq, err)
facts = append(facts, fact)
continue
}
fact = entities.NewFactGatheredWithRequest(factReq, &entities.FactValueInt{Value: comparisonResult})
facts = append(facts, fact)
continue
}

fact = entities.NewFactGatheredWithRequest(factReq, &entities.FactValueString{Value: installedVersion})
facts = append(facts, fact)
}

log.Infof("Requested %s facts gathered", PackageVersionGathererName)
return facts, nil
}

func executeZypperVersionCmpCommand(
executor utils.CommandExecutor,
installedVersion string,
comparedVersion string,
) (int, *entities.FactGatheringError) {
zypperOutput, err := executor.Exec("/usr/bin/zypper", "--terse", "versioncmp", comparedVersion, installedVersion)
if err != nil {
gatheringError := PackageVersionZypperCommandError.Wrap(err.Error())
log.Error(gatheringError)
return invalidVersionCompare, gatheringError
}

result, err := strconv.ParseInt(string(zypperOutput), 10, 32)
if err != nil {
gatheringError := PackageVersionRpmCommandError.Wrap(err.Error())
log.Error(gatheringError)
return invalidVersionCompare, gatheringError
}

return int(result), nil
}

func executeRpmVersionRetrieveCommand(
executor utils.CommandExecutor,
packageName string,
) (string, *entities.FactGatheringError) {
rpmOutput, err := executor.Exec("/usr/bin/rpm", "-q", "--qf", "%{VERSION}", packageName)
if err != nil {
gatheringError := PackageVersionRpmCommandError.Wrap(string(rpmOutput) + err.Error())
log.Error(gatheringError)
return "", gatheringError
}

return string(rpmOutput), nil
}
89 changes: 75 additions & 14 deletions internal/factsengine/gatherers/packageversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (suite *PackageVersionTestSuite) SetupTest() {
}

func (suite *PackageVersionTestSuite) TestPackageVersionGathererNoArgumentProvided() {
suite.mockExecutor.On("Exec", "rpm", "-q", "--qf", "%{VERSION}", "").Return(
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "").Return(
[]byte("rpm: no arguments given for query"), nil)

p := gatherers.NewPackageVersionGatherer(suite.mockExecutor)
Expand Down Expand Up @@ -71,10 +71,16 @@ func (suite *PackageVersionTestSuite) TestPackageVersionGathererNoArgumentProvid
}

func (suite *PackageVersionTestSuite) TestPackageVersionGather() {
suite.mockExecutor.On("Exec", "rpm", "-q", "--qf", "%{VERSION}", "corosync").Return(
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "corosync").Return(
[]byte("2.4.5"), nil)
suite.mockExecutor.On("Exec", "rpm", "-q", "--qf", "%{VERSION}", "pacemaker").Return(
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "pacemaker").Return(
[]byte("2.0.5+20201202.ba59be712"), nil)
suite.mockExecutor.On("Exec", "/usr/bin/zypper", "--terse", "versioncmp", "2.4.4", "2.4.5").Return(
[]byte("-1"), nil)
suite.mockExecutor.On("Exec", "/usr/bin/zypper", "--terse", "versioncmp", "2.4.5", "2.4.5").Return(
[]byte("0"), nil)
suite.mockExecutor.On("Exec", "/usr/bin/zypper", "--terse", "versioncmp", "2.4.6", "2.4.5").Return(
[]byte("1"), nil)

p := gatherers.NewPackageVersionGatherer(suite.mockExecutor)

Expand All @@ -91,6 +97,24 @@ func (suite *PackageVersionTestSuite) TestPackageVersionGather() {
Argument: "pacemaker",
CheckID: "check2",
},
{
Name: "corosync_same_version",
Gatherer: "package_version",
Argument: "corosync,2.4.5",
CheckID: "check3",
},
{
Name: "corosync_older_than_installed",
Gatherer: "package_version",
Argument: "corosync,2.4.4",
CheckID: "check4",
},
{
Name: "corosync_newer_than_installed",
Gatherer: "package_version",
Argument: "corosync,2.4.6",
CheckID: "check5",
},
}

factResults, err := p.Gather(factRequests)
Expand All @@ -106,25 +130,43 @@ func (suite *PackageVersionTestSuite) TestPackageVersionGather() {
Value: &entities.FactValueString{Value: "2.0.5+20201202.ba59be712"},
CheckID: "check2",
},
{
Name: "corosync_same_version",
Value: &entities.FactValueInt{Value: 0},
CheckID: "check3",
},
{
Name: "corosync_older_than_installed",
Value: &entities.FactValueInt{Value: -1},
CheckID: "check4",
},
{
Name: "corosync_newer_than_installed",
Value: &entities.FactValueInt{Value: 1},
CheckID: "check5",
},
}

suite.NoError(err)
suite.ElementsMatch(expectedResults, factResults)
}

func (suite *PackageVersionTestSuite) TestPackageVersionGatherMissingPackageError() {
suite.mockExecutor.On("Exec", "rpm", "-q", "--qf", "%{VERSION}", "corosync").Return(
func (suite *PackageVersionTestSuite) TestPackageVersionGatherErrors() {
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "sbd").Return(
[]byte("package sbd is not installed"), errors.New(""))
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "pacemake").Return(
[]byte("package pacemake is not installed"), errors.New(""))
suite.mockExecutor.On("Exec", "/usr/bin/rpm", "-q", "--qf", "%{VERSION}", "corosync").Return(
[]byte("2.4.5"), nil)
suite.mockExecutor.On("Exec", "rpm", "-q", "--qf", "%{VERSION}", "pacemake").Return(
[]byte("Error getting version of package: pacemake\n"), errors.New("some error"))

suite.mockExecutor.On("Exec", "/usr/bin/zypper", "--terse", "versioncmp", "1.2.3", "2.4.5").Return(
[]byte(""), errors.New("zypper: command not found"))
p := gatherers.NewPackageVersionGatherer(suite.mockExecutor)

factRequests := []entities.FactRequest{
{
Name: "corosync",
Name: "sbd_compare_version",
Gatherer: "package_version",
Argument: "corosync",
Argument: "sbd,1.2.3",
CheckID: "check1",
},
{
Expand All @@ -133,25 +175,44 @@ func (suite *PackageVersionTestSuite) TestPackageVersionGatherMissingPackageErro
Argument: "pacemake",
CheckID: "check2",
},
{
Name: "corosync_compare",
Gatherer: "package_version",
Argument: "corosync,1.2.3",
CheckID: "check3",
},
}

factResults, err := p.Gather(factRequests)

expectedResults := []entities.Fact{
{
Name: "corosync",
Value: &entities.FactValueString{Value: "2.4.5"},
Name: "sbd_compare_version",
Value: nil,
Error: &entities.FactGatheringError{
Message: "error while fetching package version: package sbd is not installed",
Type: "package-version-rpm-cmd-error",
},
CheckID: "check1",
},
{
Name: "pacemaker",
Value: nil,
Error: &entities.FactGatheringError{
Message: "error getting version of package: pacemake",
Type: "package-version-cmd-error",
Message: "error while fetching package version: package pacemake is not installed",
Type: "package-version-rpm-cmd-error",
},
CheckID: "check2",
},
{
Name: "corosync_compare",
Value: nil,
Error: &entities.FactGatheringError{
Message: "error while executing zypper: zypper: command not found",
Type: "package-version-zypper-cmd-error",
},
CheckID: "check3",
},
}

suite.NoError(err)
Expand Down

0 comments on commit 495750d

Please sign in to comment.