Skip to content

Commit

Permalink
Merge pull request #226 from opencontrol/add-workspace
Browse files Browse the repository at this point in the history
Create Workspace interface and make LocalWorkspace private
  • Loading branch information
afeld committed Oct 3, 2016
2 parents 62d5371 + 506cd61 commit 40b7371
Show file tree
Hide file tree
Showing 19 changed files with 255 additions and 178 deletions.
18 changes: 11 additions & 7 deletions commands/diff/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,32 @@ import (

// Inventory maintains the inventory of all the controls within a given workspace.
type Inventory struct {
*lib.LocalWorkspace
common.Workspace
masterControlList map[string]common.Control
actualSatisfiedControls map[string]common.Satisfies
MissingControlList map[string]common.Control
}

// retrieveMasterControlsList will gather the list of controls needed for a given certification.
func (i *Inventory) retrieveMasterControlsList() {
standardKeys := i.Certification.GetSortedStandards()
standardKeys := i.GetCertification().GetSortedStandards()
for _, standardKey := range standardKeys {
for _, controlKey := range i.Certification.GetControlKeysFor(standardKey) {
for _, controlKey := range i.GetCertification().GetControlKeysFor(standardKey) {
key := standardAndControlString(standardKey, controlKey)
if _, exists := i.masterControlList[key]; !exists {
i.masterControlList[key] = i.Standards.Get(standardKey).GetControl(controlKey)
standard, found := i.GetStandard(standardKey)
if !found {
continue
}
i.masterControlList[key] = standard.GetControl(controlKey)
}
}
}
}

// findDocumentedControls will find the list of all documented controls found within the workspace.
func (i *Inventory) findDocumentedControls() {
for _, component := range i.Components.GetAll() {
for _, component := range i.GetAllComponents() {
for _, satisfiedControl := range component.GetAllSatisfies() {
key := standardAndControlString(satisfiedControl.GetStandardKey(), satisfiedControl.GetControlKey())
if _, exists := i.actualSatisfiedControls[key]; !exists {
Expand Down Expand Up @@ -73,12 +77,12 @@ func ComputeGapAnalysis(config Config) (Inventory, []error) {
}
workspace, _ := lib.LoadData(config.OpencontrolDir, certificationPath)
i := Inventory{
LocalWorkspace: workspace,
Workspace: workspace,
masterControlList: make(map[string]common.Control),
actualSatisfiedControls: make(map[string]common.Satisfies),
MissingControlList: make(map[string]common.Control),
}
if i.Certification == nil || i.Components == nil {
if i.GetCertification() == nil || i.GetAllComponents() == nil {
return Inventory{}, []error{fmt.Errorf("Unable to load data in %s for certification %s", config.OpencontrolDir, config.Certification)}
}

Expand Down
2 changes: 1 addition & 1 deletion commands/docs/gitbook/gitbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Config struct {
// OpenControlGitBook struct is an extension of models.OpenControl that adds
// an exportPath
type OpenControlGitBook struct {
*lib.LocalWorkspace
common.Workspace
markdownPath string
exportPath string
FSUtil fs.Util
Expand Down
25 changes: 17 additions & 8 deletions commands/docs/gitbook/gitbookCertification.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"path/filepath"

"github.com/opencontrol/compliance-masonry/lib/common"
"github.com/opencontrol/compliance-masonry/lib/result"
)

func (openControl *OpenControlGitBook) getResponsibleRole(text string, component common.Component) string {
Expand Down Expand Up @@ -51,7 +50,7 @@ func (openControl *OpenControlGitBook) getParameter(text string, parameter commo
return text
}

func (openControl *OpenControlGitBook) getCoveredBy(text string, justification result.Verification) string {
func (openControl *OpenControlGitBook) getCoveredBy(text string, justification common.Verification) string {
if len(justification.SatisfiesData.GetCoveredBy()) > 0 {
text += "Covered By:\n"
}
Expand All @@ -61,7 +60,10 @@ func (openControl *OpenControlGitBook) getCoveredBy(text string, justification r
if componentKey == "" {
componentKey = justification.ComponentKey
}
component := openControl.Components.Get(componentKey)
component, found := openControl.GetComponent(componentKey)
if !found {
continue
}
text = openControl.getCoveredByVerification(text, component, coveredBy)
}
return text
Expand All @@ -88,14 +90,17 @@ func (openControl *OpenControlGitBook) getControlOrigin(text string, controlOrig
func (openControl *OpenControlGitBook) exportControl(control *ControlGitbook) (string, string) {
key := replaceParentheses(fmt.Sprintf("%s-%s", control.standardKey, control.controlKey))
text := fmt.Sprintf("#%s\n##%s\n", key, control.GetName())
selectJustifications := openControl.Justifications.Get(control.standardKey, control.controlKey)
selectJustifications := openControl.GetAllVerificationsWith(control.standardKey, control.controlKey)
// In the case that no information was found period for the standard and control
if len(selectJustifications) == 0 {
errorText := fmt.Sprintf("No information found for the combination of standard %s and control %s", control.standardKey, control.controlKey)
text = fmt.Sprintf("%s%s\n", text, errorText)
}
for _, justification := range selectJustifications {
component := openControl.Components.Get(justification.ComponentKey)
component, found := openControl.GetComponent(justification.ComponentKey)
if !found {
continue
}
text = fmt.Sprintf("%s\n#### %s\n", text, component.GetName())

text = openControl.getResponsibleRole(text, component)
Expand All @@ -113,11 +118,15 @@ func (openControl *OpenControlGitBook) exportControl(control *ControlGitbook) (s
func (openControl *OpenControlGitBook) exportStandards() {
standardsExportPath := filepath.Join(openControl.exportPath, "standards")
openControl.FSUtil.Mkdirs(standardsExportPath)
standardKeys := openControl.Certification.GetSortedStandards()
standardKeys := openControl.GetCertification().GetSortedStandards()
for _, standardKey := range standardKeys {
controlKeys := openControl.Standards.Get(standardKey).GetSortedControls()
standard, found := openControl.GetStandard(standardKey)
if !found {
continue
}
controlKeys := standard.GetSortedControls()
for _, controlKey := range controlKeys {
control := openControl.Standards.Get(standardKey).GetControls()[controlKey]
control := standard.GetControl(controlKey)
controlPath, controlText := openControl.exportControl(&ControlGitbook{control, standardsExportPath, standardKey, controlKey})
ioutil.WriteFile(controlPath, []byte(controlText), 0700)
}
Expand Down
3 changes: 2 additions & 1 deletion commands/docs/gitbook/gitbookCertification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ func TestExportControl(t *testing.T) {
dir,
fs.OSUtil{},
}
control := openControl.Standards.Get(example.standardKey).GetControl(example.controlKey)
standard, _ := openControl.GetStandard(example.standardKey)
control := standard.GetControl(example.controlKey)
actualPath, actualText := openControl.exportControl(&ControlGitbook{control, dir, example.standardKey, example.controlKey})
expectedPath := filepath.Join(dir, example.expectedPath)
// Verify the expected export path is the same as the actual export path
Expand Down
2 changes: 1 addition & 1 deletion commands/docs/gitbook/gitbookComponents.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (component *ComponentGitbook) exportComponent() (string, string) {
func (openControl *OpenControlGitBook) exportComponents() {
componentsExportPath := filepath.Join(openControl.exportPath, "components")
openControl.FSUtil.Mkdirs(componentsExportPath)
for _, component := range openControl.Components.GetAll() {
for _, component := range openControl.GetAllComponents() {
componentsGitBook := ComponentGitbook{component, componentsExportPath}
componentPath, componentText := componentsGitBook.exportComponent()
ioutil.WriteFile(componentPath, []byte(componentText), 0700)
Expand Down
11 changes: 7 additions & 4 deletions commands/docs/gitbook/gitbookSummaries.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func createFileName(fileNameParts ...string) fileName {
// BuildComponentsSummaries creates summaries the components for the general summary
func (openControl *OpenControlGitBook) buildComponentsSummaries() string {
summary := "\n## Components\n"
for _, component := range openControl.Components.GetAll() {
for _, component := range openControl.GetAllComponents() {
summary += exportLink(component.GetName(),
filepath.Join("components", createFileName(component.GetKey()).withExt(".md")))
}
Expand All @@ -51,12 +51,15 @@ func (openControl *OpenControlGitBook) buildStandardsSummaries() (string, *map[s
summary := createSubHeading("Standards")

// Go through all the standards for the certification.
standardKeys := openControl.Certification.GetSortedStandards()
standardKeys := openControl.GetCertification().GetSortedStandards()
for _, standardKey := range standardKeys {
// Find all the information for a particular standard.
standard := openControl.Standards.Get(standardKey)
standard, found := openControl.GetStandard(standardKey)
if !found {
continue
}
// Go through all the controls for the certification.
controlKeys := openControl.Certification.GetControlKeysFor(standardKey)
controlKeys := openControl.GetCertification().GetControlKeysFor(standardKey)
for _, controlKey := range controlKeys {
var controlSummary string
controlSummary, familyFileName, familySummaryMap =
Expand Down
9 changes: 7 additions & 2 deletions lib/certifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ package lib

import (
"github.com/opencontrol/compliance-masonry/lib/certifications"
"github.com/opencontrol/compliance-masonry/lib/common"
)

// LoadCertification struct loads certifications into a Certification struct
// and add it to the main object.
func (ws *LocalWorkspace) LoadCertification(certificationFile string) error {
func (ws *localWorkspace) LoadCertification(certificationFile string) error {
cert, err := certifications.Load(certificationFile)
if err != nil {
return err
}
ws.Certification = cert
ws.certification = cert
return nil
}

func (ws *localWorkspace) GetCertification() common.Certification {
return ws.certification
}
26 changes: 26 additions & 0 deletions lib/common/verifications.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package common

// Verification struct holds data for a specific component and verification
// This is an internal data structure that helps map standards and controls to components
type Verification struct {
ComponentKey string
SatisfiesData Satisfies
}

// Verifications is a slice of type Verifications
type Verifications []Verification

// Len returns the length of the GeneralReferences slice
func (slice Verifications) Len() int {
return len(slice)
}

// Less returns true if a GeneralReference is less than another reference
func (slice Verifications) Less(i, j int) bool {
return slice[i].ComponentKey < slice[j].ComponentKey
}

// Swap swaps the two GeneralReferences
func (slice Verifications) Swap(i, j int) {
slice[i], slice[j] = slice[j], slice[i]
}
57 changes: 57 additions & 0 deletions lib/common/verifications_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package common_test

import (
"github.com/opencontrol/compliance-masonry/lib/common"
"testing"
)

type verificationsLenTest struct {
verifications common.Verifications
expectedLength int
}

type verificationsLessTest struct {
verifications common.Verifications
leftIsLess bool
}

var verificationsLenTests = []verificationsLenTest{
// Check that the number of verifications stored is 0
{common.Verifications{}, 0},
// Check that the number of verifications stored is 1
{common.Verifications{common.Verification{}}, 1},
// Check that the number of verifications stored is 2
{common.Verifications{common.Verification{}, common.Verification{}}, 2},
}

func TestVerificationsLen(t *testing.T) {
for _, example := range verificationsLenTests {
actualLength := example.verifications.Len()
// Check that the number of verifications is the expected number
if example.expectedLength != actualLength {
t.Errorf("Expected %d, Actual: %d", example.expectedLength, actualLength)
}
}
}

var verificationsLessTests = []verificationsLessTest{
// Check that the left verification is less by comparing a number and letter
{common.Verifications{common.Verification{ComponentKey: "1", SatisfiesData: nil}, common.Verification{ComponentKey: "2", SatisfiesData: nil}}, true},
// Check that the left verification is not less by comparing two letters
{common.Verifications{common.Verification{ComponentKey: "a", SatisfiesData: nil}, common.Verification{ComponentKey: "a", SatisfiesData: nil}}, false},
// Check that the left verification is not less by comparing the same letter
{common.Verifications{common.Verification{ComponentKey: "a", SatisfiesData: nil}, common.Verification{ComponentKey: "2", SatisfiesData: nil}}, false},
// Check that the left verification is not less by comparing two numbers
{common.Verifications{common.Verification{ComponentKey: "2", SatisfiesData: nil}, common.Verification{ComponentKey: "1", SatisfiesData: nil}}, false},
// Check that the left verification is not less by comparing two numbers
}

func TestVerificationsLess(t *testing.T) {
for _, example := range verificationsLessTests {
actualLeftIsLess := example.verifications.Less(0, 1)
// Check that the verification on the left is less as expected
if example.leftIsLess != actualLeftIsLess {
t.Errorf("Expected %t, Actual: %t", actualLeftIsLess, actualLeftIsLess)
}
}
}
14 changes: 14 additions & 0 deletions lib/common/workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package common

// Workspace represents all the information such as components, standards, and certification as well as
// the result information such as the justifications.
type Workspace interface {
LoadComponents(string) []error
LoadStandards(string) []error
LoadCertification(string) error
GetCertification() Certification
GetAllComponents() []Component
GetComponent(componentKey string) (Component, bool)
GetStandard(standardKey string) (Standard, bool)
GetAllVerificationsWith(standardKey string, controlKey string) Verifications
}
29 changes: 19 additions & 10 deletions lib/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ func (components *componentsMap) add(component common.Component) {
components.Unlock()
}

// Get retrieves a new component from the component map
func (components *componentsMap) Get(key string) common.Component {
// get retrieves a new component from the component map
func (components *componentsMap) get(key string) (component common.Component, found bool) {
components.RLock()
defer components.RUnlock()
return components.mapping[key]
component, found = components.mapping[key]
return
}

// CompareAndAdd compares to see if the component exists in the map. If not, it adds the component.
// compareAndAdd compares to see if the component exists in the map. If not, it adds the component.
// Returns true if the component was added, returns false if the component was not added.
// This function is thread-safe.
func (components *componentsMap) CompareAndAdd(component common.Component) bool {
func (components *componentsMap) compareAndAdd(component common.Component) bool {
components.Lock()
defer components.Unlock()
_, exists := components.mapping[component.GetKey()]
Expand All @@ -47,8 +48,8 @@ func (components *componentsMap) CompareAndAdd(component common.Component) bool
return false
}

// GetAll retrieves all the components without giving directly to the map.
func (components *componentsMap) GetAll() []common.Component {
// getAll retrieves all the components without giving directly to the map.
func (components *componentsMap) getAll() []common.Component {
components.RLock()
defer components.RUnlock()
result := make([]common.Component, len(components.mapping))
Expand All @@ -62,16 +63,24 @@ func (components *componentsMap) GetAll() []common.Component {

// LoadComponent imports components into a Component struct and adds it to the
// Components map.
func (ws *LocalWorkspace) LoadComponent(componentDir string) error {
func (ws *localWorkspace) LoadComponent(componentDir string) error {
component, err := components.Load(componentDir)
if err != nil {
return err
}
// If the component is new, make sure we load the justifications as well.
if ws.Components.CompareAndAdd(component) {
ws.Justifications.LoadMappings(component)
if ws.components.compareAndAdd(component) {
ws.justifications.LoadMappings(component)
} else {
return fmt.Errorf("Component: %s exists!\n", component.GetKey())
}
return nil
}

func (ws *localWorkspace) GetAllComponents() []common.Component {
return ws.components.getAll()
}

func (ws *localWorkspace) GetComponent(component string) (common.Component, bool) {
return ws.components.get(component)
}
4 changes: 1 addition & 3 deletions lib/components/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"testing"

"github.com/blang/semver"
"github.com/opencontrol/compliance-masonry/lib"
"github.com/opencontrol/compliance-masonry/lib/common"
"github.com/opencontrol/compliance-masonry/lib/components"
v2 "github.com/opencontrol/compliance-masonry/lib/components/versions/2_0_0"
Expand Down Expand Up @@ -293,8 +292,7 @@ var componentTestErrors = []componentTestError{

func TestLoadComponentErrors(t *testing.T) {
for _, example := range componentTestErrors {
ws := &lib.LocalWorkspace{}
actualError := ws.LoadComponent(example.componentDir)
_, actualError := components.Load(example.componentDir)
// Check that the expected error is the actual error
if !assert.Equal(t, example.expectedError, actualError) {
t.Errorf("Expected %s, Actual: %s", example.expectedError, actualError)
Expand Down

0 comments on commit 40b7371

Please sign in to comment.