github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/inspector/rule/encoding.go (about)

     1  package rule
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  	"text/template"
    10  
    11  	yaml "gopkg.in/yaml.v2"
    12  )
    13  
    14  // ReadFromFile returns the list of rules contained in the specified file
    15  func ReadFromFile(file string, vars map[string]string) ([]Rule, error) {
    16  	if _, err := os.Stat(file); os.IsNotExist(err) {
    17  		return nil, fmt.Errorf("%q does not exist", file)
    18  	}
    19  	tmpl, err := template.ParseFiles(file)
    20  	if err != nil {
    21  		return nil, fmt.Errorf("error parsing file %q: %v", file, err)
    22  	}
    23  	var rawRules bytes.Buffer
    24  	err = tmpl.Execute(&rawRules, vars)
    25  	if err != nil {
    26  		return nil, fmt.Errorf("error reading rules from %q: %v", file, err)
    27  	}
    28  	rules, err := UnmarshalRulesYAML(rawRules.Bytes())
    29  	if err != nil {
    30  		return nil, fmt.Errorf("error unmarshaling rules from %q: %v", file, err)
    31  	}
    32  	// TODO: Validate the rules we just read
    33  	return rules, nil
    34  }
    35  
    36  // This catch all rule is used for unmarshaling
    37  // The reason for having this is that we don't know the Kind
    38  // of the rule we are reading before unmarshaling, so we
    39  // unmarshal into this catch all, where all fields are captured.
    40  // There might be a better way of doing this, but taking this
    41  // approach for now...
    42  type catchAllRule struct {
    43  	Meta                     `yaml:",inline"`
    44  	PackageName              string   `yaml:"packageName"`
    45  	PackageVersion           string   `yaml:"packageVersion"`
    46  	AcceptablePackageVersion string   `yaml:"acceptablePackageVersion"`
    47  	Executable               string   `yaml:"executable"`
    48  	Port                     int      `yaml:"port"`
    49  	ProcName                 string   `yaml:"procName"`
    50  	File                     string   `yaml:"file"`
    51  	ContentRegex             string   `yaml:"contentRegex"`
    52  	Timeout                  string   `yaml:"timeout"`
    53  	SupportedVersions        []string `yaml:"supportedVersions"`
    54  	Path                     string   `yaml:"path"`
    55  	MinimumBytes             string   `yaml:"minimumBytes"`
    56  }
    57  
    58  // UnmarshalRulesYAML unmarshals the data into a list of rules
    59  func UnmarshalRulesYAML(data []byte) ([]Rule, error) {
    60  	catchAllRules := []catchAllRule{}
    61  	if err := yaml.Unmarshal(data, &catchAllRules); err != nil {
    62  		return nil, err
    63  	}
    64  	return rulesFromCatchAllRules(catchAllRules)
    65  }
    66  
    67  // UnmarshalRulesJSON unmarshals the JSON rules into a list of rules
    68  func UnmarshalRulesJSON(data []byte) ([]Rule, error) {
    69  	catchAllRules := []catchAllRule{}
    70  	if err := json.Unmarshal(data, &catchAllRules); err != nil {
    71  		return nil, err
    72  	}
    73  	return rulesFromCatchAllRules(catchAllRules)
    74  }
    75  
    76  func rulesFromCatchAllRules(catchAllRules []catchAllRule) ([]Rule, error) {
    77  	rules := []Rule{}
    78  	for _, catchAllRule := range catchAllRules {
    79  		r, err := buildRule(catchAllRule)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  		rules = append(rules, r)
    84  	}
    85  	return rules, nil
    86  }
    87  
    88  func buildRule(catchAll catchAllRule) (Rule, error) {
    89  	kind := strings.ToLower(strings.TrimSpace(catchAll.Kind))
    90  	meta := Meta{
    91  		Kind: kind,
    92  		When: catchAll.When,
    93  	}
    94  	switch kind {
    95  	default:
    96  		return nil, fmt.Errorf("rule with kind %q is not supported", catchAll.Kind)
    97  	case "packagedependency":
    98  		r := PackageDependency{
    99  			PackageName:    catchAll.PackageName,
   100  			PackageVersion: catchAll.PackageVersion,
   101  		}
   102  		r.Meta = meta
   103  		return r, nil
   104  	case "packagenotinstalled":
   105  		r := PackageNotInstalled{
   106  			PackageName:              catchAll.PackageName,
   107  			PackageVersion:           catchAll.PackageVersion,
   108  			AcceptablePackageVersion: catchAll.AcceptablePackageVersion,
   109  		}
   110  		r.Meta = meta
   111  		return r, nil
   112  	case "executableinpath":
   113  		r := ExecutableInPath{
   114  			Executable: catchAll.Executable,
   115  		}
   116  		r.Meta = meta
   117  		return r, nil
   118  	case "dockerinpath":
   119  		r := DockerInPath{}
   120  		r.Meta = meta
   121  		return r, nil
   122  	case "tcpportavailable":
   123  		r := TCPPortAvailable{
   124  			Port:     catchAll.Port,
   125  			ProcName: catchAll.ProcName,
   126  		}
   127  		r.Meta = meta
   128  		return r, nil
   129  	case "tcpportaccessible":
   130  		r := TCPPortAccessible{
   131  			Port:    catchAll.Port,
   132  			Timeout: catchAll.Timeout,
   133  		}
   134  		r.Meta = meta
   135  		return r, nil
   136  	case "filecontentmatches":
   137  		r := FileContentMatches{
   138  			File:         catchAll.File,
   139  			ContentRegex: catchAll.ContentRegex,
   140  		}
   141  		r.Meta = meta
   142  		return r, nil
   143  	case "python2version":
   144  		r := Python2Version{
   145  			SupportedVersions: catchAll.SupportedVersions,
   146  		}
   147  		r.Meta = meta
   148  		return r, nil
   149  	case "freespace":
   150  		r := FreeSpace{
   151  			Path:         catchAll.Path,
   152  			MinimumBytes: catchAll.MinimumBytes,
   153  		}
   154  		r.Meta = meta
   155  		return r, nil
   156  
   157  	}
   158  }