ocket8888 commented on a change in pull request #5352: URL: https://github.com/apache/trafficcontrol/pull/5352#discussion_r536402406
########## File path: traffic_ops_ort/plugin_verifier/config/config.go ########## @@ -0,0 +1,90 @@ +package config + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "errors" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/pborman/getopt/v2" + "os" +) + +type Cfg struct { + CommandArgs []string + LogLocationDebug string + LogLocationError string + LogLocationInfo string + TrafficServerConfigDir string + TrafficServerPluginDir string +} + +var ( + defaultATSConfigDir = "/opt/trafficserver/etc/trafficserver" + defaultATSPluginDir = "/opt/trafficserver/libexec/trafficserver" +) + +func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) } +func (cfg Cfg) ErrorLog() log.LogLocation { return log.LogLocation(cfg.LogLocationError) } +func (cfg Cfg) InfoLog() log.LogLocation { return log.LogLocation(cfg.LogLocationInfo) } +func (cfg Cfg) WarningLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // warn logging is not used. +func (cfg Cfg) EventLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // event logging is not used. + +func Usage() { + fmt.Println("\nUsage: plugin_verifier [options] [optional config.file]") + fmt.Println("\t[options]:") + fmt.Println("\t--log-location-debug=[value] | -d [value], where to log debugs, default is empty") + fmt.Println("\t--log-location-error=[value], | -e [value], where to log errors, default is 'stderr'") + fmt.Println("\t--log-location-info=[value] | -i [value], where to log infos, default is 'stderr'") + fmt.Println("\t--trafficserver-config-dir=[value] | -c [value], where to find ATS config files, default is '/opt/trafficserver/etc/trafficserver'") + fmt.Println("\t--trafficserver-plugin-dir=[value] | -p [value], where to find ATS plugins, default is '/opt/trafficserver/libexec/trafficserver'") + fmt.Println("\t--help | -h, this help message\n") + os.Exit(0) +} + +func InitConfig() (Cfg, error) { Review comment: Would you care to give this - and other exported symbols - GoDoc comments? ########## File path: traffic_ops_ort/plugin_verifier/config/config.go ########## @@ -0,0 +1,90 @@ +package config + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "errors" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/pborman/getopt/v2" + "os" +) + +type Cfg struct { + CommandArgs []string + LogLocationDebug string + LogLocationError string + LogLocationInfo string + TrafficServerConfigDir string + TrafficServerPluginDir string +} + +var ( + defaultATSConfigDir = "/opt/trafficserver/etc/trafficserver" + defaultATSPluginDir = "/opt/trafficserver/libexec/trafficserver" +) + +func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) } +func (cfg Cfg) ErrorLog() log.LogLocation { return log.LogLocation(cfg.LogLocationError) } +func (cfg Cfg) InfoLog() log.LogLocation { return log.LogLocation(cfg.LogLocationInfo) } +func (cfg Cfg) WarningLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // warn logging is not used. +func (cfg Cfg) EventLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // event logging is not used. + +func Usage() { + fmt.Println("\nUsage: plugin_verifier [options] [optional config.file]") + fmt.Println("\t[options]:") + fmt.Println("\t--log-location-debug=[value] | -d [value], where to log debugs, default is empty") + fmt.Println("\t--log-location-error=[value], | -e [value], where to log errors, default is 'stderr'") + fmt.Println("\t--log-location-info=[value] | -i [value], where to log infos, default is 'stderr'") + fmt.Println("\t--trafficserver-config-dir=[value] | -c [value], where to find ATS config files, default is '/opt/trafficserver/etc/trafficserver'") + fmt.Println("\t--trafficserver-plugin-dir=[value] | -p [value], where to find ATS plugins, default is '/opt/trafficserver/libexec/trafficserver'") + fmt.Println("\t--help | -h, this help message\n") + os.Exit(0) +} + +func InitConfig() (Cfg, error) { + + logLocationDebugPtr := getopt.StringLong("log-location-debug", 'd', "", "Where to log debugs. May be a file path, stdout, stderr, or null, default ''") + logLocationErrorPtr := getopt.StringLong("log-location-error", 'e', "stderr", "Where to log errors. May be a file path, stdout, stderr, or null, default stderr") + logLocationInfoPtr := getopt.StringLong("log-location-info", 'i', "stderr", "Where to log infos. May be a file path, stdout, stderr, or null, default stderr") + atsConfigDirPtr := getopt.StringLong("trafficserver-config-dir", 'c', defaultATSConfigDir, "directory where ATS config files are stored.") + atsPluginDirPtr := getopt.StringLong("trafficserver-plugin-dir", 'p', defaultATSPluginDir, "directory where ATS plugins are stored.") + helpPtr := getopt.BoolLong("help", 'h', "Print usage information and exit") + getopt.Parse() + + if *helpPtr == true { + Usage() + } + + cfg := Cfg{ + CommandArgs: getopt.Args(), + LogLocationDebug: *logLocationDebugPtr, + LogLocationError: *logLocationErrorPtr, + LogLocationInfo: *logLocationInfoPtr, + TrafficServerConfigDir: *atsConfigDirPtr, + TrafficServerPluginDir: *atsPluginDirPtr, + } + + if err := log.InitCfg(cfg); err != nil { + return Cfg{}, errors.New("Initializing loggers: " + err.Error() + "\n") Review comment: error strings shouldn't capitalize the first word, nor end with punctuation or a newline ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) + verified, exists = pluginChecks[key] + log.Debugf("Verified plugin '%s', exists: %v\n", key, verified) + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", key, + lineNumber, lineNumber) + pluginErrorCount++ + } + } else if strings.HasPrefix(fields[ii], "@pparam") { + // any plugin parameters that end in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + sa := strings.Split(fields[ii], "=") + param := strings.TrimSpace(sa[1]) Review comment: same as above RE: segfault ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) + verified, exists = pluginChecks[key] + log.Debugf("Verified plugin '%s', exists: %v\n", key, verified) + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", key, + lineNumber, lineNumber) + pluginErrorCount++ + } + } else if strings.HasPrefix(fields[ii], "@pparam") { + // any plugin parameters that end in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + sa := strings.Split(fields[ii], "=") + param := strings.TrimSpace(sa[1]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + } else { // process a line from plugin.config + + // process a line from plugin.config + if strings.HasSuffix(fields[0], ".so") { Review comment: `fields` here might be empty, which would cause this line (and the one below it) to segfault ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/remap.config ########## @@ -0,0 +1,4 @@ +# remap.config Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/config/config.go ########## @@ -0,0 +1,90 @@ +package config + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "errors" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/pborman/getopt/v2" + "os" +) + +type Cfg struct { + CommandArgs []string + LogLocationDebug string + LogLocationError string + LogLocationInfo string + TrafficServerConfigDir string + TrafficServerPluginDir string +} + +var ( + defaultATSConfigDir = "/opt/trafficserver/etc/trafficserver" + defaultATSPluginDir = "/opt/trafficserver/libexec/trafficserver" +) + +func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) } +func (cfg Cfg) ErrorLog() log.LogLocation { return log.LogLocation(cfg.LogLocationError) } +func (cfg Cfg) InfoLog() log.LogLocation { return log.LogLocation(cfg.LogLocationInfo) } +func (cfg Cfg) WarningLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // warn logging is not used. +func (cfg Cfg) EventLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // event logging is not used. + +func Usage() { + fmt.Println("\nUsage: plugin_verifier [options] [optional config.file]") + fmt.Println("\t[options]:") + fmt.Println("\t--log-location-debug=[value] | -d [value], where to log debugs, default is empty") + fmt.Println("\t--log-location-error=[value], | -e [value], where to log errors, default is 'stderr'") + fmt.Println("\t--log-location-info=[value] | -i [value], where to log infos, default is 'stderr'") + fmt.Println("\t--trafficserver-config-dir=[value] | -c [value], where to find ATS config files, default is '/opt/trafficserver/etc/trafficserver'") + fmt.Println("\t--trafficserver-plugin-dir=[value] | -p [value], where to find ATS plugins, default is '/opt/trafficserver/libexec/trafficserver'") + fmt.Println("\t--help | -h, this help message\n") + os.Exit(0) +} Review comment: You get this functionality for free from [the `flag` package's exported functions](https://godoc.org/flag#PrintDefaults) ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main Review comment: Command docs can go in a GoDoc comment on the `main` package ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" Review comment: same as above RE: import grouping order ########## File path: traffic_ops_ort/plugin_verifier/config/config.go ########## @@ -0,0 +1,90 @@ +package config + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "errors" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/pborman/getopt/v2" + "os" Review comment: import grouping order should be: 1. standard libraries 2. ATC libraries/imports 3. Third-party vendored dependencies ########## File path: traffic_ops_ort/plugin_verifier/README.md ########## @@ -0,0 +1,35 @@ +# ATS plugin readiness verifier Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) + verified, exists = pluginChecks[key] + log.Debugf("Verified plugin '%s', exists: %v\n", key, verified) + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", key, + lineNumber, lineNumber) + pluginErrorCount++ + } + } else if strings.HasPrefix(fields[ii], "@pparam") { + // any plugin parameters that end in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + sa := strings.Split(fields[ii], "=") + param := strings.TrimSpace(sa[1]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + } else { // process a line from plugin.config + + // process a line from plugin.config + if strings.HasSuffix(fields[0], ".so") { + key := strings.TrimSpace(fields[0]) + verified, exists = pluginChecks[key] + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' is not available to the the installed trafficserver.\n", key, + lineNumber) + pluginErrorCount++ + } + } + // Check the arguments in a plugin.config file for possible plugin config files. + // Any plugin argument that ends in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + for ii := 1; ii < length; ii++ { + param := strings.TrimSpace(fields[ii]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + return pluginErrorCount +} + +// returns 'filename' exists 'true' or 'false' +func fileExists(filename string) bool { + log.Debugf("verifying plugin file at %s\n", filename) + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} + +// read the names of all available plugins in the +// installed trafficservers plugin directory. +func loadAvailablePlugins() { + files, err := ioutil.ReadDir(cfg.TrafficServerPluginDir) + if err != nil { + log.Errorf("%v\n", err) + os.Exit(1) + } + + for _, file := range files { + if strings.HasSuffix(file.Name(), ".so") { + log.Debugf("loaded plugin %s\n", file.Name()) + atsPlugins[file.Name()] = 1 + } + } +} + +func verifyPluginConfigfile(filename string) bool { + if filepath.IsAbs(filename) { + return fileExists(filename) + } else { + return fileExists(cfg.TrafficServerConfigDir + "/" + filename) + } +} + +// returns plugin is verified (filename exists), 'true' or 'false' +func verifyPlugin(filename string) bool { + + if !strings.HasSuffix(filename, ".so") { + return false + } + + if filepath.IsAbs(filename) { + return fileExists(filename) + } else { + return fileExists(cfg.TrafficServerPluginDir + "/" + filename) Review comment: same as above RE: path separator joining ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) Review comment: This line will segfault for config file lines like ``` @plugin= ``` which is obviously incorrect, but you should check the length of `fields` to avoid segfaulting. ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) + verified, exists = pluginChecks[key] + log.Debugf("Verified plugin '%s', exists: %v\n", key, verified) + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", key, + lineNumber, lineNumber) + pluginErrorCount++ + } + } else if strings.HasPrefix(fields[ii], "@pparam") { + // any plugin parameters that end in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + sa := strings.Split(fields[ii], "=") + param := strings.TrimSpace(sa[1]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + } else { // process a line from plugin.config + + // process a line from plugin.config + if strings.HasSuffix(fields[0], ".so") { + key := strings.TrimSpace(fields[0]) + verified, exists = pluginChecks[key] + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' is not available to the the installed trafficserver.\n", key, + lineNumber) + pluginErrorCount++ + } + } + // Check the arguments in a plugin.config file for possible plugin config files. + // Any plugin argument that ends in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + for ii := 1; ii < length; ii++ { Review comment: instead of looping over indices like this, could you use `range`? ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/bad-plugin.config ########## @@ -0,0 +1,6 @@ +# plugin.config Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/bad-remap-multiline.config ########## @@ -0,0 +1,40 @@ +# remap.config Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/plugin.config ########## @@ -0,0 +1,5 @@ +# plugin.config Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/plugin_verifier.go ########## @@ -0,0 +1,287 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "bufio" + "fmt" + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_ops_ort/plugin_verifier/config" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + cfg config.Cfg + atsPlugins = make(map[string]int) + pluginChecks = make(map[string]bool) + pluginParams = make(map[string]bool) +) + +// This function accepts config line data from either ATS +// a 'plugin.config' or a 'remap.config' format. +// +// It checks the configuration file line by line and verifies +// that any specified plugin exists in the file system at the +// complete file path or relative to the ATS plugins installation +// directory. Also, any plugin arguments or plugin parameters that +// end in '.config', '.cfg', or '.txt' are assumed to be plugin +// configuration files and they will be verified that the exist +// at the absolute path in the file name or relative to the ATS +// configuration files directory. +// +// Returns '0' if all plugins on the config line successfully verify +// otherwise, returns the the count of plugins that failed to verify. +// +func checkConfigLine(line string, lineNumber int) int { + + pluginErrorCount := 0 + exists := false + verified := false + + log.Debugf("line: %s\n", line) + + // create an array of whitespace delimited fields + l := regexp.MustCompile(`\s+`) + fields := l.Split(line, -1) + length := len(fields) + + log.Debugf("length: %d, fields: %v", length, fields) + + // processing a line from remap.config + if length > 3 && (fields[0] == "map" || + fields[0] == "map_with_recv_port" || + fields[0] == "map_with_referer" || + fields[0] == "reverse_map" || + fields[0] == "redirect" || + fields[0] == "redirect_temporary") { + + for ii := 3; ii < len(fields); ii++ { + if strings.HasPrefix(fields[ii], "@plugin=") { + sa := strings.Split(fields[ii], "=") + key := strings.TrimSpace(sa[1]) + verified, exists = pluginChecks[key] + log.Debugf("Verified plugin '%s', exists: %v\n", key, verified) + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", key, + lineNumber, lineNumber) + pluginErrorCount++ + } + } else if strings.HasPrefix(fields[ii], "@pparam") { + // any plugin parameters that end in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + sa := strings.Split(fields[ii], "=") + param := strings.TrimSpace(sa[1]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + } else { // process a line from plugin.config + + // process a line from plugin.config + if strings.HasSuffix(fields[0], ".so") { + key := strings.TrimSpace(fields[0]) + verified, exists = pluginChecks[key] + if !exists { + verified = verifyPlugin(key) + pluginChecks[key] = verified + } + if !verified { + log.Errorf("the plugin '%s' on line '%d' is not available to the the installed trafficserver.\n", key, + lineNumber) + pluginErrorCount++ + } + } + // Check the arguments in a plugin.config file for possible plugin config files. + // Any plugin argument that ends in '.config | .cfg | .txt' are + // assumed to be configuration files and are checked that they + // exist in the filesystem at the absolute location in the name + // or relative to the ATS configuration files directory. + m := regexp.MustCompile(`^*(\.config|\.cfg|\.txt)+`) + for ii := 1; ii < length; ii++ { + param := strings.TrimSpace(fields[ii]) + if m.MatchString(param) { + verified, exists = pluginParams[param] + if !exists { + verified = verifyPluginConfigfile(param) + pluginParams[param] = verified + } + if !verified { + log.Errorf("the plugin config file '%s' on line '%d' or near continuation line '%d' is not available to the installed trafficserver.\n", param, + lineNumber, lineNumber) + pluginErrorCount++ + } + } + } + } + return pluginErrorCount +} + +// returns 'filename' exists 'true' or 'false' +func fileExists(filename string) bool { + log.Debugf("verifying plugin file at %s\n", filename) + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} + +// read the names of all available plugins in the +// installed trafficservers plugin directory. +func loadAvailablePlugins() { + files, err := ioutil.ReadDir(cfg.TrafficServerPluginDir) + if err != nil { + log.Errorf("%v\n", err) + os.Exit(1) + } + + for _, file := range files { + if strings.HasSuffix(file.Name(), ".so") { + log.Debugf("loaded plugin %s\n", file.Name()) + atsPlugins[file.Name()] = 1 + } + } +} + +func verifyPluginConfigfile(filename string) bool { + if filepath.IsAbs(filename) { + return fileExists(filename) + } else { + return fileExists(cfg.TrafficServerConfigDir + "/" + filename) Review comment: Path separator joining should use [the standard library package `path/filepath`'s `Join` function](https://golang.org/pkg/path/filepath/#Join) ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/bad-remap.config ########## @@ -0,0 +1,4 @@ +# remap.config Review comment: Needs license header ########## File path: traffic_ops_ort/plugin_verifier/test-files/etc/remap-multiline.config ########## @@ -0,0 +1,38 @@ +# remap.config Review comment: Needs license header ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected]
