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 }