-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Accept fact value options in xml reader * Implement products gatherer * Add products gatherer to the standard list * Cleanup tests * Update function name to be more specific
- Loading branch information
Showing
5 changed files
with
287 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package gatherers | ||
|
||
import ( | ||
"path" | ||
|
||
"github.com/pkg/errors" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/afero" | ||
"github.com/trento-project/agent/pkg/factsengine/entities" | ||
) | ||
|
||
const ( | ||
ProductsGathererName = "products" | ||
productsDefaultPath = "/etc/products.d/" | ||
) | ||
|
||
// nolint:gochecknoglobals | ||
var ( | ||
ProductsFolderMissingError = entities.FactGatheringError{ | ||
Type: "products-folder-missing-error", | ||
Message: "products folder does not exist", | ||
} | ||
|
||
ProductsFolderReadingError = entities.FactGatheringError{ | ||
Type: "products-folder-reading-error", | ||
Message: "error reading the products folder", | ||
} | ||
|
||
ProductsFileReadingError = entities.FactGatheringError{ | ||
Type: "products-file-reading-error", | ||
Message: "error reading the products file", | ||
} | ||
|
||
productsXMLelementsToList = map[string]bool{ | ||
"distrotarget": true, | ||
"repository": true, | ||
"language": true, | ||
"url": true, | ||
"productdependency": true, | ||
} | ||
) | ||
|
||
type ProductsGatherer struct { | ||
fs afero.Fs | ||
productsPath string | ||
} | ||
|
||
func NewProductsGatherer(fs afero.Fs, productsPath string) *ProductsGatherer { | ||
return &ProductsGatherer{ | ||
fs: fs, | ||
productsPath: productsPath, | ||
} | ||
} | ||
|
||
func NewDefaultProductsGatherer() *ProductsGatherer { | ||
return &ProductsGatherer{fs: afero.NewOsFs(), productsPath: productsDefaultPath} | ||
} | ||
|
||
func (g *ProductsGatherer) Gather(factsRequests []entities.FactRequest) ([]entities.Fact, error) { | ||
facts := []entities.Fact{} | ||
log.Infof("Starting %s facts gathering process", ProductsGathererName) | ||
|
||
if exists, _ := afero.DirExists(g.fs, g.productsPath); !exists { | ||
gatheringError := ProductsFolderMissingError.Wrap(g.productsPath) | ||
log.Error(gatheringError.Error()) | ||
return nil, gatheringError | ||
} | ||
|
||
productFiles, err := afero.ReadDir(g.fs, g.productsPath) | ||
if err != nil { | ||
gatheringError := ProductsFolderReadingError.Wrap(g.productsPath).Wrap(err.Error()) | ||
log.Error(gatheringError.Error()) | ||
return nil, gatheringError | ||
} | ||
|
||
productsFactValueMap := make(map[string]entities.FactValue) | ||
for _, productFile := range productFiles { | ||
productFileName := productFile.Name() | ||
product, err := parseProductFile(g.fs, path.Join(g.productsPath, productFileName)) | ||
if err != nil { | ||
gatheringError := ProductsFileReadingError.Wrap(productFileName).Wrap(err.Error()) | ||
log.Error(gatheringError.Error()) | ||
return nil, gatheringError | ||
} | ||
|
||
productsFactValueMap[productFileName] = product | ||
} | ||
|
||
for _, requestedFact := range factsRequests { | ||
facts = append(facts, entities.NewFactGatheredWithRequest( | ||
requestedFact, &entities.FactValueMap{Value: productsFactValueMap})) | ||
} | ||
|
||
log.Infof("Requested %s facts gathered", ProductsGathererName) | ||
return facts, nil | ||
} | ||
|
||
func parseProductFile(fs afero.Fs, productFilePath string) (entities.FactValue, error) { | ||
productFile, err := afero.ReadFile(fs, productFilePath) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not open product file") | ||
} | ||
|
||
factValueMap, err := parseXMLToFactValueMap(productFile, productsXMLelementsToList) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not parse product file") | ||
} | ||
|
||
return factValueMap, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package gatherers_test | ||
|
||
import ( | ||
"path" | ||
"testing" | ||
|
||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/suite" | ||
"github.com/trento-project/agent/internal/factsengine/gatherers" | ||
"github.com/trento-project/agent/pkg/factsengine/entities" | ||
) | ||
|
||
const testProductsPath string = "/etc/products.d/" | ||
|
||
type ProductsGathererSuite struct { | ||
suite.Suite | ||
} | ||
|
||
func TestProductsGathererSuite(t *testing.T) { | ||
suite.Run(t, new(ProductsGathererSuite)) | ||
} | ||
|
||
func (s *ProductsGathererSuite) TestProductsGathererFolderMissingError() { | ||
fs := afero.NewMemMapFs() | ||
|
||
fr := []entities.FactRequest{ | ||
{ | ||
Name: "missing_folder", | ||
Gatherer: "products@v1", | ||
CheckID: "check1", | ||
}, | ||
} | ||
|
||
gatherer := gatherers.NewProductsGatherer(fs, testProductsPath) | ||
|
||
results, err := gatherer.Gather(fr) | ||
s.Nil(results) | ||
s.EqualError(err, "fact gathering error: products-folder-missing-error - "+ | ||
"products folder does not exist: /etc/products.d/") | ||
} | ||
|
||
func (s *ProductsGathererSuite) TestProductsGathererReadingError() { | ||
fs := afero.NewMemMapFs() | ||
|
||
err := afero.WriteFile(fs, path.Join(testProductsPath, "baseproduct"), []byte(` | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<product schemeversion="0"> | ||
<vendor>openSUSE</vendor> | ||
<name>Leap</name> | ||
<version>15.3</version> | ||
<release>2</releas | ||
`), 0777) | ||
|
||
s.NoError(err) | ||
|
||
fr := []entities.FactRequest{ | ||
{ | ||
Name: "invalid_xml", | ||
Gatherer: "products@v1", | ||
CheckID: "check1", | ||
}, | ||
} | ||
|
||
gatherer := gatherers.NewProductsGatherer(fs, testProductsPath) | ||
|
||
results, err := gatherer.Gather(fr) | ||
s.Nil(results) | ||
s.EqualError(err, "fact gathering error: products-file-reading-error - "+ | ||
"error reading the products file: baseproduct: could not parse product file: "+ | ||
"xml.Decoder.Token() - XML syntax error on line 8: unexpected EOF") | ||
} | ||
|
||
func (s *ProductsGathererSuite) TestProductsGathererSuccess() { | ||
fs := afero.NewMemMapFs() | ||
|
||
err := afero.WriteFile(fs, path.Join(testProductsPath, "baseproduct"), []byte(` | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<product schemeversion="0"> | ||
<vendor>openSUSE</vendor> | ||
<name>Leap</name> | ||
<version>15.3</version> | ||
<release>2</release> | ||
<urls> | ||
<url name="releasenotes">http://doc.opensuse.org/release-notes-openSUSE.rpm</url> | ||
</urls> | ||
</product> | ||
`), 0777) | ||
s.NoError(err) | ||
|
||
err = afero.WriteFile(fs, path.Join(testProductsPath, "otherproduct"), []byte(` | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<product schemeversion="0"> | ||
<vendor>openSUSE</vendor> | ||
<name>Other</name> | ||
<version>15.5</version> | ||
<release>1</release> | ||
</product> | ||
`), 0777) | ||
s.NoError(err) | ||
|
||
fr := []entities.FactRequest{ | ||
{ | ||
Name: "products", | ||
Gatherer: "products@v1", | ||
CheckID: "check1", | ||
}, | ||
} | ||
|
||
gatherer := gatherers.NewProductsGatherer(fs, testProductsPath) | ||
|
||
expectedFacts := []entities.Fact{ | ||
{ | ||
Name: "products", | ||
CheckID: "check1", | ||
Value: &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"baseproduct": &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"product": &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"schemeversion": &entities.FactValueInt{Value: 0}, | ||
"vendor": &entities.FactValueString{Value: "openSUSE"}, | ||
"name": &entities.FactValueString{Value: "Leap"}, | ||
"version": &entities.FactValueString{Value: "15.3"}, | ||
"release": &entities.FactValueString{Value: "2"}, | ||
"urls": &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"url": &entities.FactValueList{ | ||
Value: []entities.FactValue{ | ||
&entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"name": &entities.FactValueString{Value: "releasenotes"}, | ||
"#text": &entities.FactValueString{Value: "http://doc.opensuse.org/release-notes-openSUSE.rpm"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
"otherproduct": &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"product": &entities.FactValueMap{ | ||
Value: map[string]entities.FactValue{ | ||
"schemeversion": &entities.FactValueInt{Value: 0}, | ||
"vendor": &entities.FactValueString{Value: "openSUSE"}, | ||
"name": &entities.FactValueString{Value: "Other"}, | ||
"version": &entities.FactValueString{Value: "15.5"}, | ||
"release": &entities.FactValueString{Value: "1"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
Error: nil, | ||
}, | ||
} | ||
|
||
results, err := gatherer.Gather(fr) | ||
s.NoError(err) | ||
s.EqualValues(expectedFacts, results) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters