<commit_before>package menu

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/fatih/color"
	"github.com/miquella/ask"
	"golang.org/x/crypto/ssh"

	"github.com/miquella/vaulted/lib"
)

type SSHKeyMenu struct {
	*Menu
}

func (m *SSHKeyMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("a,add      - Add")
	fmt.Println("D,delete   - Delete")
	fmt.Println("g,generate - Generate Key")
	fmt.Println("v          - HashiCorp Vault Signing URL")
	fmt.Println("u,users    - HashiCorp Vault User Principals")
	fmt.Println("?,help     - Help")
	fmt.Println("b,back     - Back")
	fmt.Println("q,quit     - Quit")
}

func (m *SSHKeyMenu) Handler() error {
	for {
		var err error
		m.Printer()
		input, err := interaction.ReadMenu("Edit ssh keys: [a,D,g,v,u,b]: ")
		if err != nil {
			return err
		}
		switch input {
		case "a", "add", "key", "keys":
			err = m.AddSSHKey()
		case "D", "delete", "remove":
			var key string
			key, err = interaction.ReadValue("Key: ")
			if err == nil {
				if _, exists := m.Vault.SSHKeys[key]; exists {
					delete(m.Vault.SSHKeys, key)
				} else {
					color.Red("Key '%s' not found", key)
				}
			}
		case "g", "generate":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.GenerateRSAKey = !m.Vault.SSHOptions.GenerateRSAKey
		case "v":
			signingUrl, err := interaction.ReadValue("HashiCorp Vault signing URL: ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.VaultSigningUrl = signingUrl

			if signingUrl != "" && !m.Vault.SSHOptions.GenerateRSAKey {
				generateKey, _ := interaction.ReadValue("Would you like to enable RSA key generation (y/n): ")
				if generateKey == "y" {
					m.Vault.SSHOptions.GenerateRSAKey = true
				}
			}
		case "u", "users":
			userPrincipals, err := interaction.ReadValue("HashiCorp Vault user principals (comma separated): ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			if userPrincipals != "" {
				m.Vault.SSHOptions.ValidPrincipals = strings.Split(userPrincipals, ",")
			} else {
				m.Vault.SSHOptions.ValidPrincipals = []string{}
			}
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *SSHKeyMenu) AddSSHKey() error {
	var err error

	homeDir := ""
	user, err := user.Current()
	if err == nil {
		homeDir = user.HomeDir
	} else {
		homeDir = os.Getenv("HOME")
	}

	defaultFilename := ""
	filename := ""
	if homeDir != "" {
		defaultFilename = filepath.Join(homeDir, ".ssh", "id_rsa")
		filename, err = interaction.ReadValue(fmt.Sprintf("Key file (default: %s): ", defaultFilename))
		if err != nil {
			return err
		}
		if filename == "" {
			filename = defaultFilename
		}
		if !filepath.IsAbs(filename) {
			filename = filepath.Join(filepath.Join(homeDir, ".ssh"), filename)
		}
	} else {
		filename, err = interaction.ReadValue("Key file: ")
		if err != nil {
			return err
		}
	}

	decryptedBlock, err := loadAndDecryptKey(filename)
	if err != nil {
		color.Red("%v", err)
		return nil
	}

	comment := loadPublicKeyComment(filename + ".pub")
	var name string
	if comment != "" {
		name, err = interaction.ReadValue(fmt.Sprintf("Name (default: %s): ", comment))
		if err != nil {
			return err
		}
		if name == "" {
			name = comment
		}
	} else {
		name, err = interaction.ReadValue("Name: ")
		if err != nil {
			return err
		}
		if name == "" {
			name = filename
		}
	}

	if m.Vault.SSHKeys == nil {
		m.Vault.SSHKeys = make(map[string]string)
	}
	m.Vault.SSHKeys[name] = string(pem.EncodeToMemory(decryptedBlock))

	return nil
}

func loadAndDecryptKey(filename string) (*pem.Block, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(data)
	if block == nil {
		return nil, err
	}

	if x509.IsEncryptedPEMBlock(block) {
		var passphrase string
		var decryptedBytes []byte
		for i := 0; i < 3; i++ {
			passphrase, err = ask.HiddenAsk("Passphrase: ")
			if err != nil {
				return nil, err
			}

			decryptedBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
			if err == nil {
				break
			}
			if err != x509.IncorrectPasswordError {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		return &pem.Block{
			Type:  block.Type,
			Bytes: decryptedBytes,
		}, nil
	}
	return block, nil
}

func loadPublicKeyComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}

	_, comment, _, _, err := ssh.ParseAuthorizedKey(data)
	if err != nil {
		return ""
	}
	return comment
}

func (m *SSHKeyMenu) Printer() {
	color.Cyan("\nSSH Agent:")
	color.Cyan("  Keys:")
	if len(m.Vault.SSHKeys) > 0 || m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
		keys := []string{}
		for key := range m.Vault.SSHKeys {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		for _, key := range keys {
			green.Printf("    %s\n", key)
		}

		if m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
			faintColor.Print("    <generated RSA key>\n")
		}
	} else {
		fmt.Println("    [Empty]")
	}

	if m.Vault.SSHOptions != nil {
		if m.Vault.SSHOptions.VaultSigningUrl != "" || len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
			color.Cyan("\n  Signing (HashiCorp Vault):")
			if m.Vault.SSHOptions.VaultSigningUrl != "" {
				green.Printf("    URL: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.VaultSigningUrl)
			}

			if len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
				green.Printf("    User: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.ValidPrincipals)
			}
		}
	}
}
<commit_msg>add disable proxy to ssh edit menu<commit_after>package menu

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/fatih/color"
	"github.com/miquella/ask"
	"golang.org/x/crypto/ssh"

	"github.com/miquella/vaulted/lib"
)

type SSHKeyMenu struct {
	*Menu
}

func (m *SSHKeyMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("a,add      - Add")
	fmt.Println("D,delete   - Delete")
	fmt.Println("g,generate - Generate Key")
	fmt.Println("v          - HashiCorp Vault Signing URL")
	fmt.Println("u,users    - HashiCorp Vault User Principals")
	fmt.Println("e          - Expose External SSH Agent")
	fmt.Println("?,help     - Help")
	fmt.Println("b,back     - Back")
	fmt.Println("q,quit     - Quit")
}

func (m *SSHKeyMenu) Handler() error {
	for {
		var err error
		m.Printer()
		input, err := interaction.ReadMenu("Edit ssh keys: [a,D,g,v,u,e,b]: ")
		if err != nil {
			return err
		}
		switch input {
		case "a", "add", "key", "keys":
			err = m.AddSSHKey()
		case "D", "delete", "remove":
			var key string
			key, err = interaction.ReadValue("Key: ")
			if err == nil {
				if _, exists := m.Vault.SSHKeys[key]; exists {
					delete(m.Vault.SSHKeys, key)
				} else {
					color.Red("Key '%s' not found", key)
				}
			}
		case "g", "generate":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.GenerateRSAKey = !m.Vault.SSHOptions.GenerateRSAKey
		case "v":
			signingUrl, err := interaction.ReadValue("HashiCorp Vault signing URL: ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.VaultSigningUrl = signingUrl

			if signingUrl != "" && !m.Vault.SSHOptions.GenerateRSAKey {
				generateKey, _ := interaction.ReadValue("Would you like to enable RSA key generation (y/n): ")
				if generateKey == "y" {
					m.Vault.SSHOptions.GenerateRSAKey = true
				}
			}
		case "u", "users":
			userPrincipals, err := interaction.ReadValue("HashiCorp Vault user principals (comma separated): ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			if userPrincipals != "" {
				m.Vault.SSHOptions.ValidPrincipals = strings.Split(userPrincipals, ",")
			} else {
				m.Vault.SSHOptions.ValidPrincipals = []string{}
			}
		case "e":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.DisableProxy = !m.Vault.SSHOptions.DisableProxy
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *SSHKeyMenu) AddSSHKey() error {
	var err error

	homeDir := ""
	user, err := user.Current()
	if err == nil {
		homeDir = user.HomeDir
	} else {
		homeDir = os.Getenv("HOME")
	}

	defaultFilename := ""
	filename := ""
	if homeDir != "" {
		defaultFilename = filepath.Join(homeDir, ".ssh", "id_rsa")
		filename, err = interaction.ReadValue(fmt.Sprintf("Key file (default: %s): ", defaultFilename))
		if err != nil {
			return err
		}
		if filename == "" {
			filename = defaultFilename
		}
		if !filepath.IsAbs(filename) {
			filename = filepath.Join(filepath.Join(homeDir, ".ssh"), filename)
		}
	} else {
		filename, err = interaction.ReadValue("Key file: ")
		if err != nil {
			return err
		}
	}

	decryptedBlock, err := loadAndDecryptKey(filename)
	if err != nil {
		color.Red("%v", err)
		return nil
	}

	comment := loadPublicKeyComment(filename + ".pub")
	var name string
	if comment != "" {
		name, err = interaction.ReadValue(fmt.Sprintf("Name (default: %s): ", comment))
		if err != nil {
			return err
		}
		if name == "" {
			name = comment
		}
	} else {
		name, err = interaction.ReadValue("Name: ")
		if err != nil {
			return err
		}
		if name == "" {
			name = filename
		}
	}

	if m.Vault.SSHKeys == nil {
		m.Vault.SSHKeys = make(map[string]string)
	}
	m.Vault.SSHKeys[name] = string(pem.EncodeToMemory(decryptedBlock))

	return nil
}

func loadAndDecryptKey(filename string) (*pem.Block, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(data)
	if block == nil {
		return nil, err
	}

	if x509.IsEncryptedPEMBlock(block) {
		var passphrase string
		var decryptedBytes []byte
		for i := 0; i < 3; i++ {
			passphrase, err = ask.HiddenAsk("Passphrase: ")
			if err != nil {
				return nil, err
			}

			decryptedBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
			if err == nil {
				break
			}
			if err != x509.IncorrectPasswordError {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		return &pem.Block{
			Type:  block.Type,
			Bytes: decryptedBytes,
		}, nil
	}
	return block, nil
}

func loadPublicKeyComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}

	_, comment, _, _, err := ssh.ParseAuthorizedKey(data)
	if err != nil {
		return ""
	}
	return comment
}

func (m *SSHKeyMenu) Printer() {
	color.Cyan("\nSSH Agent:")
	color.Cyan("  Keys:")
	if len(m.Vault.SSHKeys) > 0 || m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
		keys := []string{}
		for key := range m.Vault.SSHKeys {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		for _, key := range keys {
			green.Printf("    %s\n", key)
		}

		if m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
			faintColor.Print("    <generated RSA key>\n")
		}
	} else {
		fmt.Println("    [Empty]")
	}

	if m.Vault.SSHOptions != nil {
		if m.Vault.SSHOptions.VaultSigningUrl != "" || len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
			color.Cyan("\n  Signing (HashiCorp Vault):")
			if m.Vault.SSHOptions.VaultSigningUrl != "" {
				green.Printf("    URL: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.VaultSigningUrl)
			}

			if len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
				green.Printf("    User: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.ValidPrincipals)
			}

			cyan.Print("\n  Expose external SSH agent: ")
			fmt.Printf("%t\n", !m.Vault.SSHOptions.DisableProxy)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"math"

	"github.com/gonum/matrix/mat64"
)

// tanh applies an element-wise tanh to the parameter and returns a new vector
func tanh(x *mat64.Vector) *mat64.Vector {
	y := mat64.NewVector(x.Len(), nil)
	for i := 0; i < x.Len(); i++ {
		y.SetVec(i, math.Tanh(x.At(i, 0)))
	}
	return y
}

// dot is a matrix multiplication the returns a Vector
func dot(x mat64.Matrix, y *mat64.Vector) *mat64.Vector {
	v := mat64.NewVector(y.Len(), nil)
	v.MulVec(x, y)
	return v
}

// TODO: check the size of the vectors...
func add(x, y *mat64.Vector) *mat64.Vector {
	v := mat64.NewVector(x.Len(), nil)
	v.AddVec(x, y)
	return v
}
<commit_msg>Not used anymore<commit_after><|endoftext|>


<commit_before>package mesosutil

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"regexp"

	log "github.com/golang/glog"
	mesos "github.com/mesos/mesos-go/mesosproto"
)

func FetchLog(logUrl string) ([]byte, error) {
	resp, err := defaultClient.Get(logUrl)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, ProxyError{resp.Status, resp.StatusCode}
	}

	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

type ProxyError struct {
	Status     string
	StatusCode int
}

func (p ProxyError) Error() string {
	return p.Status
}

type MesosState struct {
	Frameworks []Framework
}

func (m MesosState) Directory(source string) string {
	for _, f := range m.Frameworks {
		// should we check for the framework?
		for _, e := range append(f.CompletedExecutors, f.Executors...) {
			if e.Source == source {
				return e.Directory
			}
		}
	}
	return ""
}

type Framework struct {
	Name               string
	CompletedExecutors []Executor `json:"completed_executors"`
	Executors          []Executor
}

type Executor struct {
	Source    string
	Directory string
}

type MesosTaskStatusMounts []struct {
	Source string `json:"Source"`
}

type MesosTaskStatusConfig struct {
	Hostname   string `json:"Hostname"`
	Domainname string `json:"Domainname"`
}

type MesosTaskStatusData []struct {
	Mounts MesosTaskStatusMounts `json:"Mounts"`
	Config MesosTaskStatusConfig `json:"Config"`
}

type LogData struct {
	Data   string `json:"data"`
	Offset int    `json:"offset"`
}

func FetchLogs(status *mesos.TaskStatus, offset int, file string) ([]byte, error) {
	var mtsd MesosTaskStatusData
	err := json.Unmarshal(status.Data, &mtsd)
	if err != nil {
		return nil, err
	}
	// status.Data is an array of one value :( Maybe there is a better way to marshal it?
	firstMtsd := mtsd[0]
	log.V(2).Infof("firstMtsd: %#v", firstMtsd)
	var dir string
	for _, mount := range firstMtsd.Mounts {
		source := mount.Source
		log.V(2).Infoln("mount: ", source)
		matched, _ := regexp.MatchString("slaves.*frameworks.*executors", source)
		if matched {
			dir = source
		}
	}
	url := fmt.Sprintf("http://%s.%s:5051/files/read.json?path=%s/%s&offset=%d",
		firstMtsd.Config.Hostname, firstMtsd.Config.Domainname, dir, file, offset)
	bodyData, _ := FetchLog(url)

	var logData LogData
	err = json.Unmarshal(bodyData, &logData)
	if err != nil {
		return nil, err
	}
	return []byte(logData.Data), nil
}
<commit_msg>Fix if no domain name is received from mesos.<commit_after>package mesosutil

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"regexp"

	log "github.com/golang/glog"
	mesos "github.com/mesos/mesos-go/mesosproto"
)

func FetchLog(logUrl string) ([]byte, error) {
	resp, err := defaultClient.Get(logUrl)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, ProxyError{resp.Status, resp.StatusCode}
	}

	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

type ProxyError struct {
	Status     string
	StatusCode int
}

func (p ProxyError) Error() string {
	return p.Status
}

type MesosState struct {
	Frameworks []Framework
}

func (m MesosState) Directory(source string) string {
	for _, f := range m.Frameworks {
		// should we check for the framework?
		for _, e := range append(f.CompletedExecutors, f.Executors...) {
			if e.Source == source {
				return e.Directory
			}
		}
	}
	return ""
}

type Framework struct {
	Name               string
	CompletedExecutors []Executor `json:"completed_executors"`
	Executors          []Executor
}

type Executor struct {
	Source    string
	Directory string
}

type MesosTaskStatusMounts []struct {
	Source string `json:"Source"`
}

type MesosTaskStatusConfig struct {
	Hostname   string `json:"Hostname"`
	Domainname string `json:"Domainname"`
}

type MesosTaskStatusData []struct {
	Mounts MesosTaskStatusMounts `json:"Mounts"`
	Config MesosTaskStatusConfig `json:"Config"`
}

type LogData struct {
	Data   string `json:"data"`
	Offset int    `json:"offset"`
}

func FetchLogs(status *mesos.TaskStatus, offset int, file string) ([]byte, error) {
	var mtsd MesosTaskStatusData
	err := json.Unmarshal(status.Data, &mtsd)
	if err != nil {
		return nil, err
	}
	// status.Data is an array of one value :( Maybe there is a better way to marshal it?
	firstMtsd := mtsd[0]
	log.V(2).Infof("firstMtsd: %#v", firstMtsd)
	var dir string
	for _, mount := range firstMtsd.Mounts {
		source := mount.Source
		log.V(2).Infoln("mount: ", source)
		matched, _ := regexp.MatchString("slaves.*frameworks.*executors", source)
		if matched {
			dir = source
		}
	}
	domainName := ""
	if firstMtsd.Config.Domainname != "" {
		domainName = "." + firstMtsd.Config.Domainname
	}
	url := fmt.Sprintf("http://%s%s:5051/files/read.json?path=%s/%s&offset=%d",
		firstMtsd.Config.Hostname, domainName, dir, file, offset)
	bodyData, _ := FetchLog(url)

	var logData LogData
	err = json.Unmarshal(bodyData, &logData)
	if err != nil {
		return nil, err
	}
	return []byte(logData.Data), nil
}
<|endoftext|>


<commit_before>// osop
// Copyright (C) 2014 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// osop - other side of the pipe - outputs formatted metrics to your Stdout.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"reflect"
	"strings"
	"text/template"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/adrg/xdg"
)

// fatal is a helper function to call when something terribly wrong
// may have happened. Logs given error and terminates application.
func fatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// config defines configuration structure.
type config map[string]interface{}

// PollingReceiver defines a basic type of receiver, which
// will run every config:`pollInterval` and try to get new data ASAP.
type PollingReceiver interface {
	Init(config config) error
	Get() (interface{}, error)
}

// EventedReceiver defines an advanced receiver, which
// is able to wait for a change to happen and only then report
// a new value.
//
// Note that it does not need to implement a fully functional Get() method.
// It is only used at the beginning, so user do not have to wait
// for an event to happen to get the initial value.
type EventedReceiver interface {
	PollingReceiver
	GetEvented() (interface{}, error)
}

// IRegistry defines interface for receivers registry.
//
// Default registry is provided as a globally accessible `registry`
// variable. All receivers shall add themselves there before they
// could be used (tip: init() function is a good way to do so).
type IRegistry interface {
	AddReceiver(string, PollingReceiver, interface{})
	GetReceiver(string) (PollingReceiver, error)
	GetZero(string) (interface{}, error)
}

// Registry is a default IRegistry implementation.
type Registry struct {
	receivers map[string]reflect.Type
	zeros     map[string]interface{}
}

// AddReceiver adds new receiver to registry.
//
// `zero` should be an initial (probably empty), expected response.
// It's to workaround text/template panicking on non-existing structure elements.
func (r *Registry) AddReceiver(name string, rec PollingReceiver, zero interface{}) {
	name = strings.ToLower(name)
	r.receivers[name] = reflect.TypeOf(rec).Elem()
	r.zeros[name] = zero
}

// GetReceiver gets existing receiver from registry.
// New instance is created on every call to allow multiple
// instances of the same receiver to co-exist.
//
// Note that receiver names are case insensitive.
func (r *Registry) GetReceiver(name string) (PollingReceiver, error) {
	v := r.receivers[strings.ToLower(name)]
	if v == nil {
		return nil, fmt.Errorf("Receiver `%s` not found", name)
	}
	return reflect.New(v).Interface().(PollingReceiver), nil
}

// GetZero gets zero response for an existing receiver.
//
// Note that receiver names are case insensitive.
func (r *Registry) GetZero(name string) (interface{}, error) {
	v, ok := r.zeros[strings.ToLower(name)]
	if !ok {
		return nil, fmt.Errorf("Receiver `%s` zero value not found", name)
	}
	return v, nil
}

// registry is a default, globally available Registry instance.
var registry IRegistry = &Registry{
	receivers: make(map[string]reflect.Type),
	zeros:     make(map[string]interface{}),
}

// Change is emitted for every receiver value change.
type Change struct {
	Name  string
	Value interface{}
}

// Worker processes receiver value changes.
//
// Responsible for getting the value from receiver and propagating it
// further to the template compilation method.
type Worker struct {
	pollInterval time.Duration
	receiver     PollingReceiver
	name         string
	once         bool
}

// doChange handles a single value change.
func (w *Worker) doChange(get func() (interface{}, error), ch chan Change) {
	value, err := get()
	if err != nil {
		log.Printf("%s: %s\n", w.name, err)
		return
	}
	if value != nil {
		ch <- Change{
			Name:  w.name,
			Value: value,
		}
	}
}

// Do acts as a Worker event loop.
//
// For PollingReceivers, spawns every config:`pollInterval`.
// For EventedReceivers, blocks until an event is generated.
func (w *Worker) Do(ch chan Change) {
	switch r := w.receiver.(type) {
	case EventedReceiver:
		// Get first value in "normal" manner,
		// so user won't have to wait for an event to occur.
		w.doChange(r.Get, ch)
		for {
			w.doChange(r.GetEvented, ch)
			if w.once {
				break
			}
		}
	case PollingReceiver:
		w.doChange(r.Get, ch)
		for _ = range time.Tick(w.pollInterval) {
			w.doChange(r.Get, ch)
			if w.once {
				break
			}
		}
	}
}

// NewWorker constructs new Worker instance with given name and config.
func NewWorker(name string, config config) *Worker {
	interval := time.Second
	if config["pollInterval"] != nil {
		_interval, err := time.ParseDuration(config["pollInterval"].(string))
		if err == nil {
			interval = _interval
		}
	}
	receiver, _ := registry.GetReceiver(config["receiver"].(string))

	err := receiver.Init(config)
	for err != nil {
		log.Println(err)
		time.Sleep(time.Second)
		err = receiver.Init(config)
	}

	return &Worker{
		pollInterval: interval,
		receiver:     receiver,
		name:         name,
	}
}

func main() {
	configFilename := flag.String("c", "", "Path to the configuration file")
	flag.Parse()

	var configs map[string]map[string]interface{}
	if _, err := toml.DecodeFile(*configFilename, &configs); err != nil {
		if _, ok := err.(*os.PathError); !ok {
			fatal(err)
		}
		if *configFilename == "" {
			*configFilename = "config.toml"
		}
		xdgFile, err := xdg.ConfigFile(path.Join("osop", *configFilename))
		fatal(err)
		if _, err := os.Stat(xdgFile); os.IsNotExist(err) {
			f, err := os.Create(xdgFile)
			fatal(err)
			f.WriteString(strings.TrimSpace(`
[Now]
receiver="date"
pollInterval="1s"
format="02/01/2006 15:04:05"

[Osop]
template="<.Now>"
			`))
			f.Close()
		}
		_, err = toml.DecodeFile(xdgFile, &configs)
		fatal(err)
	}

	delims, ok := configs["Osop"]["delims"].([]interface{})
	if !ok {
		delims = []interface{}{"<", ">"}
	}
	t, err := template.New("t").Delims(
		delims[0].(string), delims[1].(string),
	).Funcs(template.FuncMap{"stringify": func(arg interface{}) string {
		s, ok := arg.(string)
		if !ok {
			return ""
		}
		return s
	}}).Parse(
		configs["Osop"]["template"].(string) + "\n",
	)
	fatal(err)

	workers := make(chan *Worker)

	data := make(map[string]interface{})

	for name, conf := range configs {
		if name == "Osop" {
			continue
		}
		zero, err := registry.GetZero(conf["receiver"].(string))
		if err != nil {
			log.Printf("Error getting receiver (`%s`), not spawning worker\n", err)
			continue
		}
		data[name] = zero
		go func(ch chan *Worker, name string, conf config) {
			ch <- NewWorker(name, conf)
		}(workers, name, conf)
	}

	changes := make(chan Change)
	var cache string
	for {
		select {
		case worker := <-workers:
			if worker != nil {
				go worker.Do(changes)
			}
		case change := <-changes:
			data[change.Name] = change.Value
			var buf bytes.Buffer
			err := t.Execute(&buf, data)
			if err != nil {
				buf.WriteByte('\n')
			}

			str := buf.String()
			if str == cache {
				continue
			}
			cache = str

			fmt.Print(cache)
		}
	}
}
<commit_msg>Nicer log message on receiver init error<commit_after>// osop
// Copyright (C) 2014,2016 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// osop - other side of the pipe - outputs formatted metrics to your Stdout.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"reflect"
	"strings"
	"text/template"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/adrg/xdg"
)

// fatal is a helper function to call when something terribly wrong
// may have happened. Logs given error and terminates application.
func fatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// config defines configuration structure.
type config map[string]interface{}

// PollingReceiver defines a basic type of receiver, which
// will run every config:`pollInterval` and try to get new data ASAP.
type PollingReceiver interface {
	Init(config config) error
	Get() (interface{}, error)
}

// EventedReceiver defines an advanced receiver, which
// is able to wait for a change to happen and only then report
// a new value.
//
// Note that it does not need to implement a fully functional Get() method.
// It is only used at the beginning, so user do not have to wait
// for an event to happen to get the initial value.
type EventedReceiver interface {
	PollingReceiver
	GetEvented() (interface{}, error)
}

// IRegistry defines interface for receivers registry.
//
// Default registry is provided as a globally accessible `registry`
// variable. All receivers shall add themselves there before they
// could be used (tip: init() function is a good way to do so).
type IRegistry interface {
	AddReceiver(string, PollingReceiver, interface{})
	GetReceiver(string) (PollingReceiver, error)
	GetZero(string) (interface{}, error)
}

// Registry is a default IRegistry implementation.
type Registry struct {
	receivers map[string]reflect.Type
	zeros     map[string]interface{}
}

// AddReceiver adds new receiver to registry.
//
// `zero` should be an initial (probably empty), expected response.
// It's to workaround text/template panicking on non-existing structure elements.
func (r *Registry) AddReceiver(name string, rec PollingReceiver, zero interface{}) {
	name = strings.ToLower(name)
	r.receivers[name] = reflect.TypeOf(rec).Elem()
	r.zeros[name] = zero
}

// GetReceiver gets existing receiver from registry.
// New instance is created on every call to allow multiple
// instances of the same receiver to co-exist.
//
// Note that receiver names are case insensitive.
func (r *Registry) GetReceiver(name string) (PollingReceiver, error) {
	v := r.receivers[strings.ToLower(name)]
	if v == nil {
		return nil, fmt.Errorf("Receiver `%s` not found", name)
	}
	return reflect.New(v).Interface().(PollingReceiver), nil
}

// GetZero gets zero response for an existing receiver.
//
// Note that receiver names are case insensitive.
func (r *Registry) GetZero(name string) (interface{}, error) {
	v, ok := r.zeros[strings.ToLower(name)]
	if !ok {
		return nil, fmt.Errorf("Receiver `%s` zero value not found", name)
	}
	return v, nil
}

// registry is a default, globally available Registry instance.
var registry IRegistry = &Registry{
	receivers: make(map[string]reflect.Type),
	zeros:     make(map[string]interface{}),
}

// Change is emitted for every receiver value change.
type Change struct {
	Name  string
	Value interface{}
}

// Worker processes receiver value changes.
//
// Responsible for getting the value from receiver and propagating it
// further to the template compilation method.
type Worker struct {
	pollInterval time.Duration
	receiver     PollingReceiver
	name         string
	once         bool
}

// doChange handles a single value change.
func (w *Worker) doChange(get func() (interface{}, error), ch chan Change) {
	value, err := get()
	if err != nil {
		log.Printf("%s: %s\n", w.name, err)
		return
	}
	if value != nil {
		ch <- Change{
			Name:  w.name,
			Value: value,
		}
	}
}

// Do acts as a Worker event loop.
//
// For PollingReceivers, spawns every config:`pollInterval`.
// For EventedReceivers, blocks until an event is generated.
func (w *Worker) Do(ch chan Change) {
	switch r := w.receiver.(type) {
	case EventedReceiver:
		// Get first value in "normal" manner,
		// so user won't have to wait for an event to occur.
		w.doChange(r.Get, ch)
		for {
			w.doChange(r.GetEvented, ch)
			if w.once {
				break
			}
		}
	case PollingReceiver:
		w.doChange(r.Get, ch)
		for _ = range time.Tick(w.pollInterval) {
			w.doChange(r.Get, ch)
			if w.once {
				break
			}
		}
	}
}

// NewWorker constructs new Worker instance with given name and config.
func NewWorker(name string, config config) *Worker {
	interval := time.Second
	if config["pollInterval"] != nil {
		_interval, err := time.ParseDuration(config["pollInterval"].(string))
		if err == nil {
			interval = _interval
		}
	}
	receiver, _ := registry.GetReceiver(config["receiver"].(string))

	err := receiver.Init(config)
	for err != nil {
		log.Printf("%s: Init error: %s\n", name, err)
		time.Sleep(time.Second)
		err = receiver.Init(config)
	}

	return &Worker{
		pollInterval: interval,
		receiver:     receiver,
		name:         name,
	}
}

func main() {
	configFilename := flag.String("c", "", "Path to the configuration file")
	flag.Parse()

	var configs map[string]map[string]interface{}
	if _, err := toml.DecodeFile(*configFilename, &configs); err != nil {
		if _, ok := err.(*os.PathError); !ok {
			fatal(err)
		}
		if *configFilename == "" {
			*configFilename = "config.toml"
		}
		xdgFile, err := xdg.ConfigFile(path.Join("osop", *configFilename))
		fatal(err)
		if _, err := os.Stat(xdgFile); os.IsNotExist(err) {
			f, err := os.Create(xdgFile)
			fatal(err)
			f.WriteString(strings.TrimSpace(`
[Now]
receiver="date"
pollInterval="1s"
format="02/01/2006 15:04:05"

[Osop]
template="<.Now>"
			`))
			f.Close()
		}
		_, err = toml.DecodeFile(xdgFile, &configs)
		fatal(err)
	}

	delims, ok := configs["Osop"]["delims"].([]interface{})
	if !ok {
		delims = []interface{}{"<", ">"}
	}
	t, err := template.New("t").Delims(
		delims[0].(string), delims[1].(string),
	).Funcs(template.FuncMap{"stringify": func(arg interface{}) string {
		s, ok := arg.(string)
		if !ok {
			return ""
		}
		return s
	}}).Parse(
		configs["Osop"]["template"].(string) + "\n",
	)
	fatal(err)

	workers := make(chan *Worker)

	data := make(map[string]interface{})

	for name, conf := range configs {
		if name == "Osop" {
			continue
		}
		zero, err := registry.GetZero(conf["receiver"].(string))
		if err != nil {
			log.Printf("Error getting receiver (`%s`), not spawning worker\n", err)
			continue
		}
		data[name] = zero
		go func(ch chan *Worker, name string, conf config) {
			ch <- NewWorker(name, conf)
		}(workers, name, conf)
	}

	changes := make(chan Change)
	var cache string
	for {
		select {
		case worker := <-workers:
			if worker != nil {
				go worker.Do(changes)
			}
		case change := <-changes:
			data[change.Name] = change.Value
			var buf bytes.Buffer
			err := t.Execute(&buf, data)
			if err != nil {
				buf.WriteByte('\n')
			}

			str := buf.String()
			if str == cache {
				continue
			}
			cache = str

			fmt.Print(cache)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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 timeouts

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/coreos/ignition/tests/register"
	"github.com/coreos/ignition/tests/types"
)

func init() {
	register.Register(register.PositiveTest, IncreaseHTTPResponseHeadersTimeout())
	register.Register(register.PositiveTest, ConfirmHTTPBackoffWorks())
}

var (
	respondDelayServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Hold the connection open for 11 seconds, then return
		time.Sleep(time.Second * 11)
		w.WriteHeader(http.StatusOK)
	}))

	lastResponse           time.Time
	respondThrottledServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if (lastResponse != time.Time{}) && time.Since(lastResponse) > time.Second*4 {
			// Only respond successfully if it's been more than 4 seconds since
			// the last attempt
			w.WriteHeader(http.StatusOK)
			return
		}
		lastResponse = time.Now()
		w.WriteHeader(http.StatusInternalServerError)
	}))
)

func IncreaseHTTPResponseHeadersTimeout() types.Test {
	name := "Increase HTTP Response Headers Timeout"
	in := types.GetBaseDisk()
	out := types.GetBaseDisk()
	config := fmt.Sprintf(`{
		"ignition": {
			"version": "$version",
			"timeouts": {
				"httpResponseHeaders": 12
			}
		},
		"storage": {
		    "files": [
			    {
					"filesystem": "root",
					"path": "/foo/bar",
					"contents": {
						"source": %q
					}
				}
			]
		}
	}`, respondDelayServer.URL)
	configMinVersion := "2.1.0"
	out[0].Partitions.AddFiles("ROOT", []types.File{
		{
			Node: types.Node{
				Name:      "bar",
				Directory: "foo",
			},
			Contents: "",
		},
	})

	return types.Test{
		Name:             name,
		In:               in,
		Out:              out,
		Config:           config,
		ConfigMinVersion: configMinVersion,
	}
}

func ConfirmHTTPBackoffWorks() types.Test {
	name := "Confirm HTTP Backoff Works"
	in := types.GetBaseDisk()
	out := types.GetBaseDisk()
	config := fmt.Sprintf(`{
		"ignition": {
			"version": "$version"
		},
		"storage": {
		    "files": [
			    {
					"filesystem": "root",
					"path": "/foo/bar",
					"contents": {
						"source": %q
					}
				}
			]
		}
	}`, respondThrottledServer.URL)
	configMinVersion := "2.0.0"
	out[0].Partitions.AddFiles("ROOT", []types.File{
		{
			Node: types.Node{
				Name:      "bar",
				Directory: "foo",
			},
			Contents: "",
		},
	})

	return types.Test{
		Name:             name,
		In:               in,
		Out:              out,
		Config:           config,
		ConfigMinVersion: configMinVersion,
	}
}
<commit_msg>tests/timeouts: fix backoff test race<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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 timeouts

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/coreos/ignition/tests/register"
	"github.com/coreos/ignition/tests/types"
)

func init() {
	register.Register(register.PositiveTest, IncreaseHTTPResponseHeadersTimeout())
	register.Register(register.PositiveTest, ConfirmHTTPBackoffWorks())
}

var (
	respondDelayServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Hold the connection open for 11 seconds, then return
		time.Sleep(time.Second * 11)
		w.WriteHeader(http.StatusOK)
	}))

	lastResponses          = map[string]time.Time{}
	respondThrottledServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lastResponse, ok := lastResponses[r.RequestURI]
		if ok && time.Since(lastResponse) > time.Second*4 {
			// Only respond successfully if it's been more than 4 seconds since
			// the last attempt
			w.WriteHeader(http.StatusOK)
			return
		}
		lastResponses[r.RequestURI] = time.Now()
		w.WriteHeader(http.StatusInternalServerError)
	}))
)

func IncreaseHTTPResponseHeadersTimeout() types.Test {
	name := "Increase HTTP Response Headers Timeout"
	in := types.GetBaseDisk()
	out := types.GetBaseDisk()
	config := fmt.Sprintf(`{
		"ignition": {
			"version": "$version",
			"timeouts": {
				"httpResponseHeaders": 12
			}
		},
		"storage": {
		    "files": [
			    {
					"filesystem": "root",
					"path": "/foo/bar",
					"contents": {
						"source": %q
					}
				}
			]
		}
	}`, respondDelayServer.URL)
	configMinVersion := "2.1.0"
	out[0].Partitions.AddFiles("ROOT", []types.File{
		{
			Node: types.Node{
				Name:      "bar",
				Directory: "foo",
			},
			Contents: "",
		},
	})

	return types.Test{
		Name:             name,
		In:               in,
		Out:              out,
		Config:           config,
		ConfigMinVersion: configMinVersion,
	}
}

func ConfirmHTTPBackoffWorks() types.Test {
	name := "Confirm HTTP Backoff Works"
	in := types.GetBaseDisk()
	out := types.GetBaseDisk()
	config := fmt.Sprintf(`{
		"ignition": {
			"version": "$version"
		},
		"storage": {
		    "files": [
			    {
					"filesystem": "root",
					"path": "/foo/bar",
					"contents": {
						"source": "%s/$version"
					}
				}
			]
		}
	}`, respondThrottledServer.URL)
	configMinVersion := "2.0.0"
	out[0].Partitions.AddFiles("ROOT", []types.File{
		{
			Node: types.Node{
				Name:      "bar",
				Directory: "foo",
			},
			Contents: "",
		},
	})

	return types.Test{
		Name:             name,
		In:               in,
		Out:              out,
		Config:           config,
		ConfigMinVersion: configMinVersion,
	}
}
<|endoftext|>


<commit_before>package misc

import (
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
)

func Prerender(res http.ResponseWriter, req *http.Request) {
	prerender := false

	uas := regexp.MustCompile(`(?i)(baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|bufferbot|quora link preview)`)
	fileExtensions := regexp.MustCompile(`(?i)\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent)$`)

	if uas.MatchString(req.UserAgent()) {
		prerender = true
	}

	if regexp.MustCompile(`_escaped_fragment_`).MatchString(req.URL.String()) {
		prerender = true
	}

	if regexp.MustCompile(`Prerender`).MatchString(req.UserAgent()) {
		prerender = true
	}

	if fileExtensions.MatchString(req.URL.Path) {
		prerender = false
	}

	if prerender {
		basePrerenderUrl := os.Getenv("PRERENDER_URL")
		prerenderUrl := basePrerenderUrl + req.URL.String()

		resp, err := http.Get(prerenderUrl)
		defer resp.Body.Close()
		if err != nil {
			log.Println(err)
			return
		}

		io.Copy(res, resp.Body)
	}
}
<commit_msg>Don't prerender when UA is 'Prerender'<commit_after>package misc

import (
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
)

func Prerender(res http.ResponseWriter, req *http.Request) {
	prerender := false

	uas := regexp.MustCompile(`(?i)(baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|bufferbot|quora link preview)`)
	fileExtensions := regexp.MustCompile(`(?i)\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent)$`)

	if uas.MatchString(req.UserAgent()) {
		prerender = true
	}

	if regexp.MustCompile(`_escaped_fragment_`).MatchString(req.URL.String()) {
		prerender = true
	}

	if regexp.MustCompile(`Prerender`).MatchString(req.UserAgent()) {
		prerender = false
	}

	if fileExtensions.MatchString(req.URL.Path) {
		prerender = false
	}

	if prerender {
		basePrerenderUrl := os.Getenv("PRERENDER_URL")
		prerenderUrl := basePrerenderUrl + req.URL.String()

		resp, err := http.Get(prerenderUrl)
		defer resp.Body.Close()
		if err != nil {
			log.Println(err)
			return
		}

		io.Copy(res, resp.Body)
	}
}
<|endoftext|>


<commit_before>package model

import (
	"regexp"
)

type Document struct {
	Title       string
	Description string
	Content     string
	Hash        string

	rawLines []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:     repositoryItem.GetHash(),
		rawLines: repositoryItem.GetLines(),
	}

	// parse
	return doc.parse()
}

func (doc *Document) parse() *Document {
	return setTitle(doc)
}

func setTitle(doc *Document) *Document {
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")

	for lineNumber, line := range doc.rawLines {
		matches := titleRegexp.FindStringSubmatch(line)

		// line must match title pattern
		lineMatchesTitlePattern := len(matches) == 2
		if lineMatchesTitlePattern {

			// is first line or all previous lines are empty
			if lineNumber == 0 || linesMeetCondition(doc.rawLines[0:lineNumber], regexp.MustCompile("^\\s*$")) {

				doc.Title = matches[1]
				return setDescription(doc, lineNumber+1)

			}
		}
	}

	return doc
}

func setDescription(doc *Document, startLine int) *Document {
	if startLine > len(doc.rawLines) {
		return doc
	}

	descriptionRegexp := regexp.MustCompile("^\\w.+")

	for lineNumber, line := range doc.rawLines[startLine:] {
		matches := descriptionRegexp.FindStringSubmatch(line)

		// line must match description pattern
		lineMatchesDescriptionPattern := len(matches) == 1
		if lineMatchesDescriptionPattern {
			doc.Description = matches[0]
			return setContent(doc, lineNumber+1)
		}
	}

	return doc
}

func setContent(doc *Document, startLine int) *Document {
	if startLine > len(doc.rawLines) {
		return doc
	}

	content := ""

	for _, line := range doc.rawLines[startLine:] {
		content += line + "\n"
	}

	doc.Content = content

	return doc
}

func linesMeetCondition(lines []string, condition *regexp.Regexp) bool {

	for _, line := range lines {
		if !condition.MatchString(line) {
			return false
		}
	}

	return true
}
<commit_msg>Trying to implement another approach for the document parsing. Locating the different document components.<commit_after>package model

import (
	"regexp"
)

type Document struct {
	Title       string
	Description string
	Content     string
	Hash        string

	pattern  DocumentPattern
	rawLines []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:     repositoryItem.GetHash(),
		pattern:  NewDocumentPattern(),
		rawLines: repositoryItem.GetLines(),
	}

	// parse
	return doc.parse()
}

func (doc *Document) parse() *Document {
	return setTitle(doc)
}

type DocumentPattern struct {
	EmptyLine      regexp.Regexp
	Title          regexp.Regexp
	Description    regexp.Regexp
	HorizontalRule regexp.Regexp
	MetaData       regexp.Regexp
}

func NewDocumentPattern() DocumentPattern {
	emptyLineRegexp := regexp.MustCompile("^\\s*$")
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")
	descriptionRegexp := regexp.MustCompile("^\\w.+")
	horizontalRuleRegexp := regexp.MustCompile("^-{2,}")
	metaDataRegexp := regexp.MustCompile("^(\\w+):\\s*(\\w.+)$")

	return DocumentPattern{
		EmptyLine:      *emptyLineRegexp,
		Title:          *titleRegexp,
		Description:    *descriptionRegexp,
		HorizontalRule: *horizontalRuleRegexp,
		MetaData:       *metaDataRegexp,
	}
}

// Check if the current Document contains a title
func (doc *Document) locateTitle() (found bool, lineNumber int) {

	// In order to be the "title" the line must either
	// be empty or match the title pattern.

	for lineNumber, line := range doc.rawLines {

		lineMatchesTitlePattern := doc.pattern.Title.MatchString(line)
		if lineMatchesTitlePattern {
			return true, lineNumber
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return false, 0
}

// Check if the current Document contains a description
func (doc *Document) locateDescription() (found bool, lineNumber int) {

	// The description must be preceeded by a title
	titleExists, titleLineNumber := doc.locateTitle()
	if !titleExists {
		return false, 0
	}

	// If the document has no more lines than the line
	// in which the title has been located, there
	// will be no room for a description
	startLine := titleLineNumber + 1
	if len(doc.rawLines) <= startLine {
		return false, 0
	}

	// In order to be a "description" the line must either
	// be empty or match the description pattern.
	for lineNumber, line := range doc.rawLines[startLine:] {

		lineMatchesDescriptionPattern := doc.pattern.Description.MatchString(line)
		if lineMatchesDescriptionPattern {
			return true, lineNumber
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			break
		}
	}

	return false, 0
}

// Check if the current Document contains meta data
func (doc *Document) locateMetaData() (found bool, lineNumber int) {

	// Find the last horizontal rule in the document
	lastFoundHorizontalRulePosition := -1
	for lineNumber, line := range doc.rawLines {

		lineMatchesHorizontalRulePattern := doc.pattern.HorizontalRule.MatchString(line)
		if lineMatchesHorizontalRulePattern {
			lastFoundHorizontalRulePosition = lineNumber
		}

	}

	// If there is no horizontal rule there is no meta data
	if lastFoundHorizontalRulePosition == -1 {
		return false, 0
	}

	// If the document has no more lines than
	// the last found horizontal rule there is no
	// room for meta data
	metaDataStartLine := lastFoundHorizontalRulePosition + 1
	if len(doc.rawLines) <= metaDataStartLine {
		return false, 0
	}

	// Check if the last horizontal rule is followed
	// either by white space or be meta data
	for lineNumber, line := range doc.rawLines[metaDataStartLine:] {

		lineMatchesMetaDataPattern := doc.pattern.MetaData.MatchString(line)
		if lineMatchesMetaDataPattern {
			return true, metaDataStartLine
		}

		lineIsEmpty := doc.pattern.EmptyLine.MatchString(line)
		if !lineIsEmpty {
			return false, 0
		}

	}

	return false, 0
}

// Check if the current Document contains content
func (doc *Document) locateContent() (found bool, startLine int, endLine int) {

	// Content must be preceeded by a description
	descriptionExists, descriptionLineNumber := doc.locateDescription()
	if !descriptionExists {
		return false, 0, 0
	}

	// If the document has no more lines than the line
	// in which the description has been located, there
	// will be no room for content
	startLine = descriptionLineNumber + 1
	if len(doc.rawLines) <= startLine {
		return false, 0, 0
	}

	// If the document contains meta data
	// the content will be between the description
	// and the meta data. If not the content
	// will go up to the end of the document.
	endLine = 0
	metaDataExists, metaDataLineNumber := doc.locateMetaData()
	if metaDataExists {
		endLine = metaDataLineNumber - 1
	} else {
		endLine = len(doc.rawLines)
	}

	// All lines between the start- and endLine are content
	return true, startLine, endLine
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"

	"github.com/ikeikeikeike/gopkg/convert"
)

type Ranking struct {
	Id int64 `orm:"auto"`

	BeginName string    `orm:"size(255);index"` // dayly, weekly, monthly, yearly
	BeginTime time.Time `orm:"type(datetime);index"`

	Rank     int64 `orm:"default(0);index"` // rank order number
	PageView int64 `orm:"default(0);index"` // 1,2,3,4

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Entry *Entry `orm:"rel(fk);index"`
}

func (m *Ranking) LoadRelated() *Ranking {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Entry", 2, DefaultPerEntities)
	return m
}

func (m *Ranking) RelLoader() {
	m.LoadRelated()
}

func (m *Ranking) IdStr() string {
	return convert.ToStr(m.Id)
}

func (m *Ranking) Insert() error {
	if _, err := orm.NewOrm().Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *Ranking) Read(fields ...string) error {
	if err := orm.NewOrm().Read(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Ranking) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

func (m *Ranking) Update(fields ...string) error {
	if _, err := orm.NewOrm().Update(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Ranking) Delete() error {
	if _, err := orm.NewOrm().Delete(m); err != nil {
		return err
	}
	return nil
}

func Rankings() orm.QuerySeter {
	return orm.NewOrm().QueryTable("ranking").OrderBy("-Id")
}

func init() {
	orm.RegisterModelWithPrefix(
		beego.AppConfig.String("dbprefix"),
		new(Ranking))
}
<commit_msg>ranking model for each sites<commit_after>package models

import (
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type EntryRanking struct {
	Id int64 `orm:"auto"`

	BeginName string    `orm:"size(255);index" valid:"Required;Match(/^(dayly|weekly|monthly|yearly)$/)"` // dayly, weekly, monthly, yearly
	BeginTime time.Time `orm:"type(datetime);index"`

	Rank     int64 `orm:"default(0);index"` // rank order number
	PageView int64 `orm:"default(0);index"` // 1,2,3,4

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Entry *Entry `orm:"rel(fk);index"`
}

func (m *EntryRanking) LoadRelated() *EntryRanking {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Entry", 2, DefaultPerEntities)
	return m
}

func (m *EntryRanking) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

type VideoRanking struct {
	Id int64 `orm:"auto"`

	BeginName string    `orm:"size(255);index" valid:"Required;Match(/^(dayly|weekly|monthly|yearly)$/)"` // dayly, weekly, monthly, yearly
	BeginTime time.Time `orm:"type(datetime);index"`

	Rank     int64 `orm:"default(0);index"` // rank order number
	PageView int64 `orm:"default(0);index"` // 1,2,3,4

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Video *Video `orm:"rel(fk);index"`
}

func (m *VideoRanking) LoadRelated() *VideoRanking {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Video", 2, DefaultPerEntities)
	return m
}
func (m *VideoRanking) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

type PictureRanking struct {
	Id int64 `orm:"auto"`

	BeginName string    `orm:"size(255);index" valid:"Required;Match(/^(dayly|weekly|monthly|yearly)$/)"` // dayly, weekly, monthly, yearly
	BeginTime time.Time `orm:"type(datetime);index"`

	Rank     int64 `orm:"default(0);index"` // rank order number
	PageView int64 `orm:"default(0);index"` // 1,2,3,4

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Picture *Picture `orm:"rel(fk);index"`
}

func (m *PictureRanking) LoadRelated() *PictureRanking {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Picture", 2, DefaultPerEntities)
	return m
}
func (m *PictureRanking) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

func EntryRankings() orm.QuerySeter {
	return orm.NewOrm().QueryTable("entry_ranking").OrderBy("-Id")
}

func VideoRankings() orm.QuerySeter {
	return orm.NewOrm().QueryTable("video_ranking").OrderBy("-Id")
}

func PictureRankings() orm.QuerySeter {
	return orm.NewOrm().QueryTable("picture_ranking").OrderBy("-Id")
}

func init() {
	orm.RegisterModelWithPrefix(beego.AppConfig.String("dbprefix"), new(EntryRanking))
	orm.RegisterModelWithPrefix(beego.AppConfig.String("dbprefix"), new(VideoRanking))
	orm.RegisterModelWithPrefix(beego.AppConfig.String("dbprefix"), new(PictureRanking))
}
<|endoftext|>


<commit_before>package gompcreader

import (
	"testing"
	"time"
)

func TestReadString(t *testing.T) {
	buffer := []byte("fish and chips ")
	var result = readString(buffer)
	if result != "fish and chips" {
		t.Errorf("readString = %s, want \"fish and chips\"", result)
	}
}

func TestReadFloat(t *testing.T) {
	buffer := []byte("12.8567 ")
	var result = readFloat(buffer)
	if result != 12.8567 {
		t.Errorf("readFloat = %d, want %d", result, 12.8567)
	}
}

func TestReadInt(t *testing.T) {
	buffer := []byte("128 ")
	var result = readInt(buffer)
	if result != 128 {
		t.Errorf("readInt = %d, want %d", result, 128)
	}
}

func TestReadPackedInt(t *testing.T) {
	buffer := []byte("a128 ")
	var result = readPackedInt(buffer)
	if result != 36128 {
		t.Errorf("readInt = %d, want %d", result, 36128)
	}
}

func TestReadPackedDate(t *testing.T) {
	buffer := []byte("I23AP")
	var result = readPackedTime(buffer)
	if !time.Date(1823, 10, 25, 0, 0, 0, 0, time.UTC).Equal(result) {
		t.Errorf("readPackedTime = %s", result.Format("2006-01-02T03:04:00"))
	}
}

func TestReadPackedIdentifier(t *testing.T) {
	buffer := []byte("PLS2040")
	var result = readPackedIdentifier(buffer)
	if result != "2040 P-L" {
		t.Errorf("readPackedIdentifier = %s should be 2040 P-L", result)
	}

	buffer = []byte("T1S3138")
	result = readPackedIdentifier(buffer)
	if result != "3138 T-1" {
		t.Errorf("readPackedIdentifier = %s should be 3138 T-1", result)
	}

	buffer = []byte("J95X00A")
	result = readPackedIdentifier(buffer)
	if result != "1995 XA" {
		t.Errorf("readPackedIdentifier = %s should be 1995 XA", result)
	}

	buffer = []byte("A0001")
	result = readPackedIdentifier(buffer)
	if result != "100001" {
		t.Errorf("readPackedIdentifier = %s should be 100001", result)
	}
}
<commit_msg>swap byte[] to string in tests<commit_after>package gompcreader

import (
	"testing"
	"time"
)

func TestReadString(t *testing.T) {
	buffer := "fish and chips "
	var result = readString(buffer)
	if result != "fish and chips" {
		t.Errorf("readString = %s, want \"fish and chips\"", result)
	}
}

func TestReadFloat(t *testing.T) {
	buffer := "12.8567 "
	var result = readFloat(buffer)
	if result != 12.8567 {
		t.Errorf("readFloat = %d, want %d", result, 12.8567)
	}
}

func TestReadInt(t *testing.T) {
	buffer := "128 "
	var result = readInt(buffer)
	if result != 128 {
		t.Errorf("readInt = %d, want %d", result, 128)
	}
}

func TestReadPackedInt(t *testing.T) {
	buffer := "a128 "
	var result = readPackedInt(buffer)
	if result != 36128 {
		t.Errorf("readInt = %d, want %d", result, 36128)
	}
}

func TestReadPackedDate(t *testing.T) {
	buffer := "I23AP"
	var result = readPackedTime(buffer)
	if !time.Date(1823, 10, 25, 0, 0, 0, 0, time.UTC).Equal(result) {
		t.Errorf("readPackedTime = %s", result.Format("2006-01-02T03:04:00"))
	}
}

func TestReadPackedIdentifier(t *testing.T) {
	buffer := "PLS2040"
	var result = readPackedIdentifier(buffer)
	if result != "2040 P-L" {
		t.Errorf("readPackedIdentifier = %s should be 2040 P-L", result)
	}

	buffer = "T1S3138"
	result = readPackedIdentifier(buffer)
	if result != "3138 T-1" {
		t.Errorf("readPackedIdentifier = %s should be 3138 T-1", result)
	}

	buffer = "J95X00A"
	result = readPackedIdentifier(buffer)
	if result != "1995 XA" {
		t.Errorf("readPackedIdentifier = %s should be 1995 XA", result)
	}

	buffer = "A0001"
	result = readPackedIdentifier(buffer)
	if result != "100001" {
		t.Errorf("readPackedIdentifier = %s should be 100001", result)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"image/color"
	"strconv"
	"strings"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/gopherjs/files"
	"github.com/MJKWoolnough/gopherjs/progress"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
	"honnef.co/go/js/dom"

	"github.com/MJKWoolnough/gopherjs/overlay"
)

func servers(c dom.Element) {
	xjs.RemoveChildren(c)
	serversDiv := xjs.CreateElement("div")
	defer c.AppendChild(serversDiv)
	list, err := RPC.ServerList()
	if err != nil {
		xjs.SetInnerText(serversDiv, err.Error())
		return
	}
	newButton := xjs.CreateElement("input")
	newButton.SetAttribute("value", "New Server")
	newButton.SetAttribute("type", "button")
	newButton.AddEventListener("click", false, newServer(c))
	c.AppendChild(newButton)
	for _, s := range list {
		sd := xjs.CreateElement("div")
		xjs.SetInnerText(sd, s.Name)
		sd.AddEventListener("click", false, viewServer(c, sd, s))
		serversDiv.AppendChild(sd)
	}
	c.AppendChild(serversDiv)
}

func newServer(c dom.Element) func(dom.Event) {
	return func(dom.Event) {
		f := xjs.CreateElement("div")
		o := overlay.New(f)
		f.SetAttribute("id", "serverUpload")

		f.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "New Server"))

		nameLabel := xjs.CreateElement("label")
		nameLabel.SetAttribute("for", "name")
		xjs.SetInnerText(nameLabel, "Level Name")
		nameInput := xjs.CreateElement("input").(*dom.HTMLInputElement)
		nameInput.SetAttribute("type", "text")
		nameInput.SetID("name")

		urlLabel := xjs.CreateElement("label")
		urlLabel.SetAttribute("for", "url")
		xjs.SetInnerText(urlLabel, "URL")
		urlInput := xjs.CreateElement("input")
		urlInput.SetAttribute("type", "radio")
		urlInput.SetAttribute("name", "type")
		urlInput.SetID("url")
		urlInput.SetAttribute("checked", "true")

		uploadLabel := xjs.CreateElement("label")
		uploadLabel.SetAttribute("for", "upload")
		xjs.SetInnerText(uploadLabel, "Upload")
		uploadInput := xjs.CreateElement("input")
		uploadInput.SetAttribute("type", "radio")
		uploadInput.SetAttribute("name", "type")
		uploadInput.SetID("upload")

		fileLabel := xjs.CreateElement("label")
		fileLabel.SetAttribute("for", "file")
		xjs.SetInnerText(fileLabel, "File")
		fileInput := xjs.CreateElement("input").(*dom.HTMLInputElement)
		fileInput.SetAttribute("type", "text")
		fileInput.SetID("file")

		urlInput.AddEventListener("click", false, func(dom.Event) {
			fileInput.SetAttribute("type", "text")
		})

		uploadInput.AddEventListener("click", false, func(dom.Event) {
			fileInput.SetAttribute("type", "file")
		})

		submit := xjs.CreateElement("input")
		submit.SetAttribute("value", "Submit")
		submit.SetAttribute("type", "button")

		submit.AddEventListener("click", false, func(e dom.Event) {
			name := nameInput.Value
			if len(name) == 0 {
				dom.GetWindow().Alert("Name cannot be empty")
				return
			}
			var file readLener
			uploadType := uint8(3)
			if fileInput.GetAttribute("type") == "file" {
				uploadType = 4
				fs := fileInput.Files()
				if len(fs) != 1 {
					dom.GetWindow().Alert("File Error occurred")
					return
				}
				f := files.NewFile(fs[0])
				file = files.NewFileReader(f)
			} else {
				url := fileInput.Value
				if len(url) == 0 {
					dom.GetWindow().Alert("URL cannot be empty")
					return
				}
				file = strings.NewReader(url)
			}
			length := file.Len()
			status := xjs.CreateElement("div")
			pb := progress.New(color.RGBA{255, 0, 0, 0}, color.RGBA{0, 0, 255, 0}, 400, 50)
			xjs.RemoveChildren(f)
			f.AppendChild(status)
			f.AppendChild(pb)

			go func() {
				conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/upload")
				if err != nil {
					xjs.SetInnerText(status, err.Error())
					return
				}
				defer removeCloser(closeOnExit(conn))
				defer conn.Close()
				o.OnClose(func() { conn.Close() })

				w := &byteio.StickyWriter{Writer: &byteio.LittleEndianWriter{Writer: conn}}
				xjs.SetInnerText(status, "Uploading Data...")
				uploadFile(uploadType, pb.Reader(file, length), w)
				if w.Err != nil {
					xjs.SetInnerText(status, w.Err.Error())
					return
				}

				r := &byteio.StickyReader{Reader: &byteio.LittleEndianReader{conn}}

				if r.ReadUint8() == 0 {
					xjs.SetInnerText(status, readError(r).Error())
					return
				}

				f.RemoveChild(pb)
				xjs.SetInnerText(status, "Checking Zip...")

				w.WriteUint8(uint8(len(name)))
				w.Write([]byte(name))
				for {
					switch r.ReadUint8() {
					case 1:
						numJars := r.ReadInt16()
						jars := make([]string, numJars)
						for i := int16(0); i < numJars; i++ {
							jars[i] = readString(r)
						}
						if r.Err != nil {
							xjs.SetInnerText(status, r.Err.Error())
						}

						c := make(chan int16, 1)

						jarSelect := xjs.CreateElement("div")
						jso := overlay.New(jarSelect)
						selected := false
						jso.OnClose(func() {
							if !selected {
								selected = true
								c <- -1
							}
						})

						jarSelect.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Select Server JAR"))
						radios := make([]*dom.HTMLInputElement, numJars)

						for num, name := range jars {
							r := xjs.CreateElement("input").(*dom.HTMLInputElement)
							r.SetAttribute("type", "radio")
							r.SetAttribute("name", "jarChoose")
							v := strconv.Itoa(num)
							r.SetAttribute("value", v)
							r.SetID("jarChoose_" + v)
							if num == 0 {
								r.DefaultChecked = true
							}

							l := xjs.CreateElement("label")
							xjs.SetInnerText(l, name)
							l.SetAttribute("for", "jarChoose_"+v)

							jarSelect.AppendChild(r)
							jarSelect.AppendChild(l)
							jarSelect.AppendChild(xjs.CreateElement("br"))
							radios[num] = r
						}

						choose := xjs.CreateElement("input")
						choose.SetAttribute("type", "button")
						choose.SetAttribute("value", "Select")
						choose.AddEventListener("click", false, func(dom.Event) {
							if !selected {
								selected = true
								choice := -1
								for num, r := range radios {
									if r.Checked {
										choice = num
										break
									}
								}

								c <- int16(choice)
								jso.Close()
							}
						})
						jarSelect.AppendChild(choose)
						f.AppendChild(jso)
						w.WriteInt16(<-c)
						close(c)
						if w.Err != nil {
							xjs.SetInnerText(status, w.Err.Error())
						}
					case 255:
						o.Close()
						servers(c)
						return
					default:
						xjs.SetInnerText(status, readError(r).Error())
						return
					}
				}
			}()
		})

		f.AppendChild(nameLabel)
		f.AppendChild(nameInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(urlLabel)
		f.AppendChild(urlInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(uploadLabel)
		f.AppendChild(uploadInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(fileLabel)
		f.AppendChild(fileInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(submit)

		dom.GetWindow().Document().DocumentElement().AppendChild(o)
	}
}

func viewServer(c, sd dom.Element, s Server) func(dom.Event) {
	return func(dom.Event) {
		go func() {
			m, err := RPC.GetMap(s.Map)
			if err != nil {
				dom.GetWindow().Alert(err.Error())
				return
			}
			d := xjs.CreateElement("div")
			od := overlay.New(d)
			d.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Server Details"))
			nameLabel := xjs.CreateElement("label")
			xjs.SetInnerText(nameLabel, "Name")
			name := xjs.CreateElement("input").(*dom.HTMLInputElement)
			name.Value = s.Name
			name.Type = "text"

			d.AppendChild(nameLabel)
			d.AppendChild(name)
			d.AppendChild(xjs.CreateElement("br"))

			argsLabel := xjs.CreateElement("label")
			xjs.SetInnerText(argsLabel, "Arguments")

			d.AppendChild(argsLabel)

			argSpans := make([]*dom.HTMLSpanElement, len(s.Args))

			for num, arg := range s.Args {
				a := xjs.CreateElement("span").(*dom.HTMLSpanElement)
				a.SetAttribute("contenteditable", "true")
				a.SetAttribute("class", "sizeableInput")
				a.SetTextContent(arg)
				argSpans[num] = a
				d.AppendChild(a)
			}

			remove := xjs.CreateElement("input").(*dom.HTMLInputElement)
			remove.Type = "button"
			remove.Value = "-"
			remove.AddEventListener("click", false, func(dom.Event) {
				if len(argSpans) > 0 {
					d.RemoveChild(argSpans[len(argSpans)-1])
					argSpans = argSpans[:len(argSpans)-1]
				}
			})
			add := xjs.CreateElement("input").(*dom.HTMLInputElement)
			add.Type = "button"
			add.Value = "+"
			add.AddEventListener("click", false, func(dom.Event) {
				a := xjs.CreateElement("span").(*dom.HTMLSpanElement)
				a.SetAttribute("contenteditable", "true")
				a.SetAttribute("class", "sizeableInput")
				argSpans = append(argSpans, a)
				d.InsertBefore(a, remove)
			})

			d.AppendChild(remove)
			d.AppendChild(add)
			d.AppendChild(xjs.CreateElement("br"))
			d.AppendChild(xjs.CreateElement("br"))

			submit := xjs.CreateElement("input").(*dom.HTMLInputElement)
			submit.Value = "Make Changes"
			submit.SetAttribute("type", "button")
			submit.AddEventListener("click", false, func(dom.Event) {
				go func() {
					args := make([]string, len(argSpans))
					for num, arg := range argSpans {
						args[num] = arg.TextContent()
					}
					n := name.Value
					err := RPC.SetServer(Server{
						ID:   s.ID,
						Name: n,
						Path: s.Path,
						Args: args,
					})
					if err == nil {
						od.Close()
						servers(c)
						return
					}
					xjs.RemoveChildren(d)
					errDiv := xjs.CreateElement("div")
					xjs.SetPreText(errDiv, err.Error())
					d.AppendChild(errDiv)
				}()
			})

			d.AppendChild(submit)

			d.AppendChild(xjs.CreateElement("br"))
			d.AppendChild(xjs.SetInnerText(xjs.CreateElement("label"), "Map"))
			if m.ID < 0 {
				d.AppendChild(xjs.SetInnerText(xjs.CreateElement("div"), "[Unassigned]"))
			} else {
				d.AppendChild(xjs.SetInnerText(xjs.CreateElement("div"), m.Name))
			}

			dom.GetWindow().Document().DocumentElement().AppendChild(od)
		}()
	}
}
<commit_msg>Modified servers layout and added simple control buttons<commit_after>package main

import (
	"image/color"
	"strconv"
	"strings"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/gopherjs/files"
	"github.com/MJKWoolnough/gopherjs/progress"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
	"honnef.co/go/js/dom"

	"github.com/MJKWoolnough/gopherjs/overlay"
)

func servers(c dom.Element) {
	xjs.RemoveChildren(c)
	serversDiv := xjs.CreateElement("div")
	defer c.AppendChild(serversDiv)
	list, err := RPC.ServerList()
	if err != nil {
		xjs.SetInnerText(serversDiv, err.Error())
		return
	}
	newButton := xjs.CreateElement("input")
	newButton.SetAttribute("value", "New Server")
	newButton.SetAttribute("type", "button")
	newButton.AddEventListener("click", false, newServer(c))
	c.AppendChild(newButton)
	table := xjs.CreateElement("table")
	head := xjs.CreateElement("tr")
	head.AppendChild(xjs.SetInnerText(xjs.CreateElement("th"), "Name"))
	head.AppendChild(xjs.SetInnerText(xjs.CreateElement("th"), "Status"))
	head.AppendChild(xjs.SetInnerText(xjs.CreateElement("th"), "Controls"))
	table.AppendChild(head)
	for _, s := range list {
		tr := xjs.CreateElement("tr")
		name := xjs.CreateElement("td")
		xjs.SetInnerText(name, s.Name)
		name.AddEventListener("click", false, viewServer(c, s))
		tr.AppendChild(name)
		status := xjs.CreateElement("td")
		xjs.SetInnerText(status, "")
		tr.AppendChild(status)
		controls := xjs.CreateElement("td")
		tr.AppendChild(controls)
		if s.Map >= 0 {
			b := xjs.CreateElement("input").(*dom.HTMLInputElement)
			b.Type = "button"
			if s.IsRunning() {
				b.Value = "Stop"
				b.AddEventListener("click", false, startServer(c, s))
			} else {
				b.Value = "Start"
				b.AddEventListener("click", false, stopServer(c, s))
			}
			controls.AppendChild(b)
		}
		table.AppendChild(tr)
	}
	serversDiv.AppendChild(table)
	c.AppendChild(serversDiv)
}

func startServer(c dom.Element, s Server) func(dom.Event) {
	return func(dom.Event) {

	}
}

func stopServer(c dom.Element, s Server) func(dom.Event) {
	return func(dom.Event) {

	}
}

func newServer(c dom.Element) func(dom.Event) {
	return func(dom.Event) {
		f := xjs.CreateElement("div")
		o := overlay.New(f)
		f.SetAttribute("id", "serverUpload")

		f.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "New Server"))

		nameLabel := xjs.CreateElement("label")
		nameLabel.SetAttribute("for", "name")
		xjs.SetInnerText(nameLabel, "Level Name")
		nameInput := xjs.CreateElement("input").(*dom.HTMLInputElement)
		nameInput.SetAttribute("type", "text")
		nameInput.SetID("name")

		urlLabel := xjs.CreateElement("label")
		urlLabel.SetAttribute("for", "url")
		xjs.SetInnerText(urlLabel, "URL")
		urlInput := xjs.CreateElement("input")
		urlInput.SetAttribute("type", "radio")
		urlInput.SetAttribute("name", "type")
		urlInput.SetID("url")
		urlInput.SetAttribute("checked", "true")

		uploadLabel := xjs.CreateElement("label")
		uploadLabel.SetAttribute("for", "upload")
		xjs.SetInnerText(uploadLabel, "Upload")
		uploadInput := xjs.CreateElement("input")
		uploadInput.SetAttribute("type", "radio")
		uploadInput.SetAttribute("name", "type")
		uploadInput.SetID("upload")

		fileLabel := xjs.CreateElement("label")
		fileLabel.SetAttribute("for", "file")
		xjs.SetInnerText(fileLabel, "File")
		fileInput := xjs.CreateElement("input").(*dom.HTMLInputElement)
		fileInput.SetAttribute("type", "text")
		fileInput.SetID("file")

		urlInput.AddEventListener("click", false, func(dom.Event) {
			fileInput.SetAttribute("type", "text")
		})

		uploadInput.AddEventListener("click", false, func(dom.Event) {
			fileInput.SetAttribute("type", "file")
		})

		submit := xjs.CreateElement("input")
		submit.SetAttribute("value", "Submit")
		submit.SetAttribute("type", "button")

		submit.AddEventListener("click", false, func(e dom.Event) {
			name := nameInput.Value
			if len(name) == 0 {
				dom.GetWindow().Alert("Name cannot be empty")
				return
			}
			var file readLener
			uploadType := uint8(3)
			if fileInput.GetAttribute("type") == "file" {
				uploadType = 4
				fs := fileInput.Files()
				if len(fs) != 1 {
					dom.GetWindow().Alert("File Error occurred")
					return
				}
				f := files.NewFile(fs[0])
				file = files.NewFileReader(f)
			} else {
				url := fileInput.Value
				if len(url) == 0 {
					dom.GetWindow().Alert("URL cannot be empty")
					return
				}
				file = strings.NewReader(url)
			}
			length := file.Len()
			status := xjs.CreateElement("div")
			pb := progress.New(color.RGBA{255, 0, 0, 0}, color.RGBA{0, 0, 255, 0}, 400, 50)
			xjs.RemoveChildren(f)
			f.AppendChild(status)
			f.AppendChild(pb)

			go func() {
				conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/upload")
				if err != nil {
					xjs.SetInnerText(status, err.Error())
					return
				}
				defer removeCloser(closeOnExit(conn))
				defer conn.Close()
				o.OnClose(func() { conn.Close() })

				w := &byteio.StickyWriter{Writer: &byteio.LittleEndianWriter{Writer: conn}}
				xjs.SetInnerText(status, "Uploading Data...")
				uploadFile(uploadType, pb.Reader(file, length), w)
				if w.Err != nil {
					xjs.SetInnerText(status, w.Err.Error())
					return
				}

				r := &byteio.StickyReader{Reader: &byteio.LittleEndianReader{conn}}

				if r.ReadUint8() == 0 {
					xjs.SetInnerText(status, readError(r).Error())
					return
				}

				f.RemoveChild(pb)
				xjs.SetInnerText(status, "Checking Zip...")

				w.WriteUint8(uint8(len(name)))
				w.Write([]byte(name))
				for {
					switch r.ReadUint8() {
					case 1:
						numJars := r.ReadInt16()
						jars := make([]string, numJars)
						for i := int16(0); i < numJars; i++ {
							jars[i] = readString(r)
						}
						if r.Err != nil {
							xjs.SetInnerText(status, r.Err.Error())
						}

						c := make(chan int16, 1)

						jarSelect := xjs.CreateElement("div")
						jso := overlay.New(jarSelect)
						selected := false
						jso.OnClose(func() {
							if !selected {
								selected = true
								c <- -1
							}
						})

						jarSelect.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Select Server JAR"))
						radios := make([]*dom.HTMLInputElement, numJars)

						for num, name := range jars {
							r := xjs.CreateElement("input").(*dom.HTMLInputElement)
							r.SetAttribute("type", "radio")
							r.SetAttribute("name", "jarChoose")
							v := strconv.Itoa(num)
							r.SetAttribute("value", v)
							r.SetID("jarChoose_" + v)
							if num == 0 {
								r.DefaultChecked = true
							}

							l := xjs.CreateElement("label")
							xjs.SetInnerText(l, name)
							l.SetAttribute("for", "jarChoose_"+v)

							jarSelect.AppendChild(r)
							jarSelect.AppendChild(l)
							jarSelect.AppendChild(xjs.CreateElement("br"))
							radios[num] = r
						}

						choose := xjs.CreateElement("input")
						choose.SetAttribute("type", "button")
						choose.SetAttribute("value", "Select")
						choose.AddEventListener("click", false, func(dom.Event) {
							if !selected {
								selected = true
								choice := -1
								for num, r := range radios {
									if r.Checked {
										choice = num
										break
									}
								}

								c <- int16(choice)
								jso.Close()
							}
						})
						jarSelect.AppendChild(choose)
						f.AppendChild(jso)
						w.WriteInt16(<-c)
						close(c)
						if w.Err != nil {
							xjs.SetInnerText(status, w.Err.Error())
						}
					case 255:
						o.Close()
						servers(c)
						return
					default:
						xjs.SetInnerText(status, readError(r).Error())
						return
					}
				}
			}()
		})

		f.AppendChild(nameLabel)
		f.AppendChild(nameInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(urlLabel)
		f.AppendChild(urlInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(uploadLabel)
		f.AppendChild(uploadInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(fileLabel)
		f.AppendChild(fileInput)
		f.AppendChild(xjs.CreateElement("br"))

		f.AppendChild(submit)

		dom.GetWindow().Document().DocumentElement().AppendChild(o)
	}
}

func viewServer(c dom.Element, s Server) func(dom.Event) {
	return func(dom.Event) {
		go func() {
			m, err := RPC.GetMap(s.Map)
			if err != nil {
				dom.GetWindow().Alert(err.Error())
				return
			}
			d := xjs.CreateElement("div")
			od := overlay.New(d)
			d.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Server Details"))
			nameLabel := xjs.CreateElement("label")
			xjs.SetInnerText(nameLabel, "Name")
			name := xjs.CreateElement("input").(*dom.HTMLInputElement)
			name.Value = s.Name
			name.Type = "text"

			d.AppendChild(nameLabel)
			d.AppendChild(name)
			d.AppendChild(xjs.CreateElement("br"))

			argsLabel := xjs.CreateElement("label")
			xjs.SetInnerText(argsLabel, "Arguments")

			d.AppendChild(argsLabel)

			argSpans := make([]*dom.HTMLSpanElement, len(s.Args))

			for num, arg := range s.Args {
				a := xjs.CreateElement("span").(*dom.HTMLSpanElement)
				a.SetAttribute("contenteditable", "true")
				a.SetAttribute("class", "sizeableInput")
				a.SetTextContent(arg)
				argSpans[num] = a
				d.AppendChild(a)
			}

			remove := xjs.CreateElement("input").(*dom.HTMLInputElement)
			remove.Type = "button"
			remove.Value = "-"
			remove.AddEventListener("click", false, func(dom.Event) {
				if len(argSpans) > 0 {
					d.RemoveChild(argSpans[len(argSpans)-1])
					argSpans = argSpans[:len(argSpans)-1]
				}
			})
			add := xjs.CreateElement("input").(*dom.HTMLInputElement)
			add.Type = "button"
			add.Value = "+"
			add.AddEventListener("click", false, func(dom.Event) {
				a := xjs.CreateElement("span").(*dom.HTMLSpanElement)
				a.SetAttribute("contenteditable", "true")
				a.SetAttribute("class", "sizeableInput")
				argSpans = append(argSpans, a)
				d.InsertBefore(a, remove)
			})

			d.AppendChild(remove)
			d.AppendChild(add)
			d.AppendChild(xjs.CreateElement("br"))
			d.AppendChild(xjs.CreateElement("br"))

			submit := xjs.CreateElement("input").(*dom.HTMLInputElement)
			submit.Value = "Make Changes"
			submit.SetAttribute("type", "button")
			submit.AddEventListener("click", false, func(dom.Event) {
				go func() {
					args := make([]string, len(argSpans))
					for num, arg := range argSpans {
						args[num] = arg.TextContent()
					}
					n := name.Value
					err := RPC.SetServer(Server{
						ID:   s.ID,
						Name: n,
						Path: s.Path,
						Args: args,
					})
					if err == nil {
						od.Close()
						servers(c)
						return
					}
					xjs.RemoveChildren(d)
					errDiv := xjs.CreateElement("div")
					xjs.SetPreText(errDiv, err.Error())
					d.AppendChild(errDiv)
				}()
			})

			d.AppendChild(submit)

			d.AppendChild(xjs.CreateElement("br"))
			d.AppendChild(xjs.SetInnerText(xjs.CreateElement("label"), "Map"))
			if m.ID < 0 {
				d.AppendChild(xjs.SetInnerText(xjs.CreateElement("div"), "[Unassigned]"))
			} else {
				d.AppendChild(xjs.SetInnerText(xjs.CreateElement("div"), m.Name))
			}

			dom.GetWindow().Document().DocumentElement().AppendChild(od)
		}()
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// Nest is a generic nesting function that
// will generate the appropariate prefix based
// on the nest level
func Nest(level int, msg string) (rtn string) {
	for index, line := range strings.Split(msg, "\n") {
		// skip the last new line at the end of the message
		// because we add the new line in on each Sprintf
		if index == len(strings.Split(msg, "\n")) - 1 && line == "" {
			continue
		}
		rtn += fmt.Sprintf("%s%s\n", GenerateNestedPrefix(level), shorten(level, line))
	}
	return
}

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	process := fmt.Sprintf(msg, v...)
	subLen := len(process) + len("+   >")

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("+ %s %s >\n", process, strings.Repeat("-", (maxLen-subLen)))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker(" ", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}

func shorten(level int, msg string) string {
	switch {
	case isProgress(msg):
		return shortenProgress(level, msg)
	case isWarning(msg):
		return shortenWarning(level, msg)
	}
	return msg
}

func isProgress(line string) bool {
	return len(line) == 80 && strings.HasSuffix(line, "-- >")
}

func shortenProgress(level int, msg string) string {
	suffix := fmt.Sprintf("%s >", strings.Repeat("-", (level * 2)))
	return strings.Replace(msg, suffix, " >", 1)
}

func isWarning(line string) bool {
	return line == "----------------------------------  WARNING  ----------------------------------"
}

func shortenWarning(level int, msg string) string {
	wrapper := strings.Repeat("-", level)
	return strings.Replace(msg, fmt.Sprintf("%s  WARNING  %s", wrapper, wrapper), "  WARNING  ", 1)
}
<commit_msg>update sub bullet<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// Nest is a generic nesting function that
// will generate the appropariate prefix based
// on the nest level
func Nest(level int, msg string) (rtn string) {
	for index, line := range strings.Split(msg, "\n") {
		// skip the last new line at the end of the message
		// because we add the new line in on each Sprintf
		if index == len(strings.Split(msg, "\n")) - 1 && line == "" {
			continue
		}
		rtn += fmt.Sprintf("%s%s\n", GenerateNestedPrefix(level), shorten(level, line))
	}
	return
}

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	process := fmt.Sprintf(msg, v...)
	subLen := len(process) + len("+   >")

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("+ %s %s >\n", process, strings.Repeat("-", (maxLen-subLen)))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker("  +", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}

func shorten(level int, msg string) string {
	switch {
	case isProgress(msg):
		return shortenProgress(level, msg)
	case isWarning(msg):
		return shortenWarning(level, msg)
	}
	return msg
}

func isProgress(line string) bool {
	return len(line) == 80 && strings.HasSuffix(line, "-- >")
}

func shortenProgress(level int, msg string) string {
	suffix := fmt.Sprintf("%s >", strings.Repeat("-", (level * 2)))
	return strings.Replace(msg, suffix, " >", 1)
}

func isWarning(line string) bool {
	return line == "----------------------------------  WARNING  ----------------------------------"
}

func shortenWarning(level int, msg string) string {
	wrapper := strings.Repeat("-", level)
	return strings.Replace(msg, fmt.Sprintf("%s  WARNING  %s", wrapper, wrapper), "  WARNING  ", 1)
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   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 cgroups

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync"

	systemdDbus "github.com/coreos/go-systemd/dbus"
	"github.com/godbus/dbus"
	specs "github.com/opencontainers/runtime-spec/specs-go"
)

const (
	SystemdDbus  Name = "systemd"
	defaultSlice      = "system.slice"
)

func Systemd() ([]Subsystem, error) {
	root, err := v1MountPoint()
	if err != nil {
		return nil, err
	}
	defaultSubsystems, err := defaults(root)
	if err != nil {
		return nil, err
	}
	s, err := NewSystemd(root)
	if err != nil {
		return nil, err
	}
	// make sure the systemd controller is added first
	return append([]Subsystem{s}, defaultSubsystems...), nil
}

func Slice(slice, name string) Path {
	if slice == "" {
		slice = defaultSlice
	}
	return func(subsystem Name) (string, error) {
		return filepath.Join(slice, name), nil
	}
}

func NewSystemd(root string) (*SystemdController, error) {
	return &SystemdController{
		root: root,
	}, nil
}

type SystemdController struct {
	mu   sync.Mutex
	root string
}

func (s *SystemdController) Name() Name {
	return SystemdDbus
}

func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error {
	conn, err := systemdDbus.New()
	if err != nil {
		return err
	}
	defer conn.Close()
	slice, name := splitName(path)
	properties := []systemdDbus.Property{
		systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
		systemdDbus.PropWants(slice),
		newProperty("DefaultDependencies", false),
		newProperty("Delegate", true),
		newProperty("MemoryAccounting", true),
		newProperty("CPUAccounting", true),
		newProperty("BlockIOAccounting", true),
	}
	ch := make(chan string)
	_, err = conn.StartTransientUnit(name, "replace", properties, ch)
	if err != nil {
		return err
	}
	<-ch
	return nil
}

func (s *SystemdController) Delete(path string) error {
	conn, err := systemdDbus.New()
	if err != nil {
		return err
	}
	defer conn.Close()
	_, name := splitName(path)
	ch := make(chan string)
	_, err = conn.StopUnit(name, "replace", ch)
	if err != nil {
		return err
	}
	<-ch
	return nil
}

func newProperty(name string, units interface{}) systemdDbus.Property {
	return systemdDbus.Property{
		Name:  name,
		Value: dbus.MakeVariant(units),
	}
}

func unitName(name string) string {
	return fmt.Sprintf("%s.slice", name)
}

func splitName(path string) (slice string, unit string) {
	slice, unit = filepath.Split(path)
	return strings.TrimSuffix(slice, "/"), unit
}
<commit_msg>systemd-239+ no longer allows delegate slice<commit_after>/*
   Copyright The containerd Authors.

   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 cgroups

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync"

	systemdDbus "github.com/coreos/go-systemd/dbus"
	"github.com/godbus/dbus"
	specs "github.com/opencontainers/runtime-spec/specs-go"
)

const (
	SystemdDbus  Name = "systemd"
	defaultSlice      = "system.slice"
)

var (
	canDelegate bool
	once sync.Once
)

func Systemd() ([]Subsystem, error) {
	root, err := v1MountPoint()
	if err != nil {
		return nil, err
	}
	defaultSubsystems, err := defaults(root)
	if err != nil {
		return nil, err
	}
	s, err := NewSystemd(root)
	if err != nil {
		return nil, err
	}
	// make sure the systemd controller is added first
	return append([]Subsystem{s}, defaultSubsystems...), nil
}

func Slice(slice, name string) Path {
	if slice == "" {
		slice = defaultSlice
	}
	return func(subsystem Name) (string, error) {
		return filepath.Join(slice, name), nil
	}
}

func NewSystemd(root string) (*SystemdController, error) {
	return &SystemdController{
		root: root,
	}, nil
}

type SystemdController struct {
	mu   sync.Mutex
	root string
}

func (s *SystemdController) Name() Name {
	return SystemdDbus
}

func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error {
	conn, err := systemdDbus.New()
	if err != nil {
		return err
	}
	defer conn.Close()
	slice, name := splitName(path)
	// We need to see if systemd can handle the delegate property
	// Systemd will return an error if it cannot handle delegate regardless
	// of its bool setting.
	checkDelegate := func() {
		canDelegate = true
		dlSlice := newProperty("Delegate", true)
		if _, err := conn.StartTransientUnit(slice, "testdelegate", []systemdDbus.Property{dlSlice}, nil); err != nil {
			if dbusError, ok := err.(dbus.Error); ok {
				// Starting with systemd v237, Delegate is not even a property of slices anymore,
				// so the D-Bus call fails with "InvalidArgs" error.
				if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
					canDelegate = false
				}
			}
		}

		conn.StopUnit(slice, "testDelegate", nil)
	}
	once.Do(checkDelegate)
	properties := []systemdDbus.Property{
		systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
		systemdDbus.PropWants(slice),
		newProperty("DefaultDependencies", false),
		newProperty("MemoryAccounting", true),
		newProperty("CPUAccounting", true),
		newProperty("BlockIOAccounting", true),
	}

	// If we can delegate, we add the property back in
	if canDelegate {
		properties = append(properties, newProperty("Delegate", true))
	}

	ch := make(chan string)
	_, err = conn.StartTransientUnit(name, "replace", properties, ch)
	if err != nil {
		return err
	}
	<-ch
	return nil
}

func (s *SystemdController) Delete(path string) error {
	conn, err := systemdDbus.New()
	if err != nil {
		return err
	}
	defer conn.Close()
	_, name := splitName(path)
	ch := make(chan string)
	_, err = conn.StopUnit(name, "replace", ch)
	if err != nil {
		return err
	}
	<-ch
	return nil
}

func newProperty(name string, units interface{}) systemdDbus.Property {
	return systemdDbus.Property{
		Name:  name,
		Value: dbus.MakeVariant(units),
	}
}

func unitName(name string) string {
	return fmt.Sprintf("%s.slice", name)
}

func splitName(path string) (slice string, unit string) {
	slice, unit = filepath.Split(path)
	return strings.TrimSuffix(slice, "/"), unit
}
<|endoftext|>