<commit_before>package main

import (
    "os"
    "os/exec"

    "time"
    "net/http"

    "errors"

    "io"
    "io/ioutil"

    "log"

//    "fmt"
    "strings"
    "runtime"
    "strconv" // For Itoa
//    "encoding/csv"
    "encoding/json"

    "github.com/go-martini/martini"
)

func main() {
    m := martini.Classic()

    // CPU count
    m.Get("/sh/numberofcores.php", func () ([]byte, error) {
        return json.Marshal(runtime.NumCPU())
    })

    // Server's hostname
    m.Get("/sh/hostname.php", func () ([]byte, error) {
        host, err := os.Hostname()

        if err != nil {
            return nil, err
        }

        return json.Marshal(host)
    })

    // PS
    m.Get("/sh/ps.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("ps", "aux").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput, 1, 1))
    })

    m.Get("/sh/df.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("df", "-Ph").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput, 1, 1))
    })

    m.Get("/sh/time.php", func () ([]byte, error) {
        raw, err := exec.Command("date").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(string(raw[:]))
    })

    m.Get("/sh/issue.php", func () ([]byte, error) {
        raw, err := exec.Command("uname", "-rsm").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(string(raw[:]))
    })

    m.Get("/sh/users.php", func () ([]byte, error) {
        data, err := ioutil.ReadFile("/etc/passwd")

        if err != nil {
            return nil, err
        }

        lines := strings.Split(string(data), "\n")

        // Output records
        var records [][]string

        for _, line := range lines {
            parts := strings.Split(line, ":")

            // Skip bad or empty lines
            if len(parts) != 7 {
                log.Println(len(parts))
                continue
            }

            // Parse base 10, 16 bit UID integer
            uid, err := strconv.ParseInt(parts[2], 10, 16)

            // Error parsing UID
            if err != nil {
                continue
            }

            userType := "user"

            // Check if system user
            if uid <= 499 {
                userType = "system"
            }

            user := []string{
                // User type
                userType,
                // Username
                parts[0],
                // Home directory
                parts[6],
            }

            records = append(records, user)
        }

        return json.Marshal(records)
    })

    m.Get("/sh/online.php", func () ([]byte, error) {
        raw, err := exec.Command("w").Output()

        if err != nil {
            return nil, err
        }

        // We'll add all the parsed lines here
        var entries [][]string

        // Skip first and last line of output
        for _, entry := range parseCommandTable(raw, 2, 1) {
            entries = append(entries, []string{
                // User
                entry[0],
                // From
                entry[2],
                // Login at
                entry[3],
                // Idle
                entry[4],
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/loadavg.php", func () ([]byte, error) {
        raw, err := exec.Command("w").Output()

        if err != nil {
            return nil, err
        }

        lines := strings.Split(string(raw[:]), "\n")
        headers := strings.Fields(lines[0])

        var cpuLoads [][2]string

        // Last 3 headers are CPU loads, clean them up
        for _, load := range headers[len(headers)-3:] {
            // Remove trailing coma if there
            cleanLoad := strings.Split(string(load), ",")[0]

            loadFloat, err := strconv.ParseFloat(cleanLoad, 32)

            if err != nil {
                continue
            }

            loadPercentage := int(loadFloat * 100) / runtime.NumCPU()

            cpuLoads = append(cpuLoads, [2]string{
                cleanLoad,
                strconv.Itoa(loadPercentage),
            })
        }

        return json.Marshal(cpuLoads)
    })

    m.Get("/sh/ping.php", func () ([]byte, error) {
        HOSTS := [4]string{
            "wikipedia.org",
            "github.com",
            "google.com",
            "gnu.org",
        }

        var entries [][]string

        for _, host := range HOSTS {
            ping, err := pingHost(host, 2)

            if err != nil {
                continue
            }

            entries = append(entries, []string{
                host,
                ping,
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/netstat.php", func () ([]byte, error) {
        raw, err := exec.Command("netstat", "-nt").Output()

        if err != nil {
            return nil, err
        }

        // Get entries as a table
        entries := parseCommandTable(raw, 2, 1)

        // Connections we care about
        var connections [][]string

        // Filter by protocol
        for _, entry := range entries {
            // Skip bad entries or non tcp
            if len(entry) != 6 || (entry[0] != "tcp" && entry[0] != "tcp4") {
                continue
            }

            connections = append(connections, entry)
        }

        // Count connections from a machine
        connectionCount := make(map[string]int)

        // Extract
        for _, conn := range connections {
            // Foreign Address
            remoteAddr := conn[4]

            // Increment counter
            connectionCount[remoteAddr]++
        }

        // Expected output by client
        var output [][2]string

        for remoteAddr, count := range connectionCount {
            // Compensate for difference between OSX and linux
            parts := strings.Split(strings.Replace(remoteAddr, ",", ".", 1), ".")
            ip := strings.Join(parts[:4], ".")
            // port := parts[4]

            output = append(output, [2]string{
                strconv.Itoa(count),
                ip,
            })
        }

        return json.Marshal(output)
    })

    m.Get("/sh/where.php", func () ([]byte, error) {
        SOFTWARE := []string{
            "php",
            "node",
            "mysql",
            "vim",
            "python",
            "ruby",
            "java",
            "apache2",
            "nginx",
            "openssl",
            "vsftpd",
            "make",
        }

        var entries [][2]string

        for _, bin := range SOFTWARE {
            path, err := exec.LookPath(bin)

            if err != nil {
                path = "Not Installed"
            }

            entries = append(entries, [2]string{
                bin,
                path,
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/mem.php", func () ([]byte, error) {
        var entries []string

        raw, err := exec.Command("free", "-m").Output()

        if err != nil {
            // OS X fallback
            raw, err := exec.Command("vm_stat").Output()

            if err != nil {
                return nil, err
            }

            vmMap := parseCommandMap(string(raw[:]))

            freePages := toMB(vmMap["Pages free"])
            activePages := toMB(vmMap["Pages active"])
            inactivePages := toMB(vmMap["Pages inactive"])
            speculativePages := toMB(vmMap["Pages speculative"])

            free := freePages + speculativePages
            used := activePages + inactivePages

            return json.Marshal([]string{
                "Mem:",

                // Total
                strconv.Itoa(free + used),

                // Used
                strconv.Itoa(used),

                // Free
                strconv.Itoa(free),
            })
        }

        // Linux
        entries = parseCommandTable(raw, 2, 1)[0][:4]

        return json.Marshal(entries)
    })

    m.Get("/sh/lastlog.php", func () ([]byte, error) {
        raw, err := exec.Command("last").Output()

        if err != nil {
            return nil, err
        }

        entries := parseCommandTable(raw, 0, 3)

        var data [][]string

        for _, entry := range entries {
            var row []string

            // Non user action
            if entry[1] == "~" {
                continue
            }

            if runtime.GOOS == "linux" {
                row = []string{
                    // Username
                    entry[0],
                    entry[2],
                    strings.Join(entry[3:6], " "),
                }
            } else if runtime.GOOS == "darwin" {
                row = []string{
                    // Username
                    entry[0],
                    // From
                    "",
                    strings.Join(entry[2:6], " "),
                }
            }

            data = append(data, row)
        }

        return json.Marshal(data)
    })

    m.Get("/sh/swap.php", func () ([]byte, error) {
        // Linux
        data, err := ioutil.ReadFile("/proc/swaps")

        if err != nil {
            // OS X fallback
            stats, err := topStats()

            // Failed everything
            if err != nil {
                return nil, err
            }

            fields := strings.Fields(stats["Swap"])

            used, _ := strconv.ParseInt(fields[0][:len(fields[0])-1], 10, 32)
            free, _ := strconv.ParseInt(fields[2][:len(fields[2])-1], 10, 32)

            usedB := mbToB(int(used))
            freeB := mbToB(int(free))

            return json.Marshal([][]string{
                {
                    "/var/vm/swapfile0",
                    "file",
                    readableSize(usedB + freeB),
                    readableSize(usedB),
                    "-1",
                },
            })
        }

        return json.Marshal(parseCommandTable(data, 1, 0))
    })

    m.Get("/sh/speed.php", func () ([]byte, error) {
        speed, err := downloadSpeed("http://cachefly.cachefly.net/10mb.test")

        if err != nil {
            return nil, err
        }

        data := make(map[string]float64)

        data["downstream"] = speed
        data["upstream"] = 0.0

        return json.Marshal(data)
    })

    // Serve static files
    m.Get("/.*", martini.Static(""))

    m.Run()
}

func parseCommandTable(rawOutput []byte, headerCount int, footerCount int) [][]string {
    // Convert output to a string (it's not binary data, so this is ok)
    output := string(rawOutput[:])

    // We'll add all the parsed lines here
    var entries [][]string

    // Lines of output
    lines := strings.Split(output, "\n")

    // Skip first and last line of output
    for _, str := range lines[headerCount:len(lines)-footerCount] {
        entries = append(entries, strings.Fields(str))
    }

    return entries
}

func parseCommandMap(output string) map[string]string {
    data := make(map[string]string)

    // Lines of output
    lines := strings.Split(output, "\n")

    for _, line := range lines {
        parts := strings.Split(line, ":")

        // Bad line, skip it
        if len(parts) != 2 {
            continue
        }

        key := strings.TrimSpace(parts[0])
        value := strings.TrimSpace(parts[1])

        // Remove trailing dot if exists
        if value[len(value) - 1] == '.' {
            value = value[:len(value) - 1]
        }

        data[key] = value
    }

    return data
}

// Get average pingTime to a host
func pingHost(hostname string, pingCount int) (string, error) {
    raw, err := exec.Command("ping", "-c", strconv.Itoa(pingCount), hostname).Output()

    if err != nil {
        return "", err
    }

    lines := strings.Split(string(raw[:]), "\n")

    if len(lines) < 2 {
        return "", errors.New("Bad output for ping command")
    }

    // Get 2nd last line
    lastLine := lines[len(lines)-2]

    // Extract average ping time as a string
    pingTime := strings.Split(strings.Split(lastLine, "=")[1], "/")[1]

    return pingTime, nil
}

func toMB(x string) int {
    num, _ := strconv.ParseInt(x, 10, 32)

    // 256 = 4096 / (1024 * 1024)
    return int(num) / 256
}

func mbToB(x int) int {
    return x * 1024 * 1024
}

// output frstring "top" command on OS X
func topStats() (map[string]string, error) {
    raw, err := exec.Command("top", "-S", "-l", "1", "-n", "0").Output()

    if err != nil {
        return nil, err
    }

    return parseCommandMap(string(raw[:])), nil
}

func readableSize(x int) string {
    KB := 1024
    MB := KB * KB
    GB := KB * KB * KB

    // Bytes
    if x < KB {
        return strconv.Itoa(x) + "b"
    } else if x < MB {
        return strconv.Itoa(x/KB) + "kb"
    } else if x < GB {
        return strconv.Itoa(x/MB) + "mb"
    }
    // else
    return strconv.Itoa(x/GB) + "gb"
}

// Download speed in kb/s
func downloadSpeed(url string) (float64, error) {
    resp, err := http.Get(url)

    if err != nil {
        return 0, err
    }

    total := 0

    // Read by 4k chunks
    buffer := make([]byte, 4096)

    // Start
    t1 := time.Now().UnixNano()

    for {
        buffer = buffer[:cap(buffer)]
        n, err := resp.Body.Read(buffer)

        total += n

        if err != nil {
            // EOF, that's good, now exit
            if err == io.EOF {
                break
            }

            return 0.0, err
        }

    }

    t2 := time.Now().UnixNano()

    // Time in seconds
    duration := float64(t2 - t1) / 1000000000.0

    // Actual download speed in kb/s
    speed := float64(total) / duration

    log.Println("Total :", total)
    log.Println("Duration :", duration)

    return speed, nil
}
<commit_msg>Add uptime command<commit_after>package main

import (
    "os"
    "os/exec"

    "./probe"

    "time"
    "net/http"

    "errors"

    "io"
    "io/ioutil"

    "log"

//    "fmt"
    "strings"
    "runtime"
    "strconv" // For Itoa
//    "encoding/csv"
    "encoding/json"

    "github.com/go-martini/martini"
)

func main() {
    m := martini.Classic()

    // CPU count
    m.Get("/sh/numberofcores.php", func () ([]byte, error) {
        return json.Marshal(runtime.NumCPU())
    })

    // Server's hostname
    m.Get("/sh/hostname.php", func () ([]byte, error) {
        host, err := os.Hostname()

        if err != nil {
            return nil, err
        }

        return json.Marshal(host)
    })

    // PS
    m.Get("/sh/ps.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("ps", "aux").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput, 1, 1))
    })

    m.Get("/sh/df.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("df", "-Ph").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput, 1, 1))
    })

    m.Get("/sh/time.php", func () ([]byte, error) {
        raw, err := exec.Command("date").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(string(raw[:]))
    })

    m.Get("/sh/issue.php", func () ([]byte, error) {
        raw, err := exec.Command("uname", "-rsm").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(string(raw[:]))
    })

    m.Get("/sh/users.php", func () ([]byte, error) {
        data, err := ioutil.ReadFile("/etc/passwd")

        if err != nil {
            return nil, err
        }

        lines := strings.Split(string(data), "\n")

        // Output records
        var records [][]string

        for _, line := range lines {
            parts := strings.Split(line, ":")

            // Skip bad or empty lines
            if len(parts) != 7 {
                log.Println(len(parts))
                continue
            }

            // Parse base 10, 16 bit UID integer
            uid, err := strconv.ParseInt(parts[2], 10, 16)

            // Error parsing UID
            if err != nil {
                continue
            }

            userType := "user"

            // Check if system user
            if uid <= 499 {
                userType = "system"
            }

            user := []string{
                // User type
                userType,
                // Username
                parts[0],
                // Home directory
                parts[6],
            }

            records = append(records, user)
        }

        return json.Marshal(records)
    })

    m.Get("/sh/online.php", func () ([]byte, error) {
        raw, err := exec.Command("w").Output()

        if err != nil {
            return nil, err
        }

        // We'll add all the parsed lines here
        var entries [][]string

        // Skip first and last line of output
        for _, entry := range parseCommandTable(raw, 2, 1) {
            entries = append(entries, []string{
                // User
                entry[0],
                // From
                entry[2],
                // Login at
                entry[3],
                // Idle
                entry[4],
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/loadavg.php", func () ([]byte, error) {
        raw, err := exec.Command("w").Output()

        if err != nil {
            return nil, err
        }

        lines := strings.Split(string(raw[:]), "\n")
        headers := strings.Fields(lines[0])

        var cpuLoads [][2]string

        // Last 3 headers are CPU loads, clean them up
        for _, load := range headers[len(headers)-3:] {
            // Remove trailing coma if there
            cleanLoad := strings.Split(string(load), ",")[0]

            loadFloat, err := strconv.ParseFloat(cleanLoad, 32)

            if err != nil {
                continue
            }

            loadPercentage := int(loadFloat * 100) / runtime.NumCPU()

            cpuLoads = append(cpuLoads, [2]string{
                cleanLoad,
                strconv.Itoa(loadPercentage),
            })
        }

        return json.Marshal(cpuLoads)
    })

    m.Get("/sh/ping.php", func () ([]byte, error) {
        HOSTS := [4]string{
            "wikipedia.org",
            "github.com",
            "google.com",
            "gnu.org",
        }

        var entries [][]string

        for _, host := range HOSTS {
            ping, err := pingHost(host, 2)

            if err != nil {
                continue
            }

            entries = append(entries, []string{
                host,
                ping,
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/netstat.php", func () ([]byte, error) {
        raw, err := exec.Command("netstat", "-nt").Output()

        if err != nil {
            return nil, err
        }

        // Get entries as a table
        entries := parseCommandTable(raw, 2, 1)

        // Connections we care about
        var connections [][]string

        // Filter by protocol
        for _, entry := range entries {
            // Skip bad entries or non tcp
            if len(entry) != 6 || (entry[0] != "tcp" && entry[0] != "tcp4") {
                continue
            }

            connections = append(connections, entry)
        }

        // Count connections from a machine
        connectionCount := make(map[string]int)

        // Extract
        for _, conn := range connections {
            // Foreign Address
            remoteAddr := conn[4]

            // Increment counter
            connectionCount[remoteAddr]++
        }

        // Expected output by client
        var output [][2]string

        for remoteAddr, count := range connectionCount {
            // Compensate for difference between OSX and linux
            parts := strings.Split(strings.Replace(remoteAddr, ",", ".", 1), ".")
            ip := strings.Join(parts[:4], ".")
            // port := parts[4]

            output = append(output, [2]string{
                strconv.Itoa(count),
                ip,
            })
        }

        return json.Marshal(output)
    })

    m.Get("/sh/where.php", func () ([]byte, error) {
        SOFTWARE := []string{
            "php",
            "node",
            "mysql",
            "vim",
            "python",
            "ruby",
            "java",
            "apache2",
            "nginx",
            "openssl",
            "vsftpd",
            "make",
        }

        var entries [][2]string

        for _, bin := range SOFTWARE {
            path, err := exec.LookPath(bin)

            if err != nil {
                path = "Not Installed"
            }

            entries = append(entries, [2]string{
                bin,
                path,
            })
        }

        return json.Marshal(entries)
    })

    m.Get("/sh/mem.php", func () ([]byte, error) {
        var entries []string

        raw, err := exec.Command("free", "-m").Output()

        if err != nil {
            // OS X fallback
            raw, err := exec.Command("vm_stat").Output()

            if err != nil {
                return nil, err
            }

            vmMap := parseCommandMap(string(raw[:]))

            freePages := toMB(vmMap["Pages free"])
            activePages := toMB(vmMap["Pages active"])
            inactivePages := toMB(vmMap["Pages inactive"])
            speculativePages := toMB(vmMap["Pages speculative"])

            free := freePages + speculativePages
            used := activePages + inactivePages

            return json.Marshal([]string{
                "Mem:",

                // Total
                strconv.Itoa(free + used),

                // Used
                strconv.Itoa(used),

                // Free
                strconv.Itoa(free),
            })
        }

        // Linux
        entries = parseCommandTable(raw, 2, 1)[0][:4]

        return json.Marshal(entries)
    })

    m.Get("/sh/lastlog.php", func () ([]byte, error) {
        raw, err := exec.Command("last").Output()

        if err != nil {
            return nil, err
        }

        entries := parseCommandTable(raw, 0, 3)

        var data [][]string

        for _, entry := range entries {
            var row []string

            // Non user action
            if entry[1] == "~" {
                continue
            }

            if runtime.GOOS == "linux" {
                row = []string{
                    // Username
                    entry[0],
                    entry[2],
                    strings.Join(entry[3:6], " "),
                }
            } else if runtime.GOOS == "darwin" {
                row = []string{
                    // Username
                    entry[0],
                    // From
                    "",
                    strings.Join(entry[2:6], " "),
                }
            }

            data = append(data, row)
        }

        return json.Marshal(data)
    })

    m.Get("/sh/swap.php", func () ([]byte, error) {
        // Linux
        data, err := ioutil.ReadFile("/proc/swaps")

        if err != nil {
            // OS X fallback
            stats, err := topStats()

            // Failed everything
            if err != nil {
                return nil, err
            }

            fields := strings.Fields(stats["Swap"])

            used, _ := strconv.ParseInt(fields[0][:len(fields[0])-1], 10, 32)
            free, _ := strconv.ParseInt(fields[2][:len(fields[2])-1], 10, 32)

            usedB := mbToB(int(used))
            freeB := mbToB(int(free))

            return json.Marshal([][]string{
                {
                    "/var/vm/swapfile0",
                    "file",
                    readableSize(usedB + freeB),
                    readableSize(usedB),
                    "-1",
                },
            })
        }

        return json.Marshal(parseCommandTable(data, 1, 0))
    })

    m.Get("/sh/speed.php", func () ([]byte, error) {
        speed, err := downloadSpeed("http://cachefly.cachefly.net/10mb.test")

        if err != nil {
            return nil, err
        }

        data := make(map[string]float64)

        data["downstream"] = speed
        data["upstream"] = 0.0

        return json.Marshal(data)
    })

    m.Get("/sh/uptime.php", func () ([]byte, error) {
        uptime, err := probe.Uptime()

        if err != nil {
            return nil, err
        }

        return json.Marshal(formatUptime(uptime))
    })

    // Serve static files
    m.Get("/.*", martini.Static(""))

    m.Run()
}

func parseCommandTable(rawOutput []byte, headerCount int, footerCount int) [][]string {
    // Convert output to a string (it's not binary data, so this is ok)
    output := string(rawOutput[:])

    // We'll add all the parsed lines here
    var entries [][]string

    // Lines of output
    lines := strings.Split(output, "\n")

    // Skip first and last line of output
    for _, str := range lines[headerCount:len(lines)-footerCount] {
        entries = append(entries, strings.Fields(str))
    }

    return entries
}

func parseCommandMap(output string) map[string]string {
    data := make(map[string]string)

    // Lines of output
    lines := strings.Split(output, "\n")

    for _, line := range lines {
        parts := strings.Split(line, ":")

        // Bad line, skip it
        if len(parts) != 2 {
            continue
        }

        key := strings.TrimSpace(parts[0])
        value := strings.TrimSpace(parts[1])

        // Remove trailing dot if exists
        if value[len(value) - 1] == '.' {
            value = value[:len(value) - 1]
        }

        data[key] = value
    }

    return data
}

func formatUptime(uptime int64) string {
    return time.Duration(uptime * 1000000000).String()
}

// Get average pingTime to a host
func pingHost(hostname string, pingCount int) (string, error) {
    raw, err := exec.Command("ping", "-c", strconv.Itoa(pingCount), hostname).Output()

    if err != nil {
        return "", err
    }

    lines := strings.Split(string(raw[:]), "\n")

    if len(lines) < 2 {
        return "", errors.New("Bad output for ping command")
    }

    // Get 2nd last line
    lastLine := lines[len(lines)-2]

    // Extract average ping time as a string
    pingTime := strings.Split(strings.Split(lastLine, "=")[1], "/")[1]

    return pingTime, nil
}

func toMB(x string) int {
    num, _ := strconv.ParseInt(x, 10, 32)

    // 256 = 4096 / (1024 * 1024)
    return int(num) / 256
}

func mbToB(x int) int {
    return x * 1024 * 1024
}

// output frstring "top" command on OS X
func topStats() (map[string]string, error) {
    raw, err := exec.Command("top", "-S", "-l", "1", "-n", "0").Output()

    if err != nil {
        return nil, err
    }

    return parseCommandMap(string(raw[:])), nil
}

func readableSize(x int) string {
    KB := 1024
    MB := KB * KB
    GB := KB * KB * KB

    // Bytes
    if x < KB {
        return strconv.Itoa(x) + "b"
    } else if x < MB {
        return strconv.Itoa(x/KB) + "kb"
    } else if x < GB {
        return strconv.Itoa(x/MB) + "mb"
    }
    // else
    return strconv.Itoa(x/GB) + "gb"
}

// Download speed in kb/s
func downloadSpeed(url string) (float64, error) {
    resp, err := http.Get(url)

    if err != nil {
        return 0, err
    }

    total := 0

    // Read by 4k chunks
    buffer := make([]byte, 4096)

    // Start
    t1 := time.Now().UnixNano()

    for {
        buffer = buffer[:cap(buffer)]
        n, err := resp.Body.Read(buffer)

        total += n

        if err != nil {
            // EOF, that's good, now exit
            if err == io.EOF {
                break
            }

            return 0.0, err
        }

    }

    t2 := time.Now().UnixNano()

    // Time in seconds
    duration := float64(t2 - t1) / 1000000000.0

    // Actual download speed in kb/s
    speed := float64(total) / duration

    log.Println("Total :", total)
    log.Println("Duration :", duration)

    return speed, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

var (
	pkgname    string
	output     string
	nocompress bool

	N = 0

	files = map[string]File{}
	file  *os.File
)

type File struct {
	Func  string
	Size  int64
	CSize int64
	Time  time.Time
}

func main() {
	flag.StringVar(&pkgname, "pkgname", "main", "package name")
	flag.StringVar(&output, "o", "assets", "output file name")
	flag.BoolVar(&nocompress, "nc", false, "don't add compressed version")
	flag.Parse()

	createDataFile()
	createFuncFile()
}

func createDataFile() {
	name := output + "data.go"
	file, _ = os.Create(name)

	defer file.Close()

	fmt.Fprintln(file, "package "+pkgname)
	for _, v := range flag.Args() {
		filepath.Walk(v, walkpath)
	}
	exec.Command("gofmt", "-w", name).Output()
}

func createFuncFile() {
	name := output + ".go"
	file, _ = os.Create(name)

	defer file.Close()
	fmt.Fprintln(file, "package "+pkgname)
	fmt.Fprint(file, `

    import (
      "fmt"
      "mime"
      "net/http"
      "path"
      "strings"
      "time"
    )

    type assetFile struct {
      // contains plain data
      pdata func() []byte
      // contains compressed data
      cdata func() []byte
      // size of plain data
      psize uint64
      // size of compressed data
      csize uint64
      // File ModTime
      time  int64
    }

    func (e *assetFile) ModTime() time.Time {
      return time.Unix(e.time, 0)
    }

    // Size returns size of file data
    func (e *assetFile) Size(comp bool) string {
      if comp {
        return fmt.Sprint(e.csize)
      }
      return fmt.Sprint(e.psize)
    }

    // Comp returns true if file has compressed version
    func (e *assetFile) Comp() bool {
      return e.csize > 0
    }

    type assetFS map[string]*assetFile

    // Open returns asset file or error if not found
    func (fs assetFS) Open(name string) (*assetFile, error) {
      i, ok := fs[name]
      if !ok {
        return nil, fmt.Errorf("%s not found", name)
      }

      return i, nil
    }

    // Asset returns asset file plain data or error if not found
    func Asset(name string) ([]byte, error) {
      f, err := _bindata.Open(name)
      if err != nil {
        return nil, err
      }
      return f.pdata(), nil
    }

    // AssetZip returns asset file compressed data or error if not found
    func AssetZip(name string) ([]byte, error) {
      f, err := _bindata.Open(name)
      if err != nil {
        return nil, err
      }
      return f.cdata(), nil
    }

    // serveAssets returns handler function for assets in given directory
    func serveAssets(prefix string) func(w http.ResponseWriter, r *http.Request) {
      return func(w http.ResponseWriter, r *http.Request) {
        name := prefix + r.URL.Path

        f, err := _bindata.Open(name)

        if err != nil {
          http.NotFound(w, r)
          return
        }

        modtime := f.ModTime()

        // return if file not modified
        if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
          w.WriteHeader(http.StatusNotModified)
          return
        }

        mime := mime.TypeByExtension(path.Ext(name))

        w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
        w.Header().Set("Content-Type", mime)

        if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && f.Comp() {
          // send compressed content if applicable
          w.Header().Set("Content-Encoding", "gzip")
          w.Header().Set("Content-Length", f.Size(true))
          w.WriteHeader(200)
          w.Write(f.cdata())
        } else {
          w.Header().Set("Content-Length", f.Size(false))
          w.WriteHeader(200)
          w.Write(f.pdata())
        }
      }
    }
  `)
	fmt.Fprintln(file, "var _bindata = assetFS{")

	keys := make([]string, 0, len(files))
	for k := range files {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		v := files[k]
		fmt.Fprintf(file, "\"%s\": &assetFile{_%s, _c%s, %d, %d, %v},\n", k, v.Func, v.Func, v.Size, v.CSize, v.Time.Unix())
	}
	fmt.Fprintln(file, "}")
	exec.Command("gofmt", "-w", name).Output()
}

func walkpath(fpath string, f os.FileInfo, err error) error {
	if f.IsDir() {
		return nil
	}

	if strings.HasPrefix(f.Name(), ".") {
		return nil
	}

	fb, err := ioutil.ReadFile(fpath)

	if err != nil {
		return err
	}

	addFile(f, fpath, fb)

	return err
}

func addFile(f os.FileInfo, path string, fb []byte) (err error) {
	N++

	varN := fmt.Sprintf("bf%d", N)

	var csize int64
	var cb []byte

	if nocompress {
		cb = []byte{}
		csize = f.Size()
	} else {
		cb = compressed(fb)
		csize = int64(len(cb))
	}

	_, err = fmt.Fprintf(file, "var __%s = []byte(%s)\n\n", varN, convert(fb))
	_, err = fmt.Fprintf(file, "func _%s() []byte {\n return __%s\n }\n\n", varN, varN)

	if f.Size() > csize {
		_, err = fmt.Fprintf(file, "var __c%s = []byte(%s)\n\n", varN, convert(cb))
		_, err = fmt.Fprintf(file, "func _c%s() []byte {\n return __c%s\n }\n\n", varN, varN)
	} else {
		_, err = fmt.Fprintf(file, "func _c%s() []byte {\n return __%s\n }\n\n", varN, varN)
		csize = 0
	}

	files[path] = File{varN, f.Size(), csize, f.ModTime()}

	return
}

func compressed(src []byte) []byte {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	gz.Write(src)
	gz.Close()
	return b.Bytes()
}

func convert(b []byte) string {
	return fmt.Sprintf("%q", string(b))
}
<commit_msg>update<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

var (
	pkgname    string
	output     string
	nocompress bool

	N = 0

	files = map[string]File{}
	file  *os.File
)

type File struct {
	Func  string
	Size  int64
	CSize int64
	Time  time.Time
}

func main() {
	flag.StringVar(&pkgname, "pkgname", "main", "package name")
	flag.StringVar(&output, "o", "assets", "output file name")
	flag.BoolVar(&nocompress, "nc", false, "don't add compressed version")
	flag.Parse()

	createDataFile()
	createFuncFile()
}

func createDataFile() {
	name := output + "data.go"
	file, _ = os.Create(name)

	defer file.Close()

	fmt.Fprintln(file, "package "+pkgname)
	for _, v := range flag.Args() {
		filepath.Walk(v, walkpath)
	}
	exec.Command("gofmt", "-w", name).Output()
}

func createFuncFile() {
	name := output + ".go"
	file, _ = os.Create(name)

	defer file.Close()
	fmt.Fprintln(file, "package "+pkgname)
	fmt.Fprint(file, `

    import (
      "fmt"
      "mime"
      "net/http"
      "path"
      "strings"
      "time"
    )

    type File struct {
      // contains plain data
      pdata func() []byte
      // contains compressed data
      cdata func() []byte
      // size of plain data
      psize uint64
      // size of compressed data
      csize uint64
      // File ModTime
      time  int64
    }

    func (e *File) ModTime() time.Time {
      return time.Unix(e.time, 0)
    }

    // Size returns size of file data
    func (e *File) Size(comp bool) string {
      if comp {
        return fmt.Sprint(e.csize)
      }
      return fmt.Sprint(e.psize)
    }

    // Comp returns true if file has compressed version
    func (e *File) Comp() bool {
      return e.csize > 0
    }

    type FS map[string]*File

    // Open returns asset file or error if not found
    func (fs FS) Open(name string) (*File, error) {
      i, ok := fs[name]
      if !ok {
        return nil, fmt.Errorf("%s not found", name)
      }

      return i, nil
    }

    // Plain returns asset file plain data or error if not found
    func Plain(name string) ([]byte, error) {
      f, err := Data.Open(name)
      if err != nil {
        return nil, err
      }
      return f.pdata(), nil
    }

    // Zip returns asset file compressed data or error if not found
    func Zip(name string) ([]byte, error) {
      f, err := Data.Open(name)
      if err != nil {
        return nil, err
      }
      return f.cdata(), nil
    }

    // Serve returns handler function for assets in given directory
    func Serve(prefix string) func(w http.ResponseWriter, r *http.Request) {
      return func(w http.ResponseWriter, r *http.Request) {
        name := prefix + r.URL.Path

        f, err := Data.Open(name)

        if err != nil {
          http.NotFound(w, r)
          return
        }

        modtime := f.ModTime()

        // return if file not modified
        if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
          w.WriteHeader(http.StatusNotModified)
          return
        }

        mime := mime.TypeByExtension(path.Ext(name))

        w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
        w.Header().Set("Content-Type", mime)

        if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && f.Comp() {
          // send compressed content if applicable
          w.Header().Set("Content-Encoding", "gzip")
          w.Header().Set("Content-Length", f.Size(true))
          w.WriteHeader(200)
          w.Write(f.cdata())
        } else {
          w.Header().Set("Content-Length", f.Size(false))
          w.WriteHeader(200)
          w.Write(f.pdata())
        }
      }
    }
  `)
	fmt.Fprintln(file, "var Data = FS{")

	keys := make([]string, 0, len(files))
	for k := range files {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		v := files[k]
		fmt.Fprintf(file, "\"%s\": &File{_%s, _c%s, %d, %d, %v},\n", k, v.Func, v.Func, v.Size, v.CSize, v.Time.Unix())
	}
	fmt.Fprintln(file, "}")
	exec.Command("gofmt", "-w", name).Output()
}

func walkpath(fpath string, f os.FileInfo, err error) error {
	if f.IsDir() {
		return nil
	}

	if strings.HasPrefix(f.Name(), ".") {
		return nil
	}

	fb, err := ioutil.ReadFile(fpath)

	if err != nil {
		return err
	}

	addFile(f, fpath, fb)

	return err
}

func addFile(f os.FileInfo, path string, fb []byte) (err error) {
	N++

	varN := fmt.Sprintf("bf%d", N)

	var csize int64
	var cb []byte

	if nocompress {
		cb = []byte{}
		csize = f.Size()
	} else {
		cb = compressed(fb)
		csize = int64(len(cb))
	}

	_, err = fmt.Fprintf(file, "var __%s = []byte(%s)\n\n", varN, convert(fb))
	_, err = fmt.Fprintf(file, "func _%s() []byte {\n return __%s\n }\n\n", varN, varN)

	if f.Size() > csize {
		_, err = fmt.Fprintf(file, "var __c%s = []byte(%s)\n\n", varN, convert(cb))
		_, err = fmt.Fprintf(file, "func _c%s() []byte {\n return __c%s\n }\n\n", varN, varN)
	} else {
		_, err = fmt.Fprintf(file, "func _c%s() []byte {\n return __%s\n }\n\n", varN, varN)
		csize = 0
	}

	files[path] = File{varN, f.Size(), csize, f.ModTime()}

	return
}

func compressed(src []byte) []byte {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	gz.Write(src)
	gz.Close()
	return b.Bytes()
}

func convert(b []byte) string {
	return fmt.Sprintf("%q", string(b))
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  // "time"
  "github.com/kr/pty"
  // "github.com/buildboxhq/buildbox-agent/buildbox"
  "os/exec"
  "path/filepath"
  "log"
)

const CMD_IOBUF_LEN = 512

func main() {
  // b := buildbox.GetNextBuild()
  // if b == nil {
    // fmt.Println("No build")
  // }

  // time.Sleep(5000 * time.Millisecond)

  absolutePath, _ := filepath.Abs("test/script.sh")
  c := exec.Command(absolutePath)
  c.Dir = "test"
  c.Env = []string{"BUILDBOX_COMIMT=1"}

  fmt.Println("before")

  pty, err := pty.Start(c)
  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("%s", pty)

  err = c.Wait()
  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("%s", c.ProcessState)
}
<commit_msg>Something seems to be running.<commit_after>package main

import (
  "fmt"
  // "time"
  "github.com/kr/pty"
  // "github.com/buildboxhq/buildbox-agent/buildbox"
  "os/exec"
  "path/filepath"
  "log"
  "io"
  "syscall"
)

const CMD_IOBUF_LEN = 512

func main() {
  // b := buildbox.GetNextBuild()
  // if b == nil {
    // fmt.Println("No build")
  // }

  // time.Sleep(5000 * time.Millisecond)

  absolutePath, _ := filepath.Abs("test/script.sh")
  c := exec.Command(absolutePath)
  c.Dir = "test"
  c.Env = []string{"BUILDBOX_COMIMT=1"}
  c.Args[0] = "script.sh"
  c.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}

  fmt.Println("before")

  output := new(io.Writer)

  ptyMaster, ptySlave, err := pty.Open()
  if err != nil {
    log.Fatal(err)
  }

  c.Stdout = ptySlave
  c.Stderr = ptySlave

	go func() {
		// defer output.Close()
		log.Printf("startPty: begin of stdout pipe")
		io.Copy(*output, ptyMaster)
		log.Printf("startPty: end of stdout pipe")
	}()

  err = c.Start()
  if err != nil {
    log.Fatal(err)
  }

  err = c.Wait()
  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("%s", c.ProcessState)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	log "github.com/Sirupsen/logrus"
	"github.com/pemcconnell/amald/config"
	"github.com/pemcconnell/amald/defs"
	"github.com/pemcconnell/amald/loaders"
	"github.com/pemcconnell/amald/notifiers"
	"github.com/pemcconnell/amald/storage"
	"github.com/pemcconnell/amald/urltest"
)

const (
	VERSION string = "0.0.4"
)

func main() {
	// capture flags
	configPath := flag.String("configPath", "./config.yaml",
		"[config] set the path for the yaml config file. This defaults to "+
			"./config.yaml")
	logLevel := flag.String("logLevel", "info",
		"[loglevel] set the verbosity of the log levels. Can be: debug, "+
			"info, warn, error, panic, fatal")

	// Set logrus level
	if level, err := log.ParseLevel(*logLevel); err == nil {
		log.SetLevel(level)
	}
	// parse flags
	flag.Parse()

	// load the config
	cfg, err := config.Load(*configPath)
	if err != nil {
		log.Fatalf("Failed to load the config from %s", *configPath)
	}

	// collect list of URLs from all possible loaders
	loaders.GetLoaders(cfg.Loaders)
	urls, err := loaders.CollectUrls()
	if err != nil {
		log.Fatalf("Failed to CollectUrls(): %s", err)
	}

	// test all of the urls
	scanResults, err := urltest.Batch(urls)
	if err != nil {
		log.Fatalf("Failed to Batch urltest: %s", err)
	}
	// if there aren't any urls found, don't continue
	if len(scanResults) == 0 {
		log.Fatal("No URLs found in loaders")
	}

	records := defs.SiteDefinitionsToRecords(scanResults)

	// grab a summary (compare current scan against old data)
	if cfg.Tests["storage"] {
		olddata, err := storage.LoadSiteDefsFromStorage(cfg.Storage["json"]["path"])
		if err != nil {
			log.Fatalf("Failed to get summary: %s", err)
		}
		// store latest test
		records = storage.MergeData(scanResults, olddata)
		storage.StoreScan(cfg.Storage["json"]["path"], records)
	}

	// run an analysis on the results, that we can use in reports
	summaries := defs.AnalyseRecords(cfg, records)

	// fire off each notifier
	notifiers.FireNotifiers(cfg, summaries, scanResults)
}
<commit_msg>Added comment to exported const VERSION<commit_after>package main

import (
	"flag"
	log "github.com/Sirupsen/logrus"
	"github.com/pemcconnell/amald/config"
	"github.com/pemcconnell/amald/defs"
	"github.com/pemcconnell/amald/loaders"
	"github.com/pemcconnell/amald/notifiers"
	"github.com/pemcconnell/amald/storage"
	"github.com/pemcconnell/amald/urltest"
)

// VERSION defines amalds application version
const VERSION string = "0.0.4"

func main() {
	// capture flags
	configPath := flag.String("configPath", "./config.yaml",
		"[config] set the path for the yaml config file. This defaults to "+
			"./config.yaml")
	logLevel := flag.String("logLevel", "info",
		"[loglevel] set the verbosity of the log levels. Can be: debug, "+
			"info, warn, error, panic, fatal")

	// Set logrus level
	if level, err := log.ParseLevel(*logLevel); err == nil {
		log.SetLevel(level)
	}
	// parse flags
	flag.Parse()

	// load the config
	cfg, err := config.Load(*configPath)
	if err != nil {
		log.Fatalf("Failed to load the config from %s", *configPath)
	}

	// collect list of URLs from all possible loaders
	loaders.GetLoaders(cfg.Loaders)
	urls, err := loaders.CollectUrls()
	if err != nil {
		log.Fatalf("Failed to CollectUrls(): %s", err)
	}

	// test all of the urls
	scanResults, err := urltest.Batch(urls)
	if err != nil {
		log.Fatalf("Failed to Batch urltest: %s", err)
	}
	// if there aren't any urls found, don't continue
	if len(scanResults) == 0 {
		log.Fatal("No URLs found in loaders")
	}

	records := defs.SiteDefinitionsToRecords(scanResults)

	// grab a summary (compare current scan against old data)
	if cfg.Tests["storage"] {
		olddata, err := storage.LoadSiteDefsFromStorage(cfg.Storage["json"]["path"])
		if err != nil {
			log.Fatalf("Failed to get summary: %s", err)
		}
		// store latest test
		records = storage.MergeData(scanResults, olddata)
		storage.StoreScan(cfg.Storage["json"]["path"], records)
	}

	// run an analysis on the results, that we can use in reports
	summaries := defs.AnalyseRecords(cfg, records)

	// fire off each notifier
	notifiers.FireNotifiers(cfg, summaries, scanResults)
}
<|endoftext|>


<commit_before>// +build go1.8

package main

import (
	"flag"
	"log"
	"strconv"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/kataras/iris"
	irisctx "github.com/kataras/iris/context"
	_ "github.com/mattn/go-sqlite3"
)

var db *sqlx.DB

func main() {
	log.Println("starting seagull")
	var err error
	var reBuildDB = flag.Bool("rebuild", false, "rebuild database")
	flag.Parse()

	if *reBuildDB {
		if err := buildDatabase(); err != nil {
			log.Fatalf("build database error: ", err)
		}
		if err := buildNetworkExtents(); err != nil {
			log.Fatalf("build database extents: %v", err)
		}
	}

	if db, err = sqlx.Open("sqlite3", "bsn.db"); err != nil {
		log.Fatalf("database error: ", err)
	}

	srv := newSrv()

	if err := srv.Run(iris.Addr(":9090"), iris.WithoutVersionChecker); err != nil {
		log.Fatalf("failed to start http server: %v\n", err)
	}
}

func newSrv() *iris.Application {
	srv := iris.New()
	srv.Use()
	srv.StaticWeb("/", "www/")
	srv.Get("/api/network/{id:string}", getDetail)
	srv.Get("/api/network", getNetworkList)
	srv.Get("/api/review/{id:int}", getReview)
	srv.Put("/api/review/{id:int}", editReview)
	srv.Get("/api/station/{id:int}", getStation)
	srv.Post("/api/station/{id:int}", updateStation)
	srv.Post("/api/station/{id:int}/review", reviewStation)
	return srv
}

func getStation(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getStation")
	idStr := ctx.Params().Get("id")

	if idStr == "" {
		ctx.NotFound()
		return
	}
	id, err := strconv.Atoi(idStr)
	if err != nil {
		ctx.NotFound()
		return
	}
	var stations Station
	db.Get(&stations, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where StationID=$1", id)

	if stations.StationID == 0 {
		ctx.NotFound()
		return
	}
	var reviews = []Review{}
	db.Select(&reviews, "SELECT ReviewID, Body, Rating, TimeStamp FROM reviews where StationID=$1", stations.StationID)
	if len(reviews) != 0 {
		stations.Reviews = append(stations.Reviews, reviews...)
	}

	ctx.Gzip(true)
	ctx.JSON(stations)

}

func getReview(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getReview")
	idStr := ctx.Params().Get("id")

	if idStr == "" {
		ctx.NotFound()
		return
	}
	id, err := strconv.Atoi(idStr)
	if err != nil {
		ctx.NotFound()
		return
	}
	var review Review
	err = db.Get(&review, "SELECT ReviewID, StationID, TimeStamp, Body, Rating, FROM reviews where ReviewID=$1", id)
	if err != nil {
		log.Println(err)
		ctx.NotFound()
		return
	}

	ctx.Gzip(true)
	ctx.JSON(review)

}

func editReview(ctx irisctx.Context) {
	defer timeLog(time.Now(), "editReview")
	var review Review
	if err := ctx.ReadJSON(&review); err != nil {
		log.Printf("error parsing json: %v\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	if len(review.Body) > 250 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("body greater than 250 characters")
		return
	}
	var existing Review
	if err := db.Get(&existing, "Select ReviewID from reviews WHERE ReviewID=$1", review.ReviewID); err != nil {
		log.Printf("review id: %d does not exist: %v\n", review.ReviewID, err)
		ctx.StatusCode(iris.StatusNotFound)
		ctx.WriteString("review does not exist")
		return
	}
	review.TimeStamp = time.Now().UTC()
	tx := db.MustBegin()
	tx.MustExec("UPDATE reviews SET Body=$1, Rating=$2, TimeStamp=$3 WHERE ReviewID=$4",
		review.Body,
		review.Rating,
		review.TimeStamp,
		review.ReviewID)
	if err := tx.Commit(); err != nil {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	var updated Review
	if err := db.Get(&updated, "Select ReviewID, StationID, Body, Rating, TimeStamp from reviews WHERE ReviewID=$1", review.ReviewID); err != nil {
		log.Printf("review id: %d does not exist: %v\n", review.ReviewID, err)
		ctx.StatusCode(iris.StatusNotFound)
		ctx.WriteString("review does not exist")
		return
	}
	ctx.Gzip(true)
	ctx.JSON(updated)
}

func updateStation(ctx irisctx.Context) {

	defer timeLog(time.Now(), "updateStation")
	log.Printf("%+#v\n", ctx)
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("bad station id " + id + " in request url")
		return
	}

	var s Station
	if err := ctx.ReadJSON(&s); err != nil {
		log.Printf("\n\nerror parsing json: %v\n\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	if s.StationID == 0 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("station id not present in request body")
		return
	}

	log.Printf("\n\n%+#v\n\n", s)

	u, err := updateStationDB(s)
	if err != nil {
		log.Printf("error updating staion: %v\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("invalid station id")
		return
	}

	ctx.Gzip(true)
	ctx.JSON(u)

}

func updateStationDB(update Station) (Station, error) {
	var existing Station
	err := db.Get(&existing, "SELECT StationID, EmptySlots, FreeBikes, Safe FROM stations WHERE StationID=$1", update.StationID)
	if err != nil {
		return Station{}, err
	}
	tx := db.MustBegin()
	tx.MustExec("UPDATE stations SET FreeBikes=$1 WHERE StationID=$2", update.FreeBikes, existing.StationID)
	tx.MustExec("UPDATE stations SET EmptySlots=$1 WHERE StationID=$2", update.EmptySlots, existing.StationID)
	tx.MustExec("UPDATE stations SET Safe=$1 WHERE StationID=$2", update.Safe, existing.StationID)
	tx.MustExec("UPDATE stations SET Open=$1 WHERE StationID=$2", update.Open, existing.StationID)
	tx.MustExec("UPDATE stations SET TimeStamp=$1 WHERE StationID=$2", time.Now().UTC(), existing.StationID)
	if err := tx.Commit(); err != nil {
		return existing, err
	}

	var updated Station
	err = db.Get(&updated, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where StationID=$1", existing.StationID)
	if err != nil {
		return existing, err
	}

	var reviews = []Review{}
	err = db.Select(&reviews, "SELECT Body, Rating, TimeStamp FROM reviews where StationID=$1", updated.StationID)
	if err != nil {
		return existing, err
	}
	if len(reviews) != 0 {
		updated.Reviews = append(updated.Reviews, reviews...)
	}

	return updated, nil

}

func reviewStation(ctx irisctx.Context) {
	defer timeLog(time.Now(), "reviewStation")
	log.Printf("%+#v\n", ctx)
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.Err()
		return
	}
	var review Review
	err := ctx.ReadForm(&review)
	if err != nil {
		log.Println(err)
		ctx.Err()
		return
	}
	review.TimeStamp = time.Now().UTC()
	var station Station
	db.Get(&station, "SELECT StationID FROM stations WHERE StationID=$1", id)
	review.StationID = station.StationID
	tx := db.MustBegin()
	tx.MustExec("INSERT INTO reviews (StationID,TimeStamp,Body,Rating) VALUES ($1, $2, $3, $4)",
		review.StationID,
		review.TimeStamp,
		review.Body,
		review.Rating)
	tx.Commit()
	return
}

func getDetail(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getDetail")
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.NotFound()
		return
	}
	var net Network
	db.Get(&net, "SELECT NetworkID, Company, Name, City, Country, Latitude, Longitude, HSpan, VSpan, CenterLat, CenterLng FROM networks WHERE NetworkID=$1", id)
	if net.NetworkID == 0 {
		log.Printf("network %v does not exist", id)
		ctx.NotFound()
		return
	}
	var stations = []Station{}
	db.Select(&stations, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where NetworkID=$1", id)
	if len(stations) == 0 {
		log.Printf("no stations in network %v", id)
	}
	net.Stations = append(net.Stations, stations...)

	ctx.Gzip(true)
	ctx.JSON(net)
	return
}

func getNetworkList(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getNetworkList")

	var NetworkList []Network
	db.Select(&NetworkList, "SELECT Company, NetworkID, Name, City, Country, Latitude, Longitude, HSpan, VSpan, CenterLat, CenterLng FROM networks")
	if len(NetworkList) == 0 {
		ctx.NotFound()
		return
	}
	log.Println(len(NetworkList))

	ctx.Gzip(true)
	ctx.JSON(NetworkList)
	return

}

func timeLog(start time.Time, name string) {
	taken := time.Since(start)
	log.Printf("%s took %s", name, taken)
}
<commit_msg>change /api/station/{id}/review to take json.<commit_after>// +build go1.8

package main

import (
	"flag"
	"log"
	"strconv"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/kataras/iris"
	irisctx "github.com/kataras/iris/context"
	_ "github.com/mattn/go-sqlite3"
)

var db *sqlx.DB

func main() {
	log.Println("starting seagull")
	var err error
	var reBuildDB = flag.Bool("rebuild", false, "rebuild database")
	flag.Parse()

	if *reBuildDB {
		if err := buildDatabase(); err != nil {
			log.Fatalf("build database error: ", err)
		}
		if err := buildNetworkExtents(); err != nil {
			log.Fatalf("build database extents: %v", err)
		}
	}

	if db, err = sqlx.Open("sqlite3", "bsn.db"); err != nil {
		log.Fatalf("database error: ", err)
	}

	srv := newSrv()

	if err := srv.Run(iris.Addr(":9090"), iris.WithoutVersionChecker); err != nil {
		log.Fatalf("failed to start http server: %v\n", err)
	}
}

func newSrv() *iris.Application {
	srv := iris.New()
	srv.Use()
	srv.StaticWeb("/", "www/")
	srv.Get("/api/network/{id:string}", getDetail)
	srv.Get("/api/network", getNetworkList)
	srv.Get("/api/review/{id:int}", getReview)
	srv.Put("/api/review/{id:int}", editReview)
	srv.Get("/api/station/{id:int}", getStation)
	srv.Post("/api/station/{id:int}", updateStation)
	srv.Post("/api/station/{id:int}/review", reviewStation)
	return srv
}

func getStation(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getStation")
	idStr := ctx.Params().Get("id")

	if idStr == "" {
		ctx.NotFound()
		return
	}
	id, err := strconv.Atoi(idStr)
	if err != nil {
		ctx.NotFound()
		return
	}
	var stations Station
	db.Get(&stations, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where StationID=$1", id)

	if stations.StationID == 0 {
		ctx.NotFound()
		return
	}
	var reviews = []Review{}
	db.Select(&reviews, "SELECT ReviewID, Body, Rating, TimeStamp FROM reviews where StationID=$1", stations.StationID)
	if len(reviews) != 0 {
		stations.Reviews = append(stations.Reviews, reviews...)
	}

	ctx.Gzip(true)
	ctx.JSON(stations)

}

func getReview(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getReview")
	idStr := ctx.Params().Get("id")

	if idStr == "" {
		ctx.NotFound()
		return
	}
	id, err := strconv.Atoi(idStr)
	if err != nil {
		ctx.NotFound()
		return
	}
	var review Review
	err = db.Get(&review, "SELECT ReviewID, StationID, TimeStamp, Body, Rating, FROM reviews where ReviewID=$1", id)
	if err != nil {
		log.Println(err)
		ctx.NotFound()
		return
	}

	ctx.Gzip(true)
	ctx.JSON(review)

}

func editReview(ctx irisctx.Context) {
	defer timeLog(time.Now(), "editReview")
	var review Review
	if err := ctx.ReadJSON(&review); err != nil {
		log.Printf("error parsing json: %v\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	if len(review.Body) > 250 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("body greater than 250 characters")
		return
	}
	var existing Review
	if err := db.Get(&existing, "Select ReviewID from reviews WHERE ReviewID=$1", review.ReviewID); err != nil {
		log.Printf("review id: %d does not exist: %v\n", review.ReviewID, err)
		ctx.StatusCode(iris.StatusNotFound)
		ctx.WriteString("review does not exist")
		return
	}
	review.TimeStamp = time.Now().UTC()
	tx := db.MustBegin()
	tx.MustExec("UPDATE reviews SET Body=$1, Rating=$2, TimeStamp=$3 WHERE ReviewID=$4",
		review.Body,
		review.Rating,
		review.TimeStamp,
		review.ReviewID)
	if err := tx.Commit(); err != nil {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	var updated Review
	if err := db.Get(&updated, "Select ReviewID, StationID, Body, Rating, TimeStamp from reviews WHERE ReviewID=$1", review.ReviewID); err != nil {
		log.Printf("review id: %d does not exist: %v\n", review.ReviewID, err)
		ctx.StatusCode(iris.StatusNotFound)
		ctx.WriteString("review does not exist")
		return
	}
	ctx.Gzip(true)
	ctx.JSON(updated)
}

func updateStation(ctx irisctx.Context) {

	defer timeLog(time.Now(), "updateStation")
	log.Printf("%+#v\n", ctx)
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("bad station id " + id + " in request url")
		return
	}

	var s Station
	if err := ctx.ReadJSON(&s); err != nil {
		log.Printf("\n\nerror parsing json: %v\n\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	if s.StationID == 0 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("station id not present in request body")
		return
	}

	log.Printf("\n\n%+#v\n\n", s)

	u, err := updateStationDB(s)
	if err != nil {
		log.Printf("error updating staion: %v\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("invalid station id")
		return
	}

	ctx.Gzip(true)
	ctx.JSON(u)

}

func updateStationDB(update Station) (Station, error) {
	var existing Station
	err := db.Get(&existing, "SELECT StationID, EmptySlots, FreeBikes, Safe FROM stations WHERE StationID=$1", update.StationID)
	if err != nil {
		return Station{}, err
	}
	tx := db.MustBegin()
	tx.MustExec("UPDATE stations SET FreeBikes=$1 WHERE StationID=$2", update.FreeBikes, existing.StationID)
	tx.MustExec("UPDATE stations SET EmptySlots=$1 WHERE StationID=$2", update.EmptySlots, existing.StationID)
	tx.MustExec("UPDATE stations SET Safe=$1 WHERE StationID=$2", update.Safe, existing.StationID)
	tx.MustExec("UPDATE stations SET Open=$1 WHERE StationID=$2", update.Open, existing.StationID)
	tx.MustExec("UPDATE stations SET TimeStamp=$1 WHERE StationID=$2", time.Now().UTC(), existing.StationID)
	if err := tx.Commit(); err != nil {
		return existing, err
	}

	var updated Station
	err = db.Get(&updated, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where StationID=$1", existing.StationID)
	if err != nil {
		return existing, err
	}

	var reviews = []Review{}
	err = db.Select(&reviews, "SELECT Body, Rating, TimeStamp FROM reviews where StationID=$1", updated.StationID)
	if err != nil {
		return existing, err
	}
	if len(reviews) != 0 {
		updated.Reviews = append(updated.Reviews, reviews...)
	}

	return updated, nil

}

func reviewStation(ctx irisctx.Context) {
	defer timeLog(time.Now(), "reviewStation")
	log.Printf("%+#v\n", ctx)
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString("bad id " + id + "in request url")
		return
	}
	var review Review
	err := ctx.ReadJSON(&review)
	if err != nil {
		log.Printf("\n\nerror parsing json: %v\n\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	review.TimeStamp = time.Now().UTC()
	var station Station
	if err := db.Get(&station, "SELECT StationID FROM stations WHERE StationID=$1", id); err != nil {
		log.Printf("\n\nerror checking if station exists : %v\n\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	review.StationID = station.StationID
	tx := db.MustBegin()
	tx.MustExec("INSERT INTO reviews (StationID,TimeStamp,Body,Rating) VALUES ($1, $2, $3, $4)",
		review.StationID,
		review.TimeStamp,
		review.Body,
		review.Rating)
	if err := tx.Commit(); err != nil {
		log.Printf("\n\nerror creating review: %v\n\n", err)
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.WriteString(err.Error())
		return
	}
	ctx.Gzip(true)
	ctx.JSON(review)
	return
}

func getDetail(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getDetail")
	id := ctx.Params().Get("id")
	if id == "" {
		ctx.NotFound()
		return
	}
	var net Network
	db.Get(&net, "SELECT NetworkID, Company, Name, City, Country, Latitude, Longitude, HSpan, VSpan, CenterLat, CenterLng FROM networks WHERE NetworkID=$1", id)
	if net.NetworkID == 0 {
		log.Printf("network %v does not exist", id)
		ctx.NotFound()
		return
	}
	var stations = []Station{}
	db.Select(&stations, "SELECT StationID, Name, Latitude, Longitude, EmptySlots, FreeBikes, Safe, Open, TimeStamp FROM stations where NetworkID=$1", id)
	if len(stations) == 0 {
		log.Printf("no stations in network %v", id)
	}
	net.Stations = append(net.Stations, stations...)

	ctx.Gzip(true)
	ctx.JSON(net)
	return
}

func getNetworkList(ctx irisctx.Context) {
	defer timeLog(time.Now(), "getNetworkList")

	var NetworkList []Network
	db.Select(&NetworkList, "SELECT Company, NetworkID, Name, City, Country, Latitude, Longitude, HSpan, VSpan, CenterLat, CenterLng FROM networks")
	if len(NetworkList) == 0 {
		ctx.NotFound()
		return
	}
	log.Println(len(NetworkList))

	ctx.Gzip(true)
	ctx.JSON(NetworkList)
	return

}

func timeLog(start time.Time, name string) {
	taken := time.Since(start)
	log.Printf("%s took %s", name, taken)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"log"
	"os"
	"strings"
)

var listener *bool = flag.Bool("listen", false, "Start listener")
var help *bool = flag.Bool("help", false, "Show help")
var mailto *string = flag.String("mailto", "", "Who to email on failure (comma separated) [sumbit]")
var workdir *string = flag.String("workdir", "/tmp", "Directory to run job from [submit]")
var out *string = flag.String("out", "/dev/null", "File to send job's stdout and stderr [submit]")
var tube *string = flag.String("tube", "", "Beanstalkd tube to send the job to [submit]")
var stats *bool = flag.Bool("stats", false, "Show queue stats")
var drain *string = flag.String("drain", "", "Empty tubes (comma separated)")
var verbose *bool = flag.Bool("v", false, "Increase verbosity")
var exclude *string = flag.String("exclude", "", "Tubes to ignore (comma separated) [listen]")
var priority *int = flag.Int("pri", 0, "Job priority (smaller runs first) [submit]")
var delay *int = flag.Int("delay", 0, "Job delay in seconds [submit]")
var local *bool = flag.Bool("local", false, "Run locally, reporting errors to the configured beanstalk")

func main() {
	log.SetFlags(0)
	flag.Parse()
	if *listener {
		include := false
		filter := make([]string, 0)
		if *exclude != "" {
			filter = strings.Split(*exclude, ",")
		}
		l, e := NewListener(*verbose, include, filter)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
		l.run()
		return
	} else if *help {
		flag.Usage()
		os.Exit(1)
	}

	if *local {
		msg := make(map[string]string)
		msg["cmd"] = command()
		msg["mailto"] = *mailto
		msg["workdir"] = *workdir
		msg["out"] = *out
		runner, e := NewRunner()
		e = runner.execute(msg)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
		return
	}

	c, e := NewClient(*verbose)
	if e != nil {
		log.Fatalf("ERROR: %s", e.Error())
	}

	if *drain != "" {
		e = c.drain(*drain)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else if *stats {
		e = c.stats()
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else if len(flag.Args()) == 0 { // Queue up many jobs from STDIN
		in := bufio.NewReaderSize(os.Stdin, 1024*1024)
		input := make([]byte, 0)
		for {
			line, e := in.ReadSlice('\n')
			if e != nil {
				if e.Error() == "EOF" {
					break
				}
				log.Fatalf("ERROR: %s", e.Error())
			}
			input = append(input, line...)
		}
		e = c.putMany(input)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else { // Queue up one job
		e = c.put(command(), *mailto, *workdir, *out, *tube, *priority, *delay)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	}
}

func command() string {
	return strings.Join(flag.Args(), " ")
}
<commit_msg>-local mode should fail gracefully when can't connect to beanstalk<commit_after>package main

import (
	"bufio"
	"flag"
	"log"
	"os"
	"strings"
)

var listener *bool = flag.Bool("listen", false, "Start listener")
var help *bool = flag.Bool("help", false, "Show help")
var mailto *string = flag.String("mailto", "", "Who to email on failure (comma separated) [sumbit]")
var workdir *string = flag.String("workdir", "/tmp", "Directory to run job from [submit]")
var out *string = flag.String("out", "/dev/null", "File to send job's stdout and stderr [submit]")
var tube *string = flag.String("tube", "", "Beanstalkd tube to send the job to [submit]")
var stats *bool = flag.Bool("stats", false, "Show queue stats")
var drain *string = flag.String("drain", "", "Empty tubes (comma separated)")
var verbose *bool = flag.Bool("v", false, "Increase verbosity")
var exclude *string = flag.String("exclude", "", "Tubes to ignore (comma separated) [listen]")
var priority *int = flag.Int("pri", 0, "Job priority (smaller runs first) [submit]")
var delay *int = flag.Int("delay", 0, "Job delay in seconds [submit]")
var local *bool = flag.Bool("local", false, "Run locally, reporting errors to the configured beanstalk")

func main() {
	log.SetFlags(0)
	flag.Parse()
	if *listener {
		include := false
		filter := make([]string, 0)
		if *exclude != "" {
			filter = strings.Split(*exclude, ",")
		}
		l, e := NewListener(*verbose, include, filter)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
		l.run()
		return
	} else if *help {
		flag.Usage()
		os.Exit(1)
	}

	if *local {
		msg := make(map[string]string)
		msg["cmd"] = command()
		msg["mailto"] = *mailto
		msg["workdir"] = *workdir
		msg["out"] = *out
		runner, e := NewRunner()
		if e != nil {
			log.Fatal(e)
		}
		e = runner.execute(msg)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
		return
	}

	c, e := NewClient(*verbose)
	if e != nil {
		log.Fatalf("ERROR: %s", e.Error())
	}

	if *drain != "" {
		e = c.drain(*drain)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else if *stats {
		e = c.stats()
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else if len(flag.Args()) == 0 { // Queue up many jobs from STDIN
		in := bufio.NewReaderSize(os.Stdin, 1024*1024)
		input := make([]byte, 0)
		for {
			line, e := in.ReadSlice('\n')
			if e != nil {
				if e.Error() == "EOF" {
					break
				}
				log.Fatalf("ERROR: %s", e.Error())
			}
			input = append(input, line...)
		}
		e = c.putMany(input)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	} else { // Queue up one job
		e = c.put(command(), *mailto, *workdir, *out, *tube, *priority, *delay)
		if e != nil {
			log.Fatalf("ERROR: %s", e.Error())
		}
	}
}

func command() string {
	return strings.Join(flag.Args(), " ")
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Datacratic. All rights reserved.

package main

import (
	"bufio"
	"container/list"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datacratic/gometrics"
)

type Stream struct {
	strings chan string
}

type Clients map[*Stream]*Stream

type Event struct {
	Matched   int
	Unmatched int
	Late      int
	Total     int
}

type Counts struct {
	When       time.Time
	BidRequest int
	Win        Event
	Click      Event
	Conversion Event
}

type Reports struct {
	Counts list.List
	mu     sync.Mutex
}

type Tailer struct {
	Monitor *metric.Monitor
	reports Reports
	clients atomic.Value
	mu      sync.Mutex
}

type TailerMetrics struct {
	Line      bool
	Client    bool
	LinesSent int
	BytesSent int
	Counts    *Counts
}

func main() {
	address := flag.String("address", ":http", "bind address of the HTTP server")
	filename := flag.String("file", "", "file to serve")
	cpu := flag.Int("cpu", 1, "number of CPU to use")
	server := flag.String("server", "localhost", "name of the server where this component runs")
	carbon := flag.String("carbon", "", "comma separated list of TCP address of Carbon daemons")

	flag.Parse()

	if *address == "" {
		log.Fatal("missing '--address' parameter")
	}

	if *filename == "" {
		log.Fatal("missing '--file' parameter")
	}

	cmd := exec.Command("tail", "-f", *filename)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	trailer := new(Tailer)

	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			text := scanner.Text()
			trailer.process(text)
		}

		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	if *carbon != "" {
		urls := strings.Split(*carbon, ",")
		trailer.Monitor = metric.NewCarbonMonitor(*server, urls)
		trailer.Monitor.Name = "trail"
		trailer.Monitor.PublishInterval = 10 * time.Second
		trailer.Monitor.Start()
	}

	log.Printf("%s.trail is using %d CPU(s)\n", *server, *cpu)
	runtime.GOMAXPROCS(*cpu)

	log.Fatal(http.ListenAndServe(*address, trailer))
}

func (trailer *Tailer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/counts" {
		// lock the counts since we're going to read them
		trailer.reports.mu.Lock()
		defer trailer.reports.mu.Unlock()

		counts := &trailer.reports.Counts

		// build the array
		items := make([]*Counts, 0, counts.Len())
		for e := counts.Front(); e != nil; e = e.Next() {
			items = append(items, e.Value.(*Counts))
		}

		data, err := json.Marshal(items)
		if err != nil {
			log.Printf("marshal failed: %s\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
		w.Write(data)
		return
	}

	if r.URL.Path == "/trail" {
		flusher, ok := w.(http.Flusher)
		if !ok {
			log.Panicf("can't support HTTP streaming '%+v'", w)
		}

		closer, ok := w.(http.CloseNotifier)
		if !ok {
			log.Panicf("can't support HTTP streaming '%+v'", w)
		}

		w.Header().Set("Content-Type", "text/plain")

		// attach the stream and detach it when we're done
		stream := trailer.New()
		defer trailer.Close(stream)

		// buffer the writes
		writer := bufio.NewWriter(w)
		lines := 0
		count := 0
		total := 0

		for done := false; !done; {
			select {
			case text := <-stream.strings:
				n, err := writer.WriteString(text + "\n")
				if err != nil {
					log.Println(err)
					done = true
				}

				lines += 1
				count += n
				total += n
			case <-time.After(time.Second):
				if err := writer.Flush(); err != nil {
					log.Println(err)
					done = true
				}

				// push the data over the wire
				flusher.Flush()

				metrics := TailerMetrics{
					Client:    true,
					LinesSent: lines,
					BytesSent: count,
				}

				if trailer.Monitor != nil {
					trailer.Monitor.RecordMetrics("trailer", &metrics)
				}

				lines = 0
				count = 0
			case <-closer.CloseNotify():
				done = true
			}
		}

		log.Printf("client received '%d' bytes\n", total)
		return
	}

	log.Printf("failed to get resouce '%s'\n", r.URL.Path)
	w.WriteHeader(http.StatusNotFound)
}

func (trailer *Tailer) process(text string) {
	metrics := TailerMetrics{
		Line: true,
	}

	now := time.Now().Round(time.Hour)

	// lock the counts since we're going to update them
	trailer.reports.mu.Lock()
	defer trailer.reports.mu.Unlock()

	counts := &trailer.reports.Counts

	front := counts.Front()
	if front == nil || front.Value.(*Counts).When != now {
		front = counts.PushFront(&Counts{
			When: now,
		})
	}

	values := front.Value.(*Counts)

	// parse the text line
	offset := 16
	if len(text) > offset {
		tokens := strings.SplitAfterN(text[16:], " ", 4)
		switch tokens[2] {
		case "AUCTION":
			values.BidRequest++
		case "WIN":
			values.Win.Total++
		case "MATCHEDLOSS":
			values.Win.Late++
		case "MATCHEDWIN":
			values.Win.Matched++
		case "UNMATCHEDWIN":
			values.Win.Unmatched++
		case "CLICK":
			values.Click.Total++
		case "MATCHEDCLICK":
			values.Click.Matched++
		case "UNMATCHEDCLICK":
			values.Click.Unmatched++
		case "CONVERSION":
			values.Conversion.Total++
		case "MATCHEDCONVERSION":
			values.Conversion.Matched++
		case "UNMATCHEDCONVERSION":
			values.Conversion.Unmatched++
		}
	}

	if e := front.Next(); e != nil {
		metrics.Counts = e.Value.(*Counts)
	}

	// only keep the last month of counts
	for counts.Len() > 720 {
		counts.Remove(counts.Back())
	}

	// send text to clients currently attached
	if clients := trailer.clients.Load(); clients != nil {
		for client := range clients.(Clients) {
			client.strings <- text
		}
	}

	if trailer.Monitor != nil {
		trailer.Monitor.RecordMetrics("process", &metrics)
	}
}

func (trailer *Tailer) New() (stream *Stream) {
	stream = &Stream{
		strings: make(chan string),
	}

	trailer.mu.Lock()
	defer trailer.mu.Unlock()

	last := trailer.clients.Load()
	next := make(Clients)
	next[stream] = stream
	if last != nil {
		clients := last.(Clients)
		for item := range clients {
			next[item] = item
		}
	}

	trailer.clients.Store(next)
	return
}

func (trailer *Tailer) Close(stream *Stream) {
	trailer.mu.Lock()
	defer trailer.mu.Unlock()

	clients := trailer.clients.Load().(Clients)
	next := make(Clients)
	for item := range clients {
		if item == stream {
			continue
		}

		next[item] = item
	}

	trailer.clients.Store(next)
}
<commit_msg>added bids to counts<commit_after>// Copyright (c) 2015 Datacratic. All rights reserved.

package main

import (
	"bufio"
	"container/list"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datacratic/gometrics"
)

type Stream struct {
	strings chan string
}

type Clients map[*Stream]*Stream

type Event struct {
	Matched   int
	Unmatched int
	Late      int
	Total     int
}

type Counts struct {
	When       time.Time
	BidRequest int
	Bid        int
	Win        Event
	Click      Event
	Conversion Event
}

type Reports struct {
	Counts list.List
	mu     sync.Mutex
}

type Tailer struct {
	Monitor *metric.Monitor
	reports Reports
	clients atomic.Value
	mu      sync.Mutex
}

type TailerMetrics struct {
	Line      bool
	Client    bool
	LinesSent int
	BytesSent int
	Counts    *Counts
}

func main() {
	address := flag.String("address", ":http", "bind address of the HTTP server")
	filename := flag.String("file", "", "file to serve")
	cpu := flag.Int("cpu", 1, "number of CPU to use")
	server := flag.String("server", "localhost", "name of the server where this component runs")
	carbon := flag.String("carbon", "", "comma separated list of TCP address of Carbon daemons")

	flag.Parse()

	if *address == "" {
		log.Fatal("missing '--address' parameter")
	}

	if *filename == "" {
		log.Fatal("missing '--file' parameter")
	}

	cmd := exec.Command("tail", "-f", *filename)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	trailer := new(Tailer)

	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			text := scanner.Text()
			trailer.process(text)
		}

		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	if *carbon != "" {
		urls := strings.Split(*carbon, ",")
		trailer.Monitor = metric.NewCarbonMonitor(*server, urls)
		trailer.Monitor.Name = "trail"
		trailer.Monitor.PublishInterval = 10 * time.Second
		trailer.Monitor.Start()
	}

	log.Printf("%s.trail is using %d CPU(s)\n", *server, *cpu)
	runtime.GOMAXPROCS(*cpu)

	log.Fatal(http.ListenAndServe(*address, trailer))
}

func (trailer *Tailer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/counts" {
		// lock the counts since we're going to read them
		trailer.reports.mu.Lock()
		defer trailer.reports.mu.Unlock()

		counts := &trailer.reports.Counts

		// build the array
		items := make([]*Counts, 0, counts.Len())
		for e := counts.Front(); e != nil; e = e.Next() {
			items = append(items, e.Value.(*Counts))
		}

		data, err := json.Marshal(items)
		if err != nil {
			log.Printf("marshal failed: %s\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
		w.Write(data)
		return
	}

	if r.URL.Path == "/trail" {
		flusher, ok := w.(http.Flusher)
		if !ok {
			log.Panicf("can't support HTTP streaming '%+v'", w)
		}

		closer, ok := w.(http.CloseNotifier)
		if !ok {
			log.Panicf("can't support HTTP streaming '%+v'", w)
		}

		w.Header().Set("Content-Type", "text/plain")

		// attach the stream and detach it when we're done
		stream := trailer.New()
		defer trailer.Close(stream)

		// buffer the writes
		writer := bufio.NewWriter(w)
		lines := 0
		count := 0
		total := 0

		for done := false; !done; {
			select {
			case text := <-stream.strings:
				n, err := writer.WriteString(text + "\n")
				if err != nil {
					log.Println(err)
					done = true
				}

				lines += 1
				count += n
				total += n
			case <-time.After(time.Second):
				if err := writer.Flush(); err != nil {
					log.Println(err)
					done = true
				}

				// push the data over the wire
				flusher.Flush()

				metrics := TailerMetrics{
					Client:    true,
					LinesSent: lines,
					BytesSent: count,
				}

				if trailer.Monitor != nil {
					trailer.Monitor.RecordMetrics("trailer", &metrics)
				}

				lines = 0
				count = 0
			case <-closer.CloseNotify():
				done = true
			}
		}

		log.Printf("client received '%d' bytes\n", total)
		return
	}

	log.Printf("failed to get resouce '%s'\n", r.URL.Path)
	w.WriteHeader(http.StatusNotFound)
}

func (trailer *Tailer) process(text string) {
	metrics := TailerMetrics{
		Line: true,
	}

	now := time.Now().Round(time.Hour)

	// lock the counts since we're going to update them
	trailer.reports.mu.Lock()
	defer trailer.reports.mu.Unlock()

	counts := &trailer.reports.Counts

	front := counts.Front()
	if front == nil || front.Value.(*Counts).When != now {
		front = counts.PushFront(&Counts{
			When: now,
		})
	}

	values := front.Value.(*Counts)

	// parse the text line
	tokens := strings.Split(text, " ")
	switch tokens[0] {
	case "AUCTION":
		values.BidRequest++
	case "SUBMITTED":
		values.Bid++
	case "WIN":
		values.Win.Total++
	case "MATCHEDLOSS":
		values.Win.Late++
	case "MATCHEDWIN":
		values.Win.Matched++
	case "UNMATCHEDWIN":
		values.Win.Unmatched++
	case "CLICK":
		values.Click.Total++
	case "MATCHEDCLICK":
		values.Click.Matched++
	case "UNMATCHEDCLICK":
		values.Click.Unmatched++
	case "CONVERSION":
		values.Conversion.Total++
	case "MATCHEDCONVERSION":
		values.Conversion.Matched++
	case "UNMATCHEDCONVERSION":
		values.Conversion.Unmatched++
	}

	if e := front.Next(); e != nil {
		metrics.Counts = e.Value.(*Counts)
	}

	// only keep the last month of counts
	for counts.Len() > 720 {
		counts.Remove(counts.Back())
	}

	// send text to clients currently attached
	if clients := trailer.clients.Load(); clients != nil {
		for client := range clients.(Clients) {
			client.strings <- text
		}
	}

	if trailer.Monitor != nil {
		trailer.Monitor.RecordMetrics("process", &metrics)
	}
}

func (trailer *Tailer) New() (stream *Stream) {
	stream = &Stream{
		strings: make(chan string),
	}

	trailer.mu.Lock()
	defer trailer.mu.Unlock()

	last := trailer.clients.Load()
	next := make(Clients)
	next[stream] = stream
	if last != nil {
		clients := last.(Clients)
		for item := range clients {
			next[item] = item
		}
	}

	trailer.clients.Store(next)
	return
}

func (trailer *Tailer) Close(stream *Stream) {
	trailer.mu.Lock()
	defer trailer.mu.Unlock()

	clients := trailer.clients.Load().(Clients)
	next := make(Clients)
	for item := range clients {
		if item == stream {
			continue
		}

		next[item] = item
	}

	trailer.clients.Store(next)
}
<|endoftext|>


<commit_before>// Licensed 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.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(  "Line_ID:" + message.USERId )).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}
<commit_msg>no message<commit_after>// Licensed 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.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(  "Line_ID:" + message.ID )).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"github.com/gorilla/mux"
	"strings"
)

type yoedConfig struct {
	Listen   string `json:"listen"`
}

type yoedHandler interface {
	Handle(username string)
}

func loadConfig(configPath string) (*yoedConfig, error) {

	configFile, err := os.Open(configPath)

	if err != nil {
		return nil, err
	}

	configJson, err := ioutil.ReadAll(configFile)

	if err != nil {
		return nil, err
	}

	config := &yoedConfig{}

	if err := json.Unmarshal(configJson, config); err != nil {
		return nil, err
	}

	return config, nil
}

func main() {

	config, err := loadConfig("./config.json")

	if err != nil {
		panic(fmt.Sprintf("failed loading config: %s", err))
	}

	handlers := make(map[string]map[string]bool, 0)

	router := mux.NewRouter()
	router.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) {
		strHandles := r.FormValue("handles")
		callbackUrl := r.FormValue("callback_url")
		if strHandles == "" || callbackUrl == "" {
			errorMsg := "Handles and callback_url are mandatory"
			log.Printf("Error on subcribe: %s", errorMsg)
			http.Error(w, errorMsg, 400)
			return
		}

		handles := strings.Split(strHandles, ",")
		for _, handle := range handles {
			log.Printf("Subscribe %s: %s", handle, callbackUrl)
			if handlers[handle] == nil {
				handlers[handle] = make(map[string]bool, 0)
			}
			handlers[handle][callbackUrl] = true
		}
	})
	router.HandleFunc(`/yoed/{handle:[a-z0-9]+}`, func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		handle := vars["handle"]
		username := r.FormValue("username")
		log.Printf("got a YO from %s on %s", username, handle)

		if 0 == len(handlers) || handlers[handle] == nil || 0 == len(handlers[handle]) {
			log.Printf("No handler registered for handle %s", handle)
		} else {
			for handler, _ := range handlers[handle] {
				log.Printf("Dispatch to handler %s", handler)
				resp, err := http.PostForm(handler, url.Values{"username":{username}})

				if err != nil {
					log.Printf("Error while dispatching message to %s: %s", handler, err)
					log.Printf("Remove handler %s", handler)
					delete(handlers[handle], handler)
				} else {
					log.Printf("Handler %s status: %s", handler, resp.Status)
				}
			}
		}
	})

	server := http.Server{
		Addr:    config.Listen,
		Handler: router,
	}

	log.Printf("Listening...")

	if err := server.ListenAndServe(); err != nil {
		fmt.Println(err)
	}

}<commit_msg>dispatch concurrently<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/mux"
)

type yoedConfig struct {
	Listen string `json:"listen"`
}

func loadConfig(configPath string) (*yoedConfig, error) {

	configFile, err := os.Open(configPath)

	if err != nil {
		return nil, err
	}

	configJson, err := ioutil.ReadAll(configFile)

	if err != nil {
		return nil, err
	}

	config := &yoedConfig{}

	if err := json.Unmarshal(configJson, config); err != nil {
		return nil, err
	}

	return config, nil
}

func dispatch(handler, username string) {

	resp, err := http.PostForm(handler, url.Values{
		"username": {username},
	})

	if err != nil {
		log.Printf("Error while dispatching message to %s: %s", handler, err)
		log.Printf("Remove handler %s", handler)
	} else {
		log.Printf("Handler %s status: %s", handler, resp.Status)
	}
}

func main() {

	config, err := loadConfig("./config.json")

	if err != nil {
		panic(fmt.Sprintf("failed loading config: %s", err))
	}

	handlers := make(map[string]map[string]bool, 0)

	router := mux.NewRouter()
	router.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) {
		strHandles := r.FormValue("handles")
		callbackUrl := r.FormValue("callback_url")
		if strHandles == "" || callbackUrl == "" {
			errorMsg := "Handles and callback_url are mandatory"
			log.Printf("Error on subcribe: %s", errorMsg)
			http.Error(w, errorMsg, 400)
			return
		}

		handles := strings.Split(strHandles, ",")
		for _, handle := range handles {
			log.Printf("Subscribe %s: %s", handle, callbackUrl)
			if handlers[handle] == nil {
				handlers[handle] = make(map[string]bool, 0)
			}
			handlers[handle][callbackUrl] = true
		}
	})
	router.HandleFunc(`/yoed/{handle:[a-z0-9]+}`, func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		handle := vars["handle"]
		username := r.FormValue("username")
		log.Printf("got a YO from %s on %s", username, handle)

		if 0 == len(handlers) || nil == handlers[handle] || 0 == len(handlers[handle]) {
			log.Printf("No handler registered for handle %s", handle)
			return
		}

		for handler, _ := range handlers[handle] {
			log.Printf("Dispatching to handler %s", handler)
			go dispatch(handler, username)
		}
	})

	server := http.Server{
		Addr:    config.Listen,
		Handler: router,
	}

	log.Printf("Listening...")

	if err := server.ListenAndServe(); err != nil {
		fmt.Println(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
  "net/http"
  "github.com/koding/logging"
  "flag"
  "fmt"
)

var (
  port = flag.String("port", "8080", "Port to use")
  path = flag.String("path", "./", "Path the server is started from")
)

func main() {
  flag.Parse()

  server := HTTPServer{
    http.Server{
      Addr: ":" + *port,
      Handler: HTTPHandler{
        realHandler: http.FileServer(http.Dir(*path)),
      },
    },
  }

  err := server.ListenAndServe()
  if err != nil {
    logging.Error(fmt.Sprint(err))
  }
}

type HTTPHandler struct {
  realHandler http.Handler
}

func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  logging.Info("%s %s", r.Method, r.URL)

  h.realHandler.ServeHTTP(w, r)
}

type HTTPServer struct {
  http.Server
}

func (h *HTTPServer) ListenAndServe() error {
  logging.Info(
    "Starting server on \033[%dm%s\033[0m at \033[%dm%s\033[0m",
    logging.CYAN,
    h.Server.Addr,
    logging.CYAN,
    *path,
  )

  return h.Server.ListenAndServe()
}

<commit_msg>GoFmt<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/koding/logging"
	"net/http"
)

var (
	port = flag.String("port", "8080", "Port to use")
	path = flag.String("path", "./", "Path the server is started from")
)

func main() {
	flag.Parse()

	server := HTTPServer{
		http.Server{
			Addr: ":" + *port,
			Handler: HTTPHandler{
				realHandler: http.FileServer(http.Dir(*path)),
			},
		},
	}

	err := server.ListenAndServe()
	if err != nil {
		logging.Error(fmt.Sprint(err))
	}
}

type HTTPHandler struct {
	realHandler http.Handler
}

func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logging.Info("%s %s", r.Method, r.URL)

	h.realHandler.ServeHTTP(w, r)
}

type HTTPServer struct {
	http.Server
}

func (h *HTTPServer) ListenAndServe() error {
	logging.Info(
		"Starting server on \033[%dm%s\033[0m at \033[%dm%s\033[0m",
		logging.CYAN,
		h.Server.Addr,
		logging.CYAN,
		*path,
	)

	return h.Server.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"html/template"
	"net/http"
	"os"
	"regexp"

	"github.com/golang/glog"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"github.com/zakuni/foon/models"
)

var db gorm.DB

func handler(w http.ResponseWriter, r *http.Request) {
	glog.Info(r.Method, " ", r.URL.Path)
	re := regexp.MustCompile("^/([^/]*)/?([^/]*)$")
	params := re.FindStringSubmatch(r.URL.Path)
	wikiName := params[1]
	pageName := params[2]

	switch r.Method {
	case "GET":
		if wikiName == "" {
			t, _ := template.ParseFiles("index.html")
			t.Execute(w, nil)
		} else if pageName == "" {
			var wiki models.Wiki
			var pages []models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.Model(&wiki).Related(&pages)
			wiki.Pages = pages

			t, _ := template.ParseFiles("wiki.html")
			t.Execute(w, wiki)
		} else {
			var wiki models.Wiki
			var page models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.FirstOrCreate(&page, models.Page{Name: pageName, WikiId: wiki.Id})

			t, _ := template.ParseFiles("page.html")
			t.Execute(w, page)
		}
	case "POST":
		if wikiName == "" {
			t, _ := template.ParseFiles("index.html")
			t.Execute(w, nil)
		} else if pageName == "" {
			var wiki models.Wiki
			var pages []models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.Model(&wiki).Related(&pages)
			wiki.Pages = pages

			t, _ := template.ParseFiles("wiki.html")
			t.Execute(w, wiki)
		} else {
			var wiki models.Wiki
			var page models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.FirstOrCreate(&page, models.Page{Name: pageName, WikiId: wiki.Id})
			page.Content = r.FormValue("content")
			db.Save(&page)

			t, _ := template.ParseFiles("page.html")
			t.Execute(w, page)
		}
	}
}

func initDb() gorm.DB {
	db, err := gorm.Open("postgres", "user=zakuni dbname=foon sslmode=disable")
	checkErr(err, "sql.Open failed")
	db.DB()

	db.AutoMigrate(models.Wiki{})
	db.AutoMigrate(models.Page{})

	return db
}

func checkErr(err error, msg string) {
	if err != nil {
		glog.Fatalln(msg, err)
	}
}

func main() {
	// necessary for glog
	flag.Parse()

	db = initDb()
	defer db.Close()

	glog.Infoln("start listening at port", os.Getenv("PORT"))
	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
<commit_msg>return MethodNotAllowed with post to  invalid path<commit_after>package main

import (
	"flag"
	"html/template"
	"net/http"
	"os"
	"regexp"

	"github.com/golang/glog"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"github.com/zakuni/foon/models"
)

var db gorm.DB

func handler(w http.ResponseWriter, r *http.Request) {
	glog.Info(r.Method, " ", r.URL.Path)
	re := regexp.MustCompile("^/([^/]*)/?([^/]*)$")
	params := re.FindStringSubmatch(r.URL.Path)
	wikiName := params[1]
	pageName := params[2]

	switch r.Method {
	case "GET":
		if wikiName == "" {
			t, _ := template.ParseFiles("index.html")
			t.Execute(w, nil)
		} else if pageName == "" {
			var wiki models.Wiki
			var pages []models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.Model(&wiki).Related(&pages)
			wiki.Pages = pages

			t, _ := template.ParseFiles("wiki.html")
			t.Execute(w, wiki)
		} else {
			var wiki models.Wiki
			var page models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.FirstOrCreate(&page, models.Page{Name: pageName, WikiId: wiki.Id})

			t, _ := template.ParseFiles("page.html")
			t.Execute(w, page)
		}
	case "POST":
		if wikiName != "" && pageName != "" {
			var wiki models.Wiki
			var page models.Page

			db.FirstOrCreate(&wiki, models.Wiki{Name: wikiName})
			db.FirstOrCreate(&page, models.Page{Name: pageName, WikiId: wiki.Id})
			page.Content = r.FormValue("content")
			db.Save(&page)

			t, _ := template.ParseFiles("page.html")
			t.Execute(w, page)
		} else {
			http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
		}
	}
}

func initDb() gorm.DB {
	db, err := gorm.Open("postgres", "user=zakuni dbname=foon sslmode=disable")
	checkErr(err, "sql.Open failed")
	db.DB()

	db.AutoMigrate(models.Wiki{})
	db.AutoMigrate(models.Page{})

	return db
}

func checkErr(err error, msg string) {
	if err != nil {
		glog.Fatalln(msg, err)
	}
}

func main() {
	// necessary for glog
	flag.Parse()

	db = initDb()
	defer db.Close()

	glog.Infoln("start listening at port", os.Getenv("PORT"))
	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
<|endoftext|>