func (chk PHPConfig) Status() (int, string, error) { // getPHPVariable returns the value of a PHP configuration value as a string // or just "" if it doesn't exist getPHPVariable := func(name string) (val string) { quote := func(str string) string { return "\"" + str + "\"" } // php -r 'echo get_cfg_var("default_mimetype"); echo := fmt.Sprintf("echo get_cfg_var(%s);", quote(name)) cmd := exec.Command("php", "-r", echo) out, err := cmd.CombinedOutput() if err != nil { errutil.ExecError(cmd, string(out), err) } return string(out) } actualValue := getPHPVariable(chk.variable) if actualValue == chk.value { return errutil.Success() } else if actualValue == "" { msg := "PHP configuration variable not set" return errutil.GenericError(msg, chk.value, []string{actualValue}) } msg := "PHP variable did not match expected value" return errutil.GenericError(msg, chk.value, []string{actualValue}) }
func (chk SystemctlUnitFileStatus) Status() (int, string, error) { units, statuses, err := systemdstatus.UnitFileStatuses() if err != nil { return 1, "", err } var actualStatus string found := false for _, unit := range units { if unit == chk.unit { found = true } } if !found { return 1, "Unit file could not be found: " + chk.unit, nil } for i, un := range units { if un == chk.unit { actualStatus = statuses[i] if actualStatus == chk.status { return errutil.Success() } } } msg := "Unit didn't have status" return errutil.GenericError(msg, chk.status, []string{actualStatus}) }
// RoutingTableMatch asks: Is this value in this column of the routing table? func RoutingTableMatch(col string, str string) (int, string, error) { column := RoutingTableColumn(col) if tabular.StrIn(str, column) { return errutil.Success() } return errutil.GenericError("Not found in routing table", str, column) }
// freeMemOrSwap is an abstraction of FreeMemory and FreeSwap, which measures // if the desired resource has a quantity free above the amount specified func freeMemOrSwap(input string, swapOrMem string) (int, string, error) { amount, units, err := chkutil.SeparateByteUnits(input) if err != nil { log.WithFields(log.Fields{ "err": err.Error(), }).Fatal("Couldn't separate string into a scalar and units") } var actualAmount int switch strings.ToLower(swapOrMem) { case "memory": actualAmount, err = memstatus.FreeMemory(units) case "swap": actualAmount, err = memstatus.FreeSwap(units) default: log.Fatalf("Invalid option passed to freeMemoOrSwap: %s", swapOrMem) } if err != nil { return 1, "", err } else if actualAmount > amount { return errutil.Success() } msg := "Free " + swapOrMem + " lower than defined threshold" actualString := fmt.Sprint(actualAmount) + units return errutil.GenericError(msg, input, []string{actualString}) }
func (chk DockerRunningAPI) Status() (int, string, error) { running := getRunningContainersAPI(chk.path) if tabular.StrContainedIn(chk.name, running) { return errutil.Success() } msg := "Docker container not runnning" return errutil.GenericError(msg, chk.name, running) }
// ResponseMatchesGeneral is an abstraction of ResponseMatches and // ResponseMatchesInsecure that simply varies in the security of the connection func ResponseMatchesGeneral(urlstr string, re *regexp.Regexp, secure bool) (int, string, error) { body := chkutil.URLToBytes(urlstr, secure) if re.Match(body) { return errutil.Success() } msg := "Response didn't match regexp" return errutil.GenericError(msg, re.String(), []string{string(body)}) }
// timerCheck is pure DRY for SystemctlTimer and SystemctlTimerLoaded func timerCheck(unit string, all bool) (int, string, error) { timers, err := systemdstatus.Timers(all) if err != nil { return 1, "", err } else if tabular.StrIn(unit, timers) { return errutil.Success() } return errutil.GenericError("Timer not found", unit, timers) }
// ipCheck(int, string, error) is an abstraction of IP4 and // IP6 func ipCheck(name string, address *net.IP, version int) (int, string, error) { ips := netstatus.InterfaceIPs(name) for _, ip := range ips { if ip.Equal(*address) { return errutil.Success() } } return errutil.GenericError("Interface does not have IP", address, ips) }
func (chk SystemctlSockListening) Status() (int, string, error) { listening, err := systemdstatus.ListeningSockets() if err != nil { return 1, "", err } if tabular.StrIn(chk.path, listening) { return errutil.Success() } return errutil.GenericError("Socket wasn't listening", chk.path, listening) }
func (chk DockerImage) Status() (int, string, error) { images, err := dockerstatus.DockerImageRepositories() if err != nil { return 1, "", err } if tabular.StrIn(chk.name, images) { return errutil.Success() } return errutil.GenericError("Docker image was not found", chk.name, images) }
func (chk DockerImageRegexp) Status() (int, string, error) { images, err := dockerstatus.DockerImageRepositories() if err != nil { return 1, "", err } if tabular.ReIn(chk.re, images) { return errutil.Success() } msg := "Docker image was not found." return errutil.GenericError(msg, chk.re.String(), images) }
func (chk PortTCP) Status() (int, string, error) { if netstatus.PortOpen("tcp", chk.port) { return errutil.Success() } // convert ports to string to send to errutil.GenericError var strPorts []string for _, port := range netstatus.OpenPorts("tcp") { strPorts = append(strPorts, fmt.Sprint(port)) } return errutil.GenericError("Port not open", fmt.Sprint(chk.port), strPorts) }
func (chk DockerRunningRegexp) Status() (int, string, error) { running, err := dockerstatus.RunningContainers() if err != nil { return 1, "", err } if tabular.ReIn(chk.re, running) { return errutil.Success() } msg := "Docker container not runnning" return errutil.GenericError(msg, chk.re.String(), running) }
func (chk SwapUsage) Status() (int, string, error) { actualPercentUsed, err := memstatus.UsedSwap("percent") if err != nil { return 1, "", err } if actualPercentUsed < int(chk.maxPercentUsed) { return errutil.Success() } msg := "Swap usage above defined maximum" slc := []string{fmt.Sprint(actualPercentUsed)} return errutil.GenericError(msg, fmt.Sprint(chk.maxPercentUsed), slc) }
func (chk CommandOutputMatches) Status() (int, string, error) { cmd := exec.Command("bash", "-c", chk.Command) out, err := cmd.CombinedOutput() if err != nil { errutil.ExecError(cmd, string(out), err) } if chk.re.Match(out) { return errutil.Success() } msg := "Command output did not match regexp" return errutil.GenericError(msg, chk.re.String(), []string{string(out)}) }
func (chk InodeUsage) Status() (int, string, error) { actualPercentUsed, err := fsstatus.PercentInodesUsed(chk.filesystem) if err != nil { return 1, "Unexpected error", err } if actualPercentUsed < chk.maxPercentUsed { return errutil.Success() } msg := "More disk space used than expected" slc := []string{fmt.Sprint(actualPercentUsed) + "%"} return errutil.GenericError(msg, fmt.Sprint(chk.maxPercentUsed)+"%", slc) }
// groupNotFound creates generic error messages and exit codes for groupExits, // UserInGroup, and GroupID func groupNotFound(name string) (int, string, error) { // get a nicely formatted list of groups that do exist var existing []string groups, err := usrstatus.Groups() if err != nil { return 1, "", err } for _, group := range groups { existing = append(existing, group.Name) } return errutil.GenericError("Group not found", name, existing) }
func (chk Module) Status() (int, string, error) { // kernelModules returns a list of all Modules that are currently loaded // TODO just read from /proc/Modules kernelModules := func() (Modules []string) { cmd := exec.Command("/sbin/lsmod") return chkutil.CommandColumnNoHeader(0, cmd) } Modules := kernelModules() if tabular.StrIn(chk.name, Modules) { return errutil.Success() } return errutil.GenericError("Module is not loaded", chk.name, Modules) }
func (chk GroupID) Status() (int, string, error) { groups, err := usrstatus.Groups() if err != nil { return 0, "", err } for _, g := range groups { if g.Name == chk.name { if g.ID == chk.id { return errutil.Success() } msg := "Group does not have expected ID" return errutil.GenericError(msg, chk.id, []int{g.ID}) } } return groupNotFound(chk.name) }
func (chk InterfaceExists) Status() (int, string, error) { // getInterfaceNames returns the names of all network interfaces getInterfaceNames := func() (interfaces []string) { for _, iface := range netstatus.GetInterfaces() { interfaces = append(interfaces, iface.Name) } return } interfaces := getInterfaceNames() for _, iface := range interfaces { if iface == chk.name { return errutil.Success() } } return errutil.GenericError("Interface does not exist", chk.name, interfaces) }
func (chk Temp) Status() (int, string, error) { // allCoreTemps returns the Temperature of each core allCoreTemps := func() (Temps []int) { cmd := exec.Command("sensors") out, err := cmd.CombinedOutput() outstr := string(out) errutil.ExecError(cmd, outstr, err) restr := `Core\s\d+:\s+[\+\-](?P<Temp>\d+)\.*\d*°C` re := regexp.MustCompile(restr) for _, line := range regexp.MustCompile(`\n+`).Split(outstr, -1) { if re.MatchString(line) { // submatch captures only the integer part of the Temperature matchDict := chkutil.SubmatchMap(re, line) if _, ok := matchDict["Temp"]; !ok { log.WithFields(log.Fields{ "regexp": re.String(), "matchDict": matchDict, "output": outstr, }).Fatal("Couldn't find any Temperatures in `sensors` output") } TempInt64, err := strconv.ParseInt(matchDict["Temp"], 10, 64) if err != nil { log.WithFields(log.Fields{ "regexp": re.String(), "matchDict": matchDict, "output": outstr, "error": err.Error(), }).Fatal("Couldn't parse integer from `sensors` output") } Temps = append(Temps, int(TempInt64)) } } return Temps } // getCoreTemp returns an integer Temperature for a certain core getCoreTemp := func(core int) (Temp int) { Temps := allCoreTemps() errutil.IndexError("No such core available", core, Temps) return Temps[core] } Temp := getCoreTemp(0) if Temp < int(chk.max) { return errutil.Success() } msg := "Core Temp exceeds defined maximum" return errutil.GenericError(msg, chk.max, []string{fmt.Sprint(Temp)}) }
func (chk PacmanIgnore) Status() (int, string, error) { path := "/etc/pacman.conf" data := chkutil.FileToString(path) re := regexp.MustCompile(`[^#]IgnorePkg\s+=\s+.+`) find := re.FindString(data) var packages []string if find != "" { spl := strings.Split(find, " ") errutil.IndexError("Not enough lines in "+path, 2, spl) packages = spl[2:] // first two are "IgnorePkg" and "=" if tabular.StrIn(chk.pkg, packages) { return errutil.Success() } } msg := "Couldn't find package in IgnorePkg" return errutil.GenericError(msg, chk.pkg, packages) }
func (chk Up) Status() (int, string, error) { // getUpInterfaces returns all the names of the interfaces that are up getUpInterfaces := func() (interfaceNames []string) { for _, iface := range netstatus.GetInterfaces() { if iface.Flags&net.FlagUp != 0 { interfaceNames = append(interfaceNames, iface.Name) } } return interfaceNames } upInterfaces := getUpInterfaces() if tabular.StrIn(chk.name, upInterfaces) { return errutil.Success() } return errutil.GenericError("Interface is not up", chk.name, upInterfaces) }
func (chk Gateway) Status() (int, string, error) { // getGatewayAddress filters all Gateway IPs for a non-zero value getGatewayAddress := func() (addr string) { ips := RoutingTableColumn("Gateway") for _, ip := range ips { if ip != "0.0.0.0" { return ip } } return "0.0.0.0" } GatewayIP := getGatewayAddress() if chk.ip.String() == GatewayIP { return errutil.Success() } msg := "Gateway does not have address" return errutil.GenericError(msg, chk.ip.String(), []string{GatewayIP}) }
func (chk CPUUsage) Status() (int, string, error) { // TODO check that parameters are in range 0 < x < 100 cpuPercentUsed := func(sampleTime time.Duration) float32 { idle0, total0 := getCPUSample() time.Sleep(sampleTime) idle1, total1 := getCPUSample() idleTicks := float32(idle1 - idle0) totalTicks := float32(total1 - total0) return (100 * (totalTicks - idleTicks) / totalTicks) } actualPercentUsed := cpuPercentUsed(3 * time.Second) if actualPercentUsed < float32(chk.maxPercentUsed) { return errutil.Success() } msg := "CPU usage above defined maximum" slc := []string{fmt.Sprint(actualPercentUsed)} return errutil.GenericError(msg, fmt.Sprint(chk.maxPercentUsed), slc) }
func (chk Running) Status() (int, string, error) { // getRunningCommands returns the entries in the "Command" column of `ps aux` getRunningCommands := func() (Commands []string) { cmd := exec.Command("ps", "aux") return chkutil.CommandColumnNoHeader(10, cmd) } // remove this process from consideration Commands := getRunningCommands() var filtered []string for _, cmd := range Commands { if !strings.Contains(cmd, "distributive") { filtered = append(filtered, cmd) } } if tabular.StrIn(chk.name, filtered) { return errutil.Success() } return errutil.GenericError("Process not Running", chk.name, filtered) }
func (chk Checksum) Status() (int, string, error) { // getFileChecksum is self-explanatory fileChecksum := func(algorithm string, path string) string { if path == "" { log.Fatal("getFileChecksum got a blank path") } else if _, err := os.Stat(chk.path); err != nil { log.WithFields(log.Fields{ "path": chk.path, }).Fatal("fileChecksum got an invalid path") } // we already validated the aglorithm chksum, _ := fsstatus.Checksum(algorithm, chkutil.FileToBytes(path)) return chksum } actualChksum := fileChecksum(chk.algorithm, chk.path) if actualChksum == chk.expectedChksum { return errutil.Success() } msg := "Checksums do not match for file: " + chk.path return errutil.GenericError(msg, chk.expectedChksum, []string{actualChksum}) }
// existsRepoWithProperty is an abstraction of YumRepoExists and YumRepoURL. // It takes a struct field name to check, and an expected value. If the expected // value is found in the field of a repo, it returns 0, "" else an error message. // Valid choices for prop: "URL" | "Name" | "Name" func existsRepoWithProperty(prop string, val *regexp.Regexp, manager string) (int, string, error) { var properties []string for _, repo := range getRepos(manager) { switch prop { case "URL": properties = append(properties, repo.URL) case "Name": properties = append(properties, repo.Name) case "Status": properties = append(properties, repo.Status) case "ID": properties = append(properties, repo.ID) default: log.Fatal("Repos don't have the requested property: " + prop) } } if tabular.ReIn(val, properties) { return errutil.Success() } msg := "Repo with given " + prop + " not found" return errutil.GenericError(msg, val.String(), properties) }
func (chk GatewayInterface) Status() (int, string, error) { // getGatewayInterface returns the interface that the default Gateway is // operating on getGatewayInterface := func() (iface string) { ips := RoutingTableColumn("Gateway") names := RoutingTableColumn("Iface") for i, ip := range ips { if ip != "0.0.0.0" { msg := "Fewer names in kernel routing table than IPs" errutil.IndexError(msg, i, names) return names[i] // interface name } } return "" } iface := getGatewayInterface() if chk.name == iface { return errutil.Success() } msg := "Default Gateway does not operate on interface" return errutil.GenericError(msg, chk.name, []string{iface}) }
func (chk DiskUsage) Status() (int, string, error) { // percentFSUsed gets the percent of the filesystem that is occupied percentFSUsed := func(path string) int { // get FS info (*nix systems only!) var stat syscall.Statfs_t syscall.Statfs(path, &stat) // blocks * size of block = available size totalBytes := stat.Blocks * uint64(stat.Bsize) availableBytes := stat.Bavail * uint64(stat.Bsize) usedBytes := totalBytes - availableBytes percentUsed := int((float64(usedBytes) / float64(totalBytes)) * 100) return percentUsed } actualPercentUsed := percentFSUsed(chk.path) if actualPercentUsed < int(chk.maxPercentUsed) { return errutil.Success() } msg := "More disk space used than expected" slc := []string{fmt.Sprint(actualPercentUsed) + "%"} return errutil.GenericError(msg, fmt.Sprint(chk.maxPercentUsed)+"%", slc) }