<commit_before>package module

import (
	"errors"
	"fmt"
	"io/ioutil"

	"github.com/dnaeon/gru/resource"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
)

// ErrMultipleImport error is returned if there are multiple import
// declarations in the same module
var ErrMultipleImport = errors.New("Multiple import declarations found")

// Import type represents a single import declaration from HCL/JSON
type ImportType struct {
	// Module names being imported
	Module []string
}

// Module type represents a collection of resources and module imports
type Module struct {
	// Name of the module
	Name string

	// Resources loaded from the module
	Resources []resource.Resource

	// Module imports
	ModuleImport ImportType
}

// New creates a new empty module
func New(name string) *Module {
	m := &Module{
		Name:      name,
		Resources: make([]resource.Resource, 0),
		ModuleImport: ImportType{
			Module: make([]string, 0),
		},
	}

	return m
}

// Load loads a module from the given HCL or JSON input
func Load(name, path string) (*Module, error) {
	m := New(name)

	input, err := ioutil.ReadFile(path)
	if err != nil {
		return m, err
	}

	// Parse configuration
	obj, err := hcl.Parse(string(input))
	if err != nil {
		return m, err
	}

	// Top-level node should be an object list
	root, ok := obj.Node.(*ast.ObjectList)
	if !ok {
		return m, fmt.Errorf("Missing root node in %s", path)
	}

	err = m.hclLoadImport(root)
	if err != nil {
		return m, err
	}

	err = m.hclLoadResources(root)
	if err != nil {
		return m, err
	}

	return m, nil
}

// hclLoadResources loads all resource declarations from the given HCL input
func (m *Module) hclLoadResources(root *ast.ObjectList) error {
	hclResources := root.Filter("resource")
	for _, item := range hclResources.Items {
		position := item.Val.Pos().String()

		// The item is expected to have exactly one key which
		// represents the resource type.
		if len(item.Keys) != 1 {
			e := fmt.Errorf("Invalid resource declaration found in %s:%s", m.Name, position)
			return e
		}

		// Get the resource type and create the actual resource
		resourceType := item.Keys[0].Token.Value().(string)
		provider, ok := resource.Get(resourceType)
		if !ok {
			e := fmt.Errorf("Unknown resource type '%s' found in %s:%s", resourceType, m.Name, position)
			return e
		}

		// Create the actual resource by calling it's provider
		r, err := provider(item)
		if err != nil {
			return err
		}

		m.Resources = append(m.Resources, r)
	}

	return nil
}

// hclLoadImport loads all import declarations from the given HCL input
func (m *Module) hclLoadImport(root *ast.ObjectList) error {
	hclImport := root.Filter("import")

	// We expect to have exactly one import declaration per module file
	if len(hclImport.Items) > 1 {
		return fmt.Errorf("Multiple import declarations found in %s", m.Name)
	}

	if len(hclImport.Items) == 0 {
		return nil
	}

	err := hcl.DecodeObject(&m.ModuleImport, hclImport.Items[0])
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>module.Load takes an io.Reader<commit_after>package module

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"

	"github.com/dnaeon/gru/resource"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
)

// ErrMultipleImport error is returned if there are multiple import
// declarations in the same module
var ErrMultipleImport = errors.New("Multiple import declarations found")

// Import type represents a single import declaration from HCL/JSON
type ImportType struct {
	// Module names being imported
	Module []string
}

// Module type represents a collection of resources and module imports
type Module struct {
	// Name of the module
	Name string

	// Resources loaded from the module
	Resources []resource.Resource

	// Module imports
	ModuleImport ImportType
}

// New creates a new empty module
func New(name string) *Module {
	m := &Module{
		Name:      name,
		Resources: make([]resource.Resource, 0),
		ModuleImport: ImportType{
			Module: make([]string, 0),
		},
	}

	return m
}

// Load loads a module from the given HCL or JSON input
func Load(name string, r io.Reader) (*Module, error) {
	m := New(name)

	input, err := ioutil.ReadAll(r)
	if err != nil {
		return m, err
	}

	// Parse configuration
	obj, err := hcl.Parse(string(input))
	if err != nil {
		return m, err
	}

	// Top-level node should be an object list
	root, ok := obj.Node.(*ast.ObjectList)
	if !ok {
		return m, fmt.Errorf("Missing root node in %s", name)
	}

	err = m.hclLoadImport(root)
	if err != nil {
		return m, err
	}

	err = m.hclLoadResources(root)
	if err != nil {
		return m, err
	}

	return m, nil
}

// hclLoadResources loads all resource declarations from the given HCL input
func (m *Module) hclLoadResources(root *ast.ObjectList) error {
	hclResources := root.Filter("resource")
	for _, item := range hclResources.Items {
		position := item.Val.Pos().String()

		// The item is expected to have exactly one key which
		// represents the resource type.
		if len(item.Keys) != 1 {
			e := fmt.Errorf("Invalid resource declaration found in %s:%s", m.Name, position)
			return e
		}

		// Get the resource type and create the actual resource
		resourceType := item.Keys[0].Token.Value().(string)
		provider, ok := resource.Get(resourceType)
		if !ok {
			e := fmt.Errorf("Unknown resource type '%s' found in %s:%s", resourceType, m.Name, position)
			return e
		}

		// Create the actual resource by calling it's provider
		r, err := provider(item)
		if err != nil {
			return err
		}

		m.Resources = append(m.Resources, r)
	}

	return nil
}

// hclLoadImport loads all import declarations from the given HCL input
func (m *Module) hclLoadImport(root *ast.ObjectList) error {
	hclImport := root.Filter("import")

	// We expect to have exactly one import declaration per module file
	if len(hclImport.Items) > 1 {
		return fmt.Errorf("Multiple import declarations found in %s", m.Name)
	}

	if len(hclImport.Items) == 0 {
		return nil
	}

	err := hcl.DecodeObject(&m.ModuleImport, hclImport.Items[0])
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"log"
	"net"
	"net/url"
	"os/exec"
	"syscall"
	"time"

	"github.com/aandryashin/selenoid/config"
)

type Driver struct {
	Service *config.Browser
}

func (d *Driver) StartWithCancel() (*url.URL, func(), error) {
	slice, ok := d.Service.Image.([]interface{})
	if !ok {
		return nil, nil, fmt.Errorf("configuration error: image is not an array: %v", d.Service.Image)
	}
	cmdLine := []string{}
	for _, c := range slice {
		if _, ok := c.(string); !ok {
			return nil, nil, fmt.Errorf("configuration error: value is not a string: %v", c)
		}
		cmdLine = append(cmdLine, c.(string))
	}
	if len(cmdLine) == 0 {
		return nil, nil, fmt.Errorf("configuration error: image is empty")
	}
	log.Println("Trying to allocate port")
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, fmt.Errorf("cannot bind to port: %v", err)
	}
	u := &url.URL{Scheme: "http", Host: l.Addr().String()}
	_, port, _ := net.SplitHostPort(l.Addr().String())
	log.Println("Available port is:", port)
	cmdLine = append(cmdLine, fmt.Sprintf("--port=%s", port))
	cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	l.Close()
	log.Println("Starting process:", cmdLine)
	s := time.Now()
	err = cmd.Start()
	if err != nil {
		e := fmt.Errorf("cannot start process %v: %v", cmdLine, err)
		log.Println(e)
		return nil, nil, e
	}
	err = wait(u.String(), 10*time.Second)
	if err != nil {
		stopProcess(cmd)
		return nil, nil, err
	}
	log.Printf("Process %d started in: %v\n", cmd.Process.Pid, time.Since(s))
	log.Println("Proxying requests to:", u.String())
	return u, func() { stopProcess(cmd) }, nil
}

func stopProcess(cmd *exec.Cmd) {
	log.Println("Terminating process", cmd.Process.Pid)
	pgid, err := syscall.Getpgid(cmd.Process.Pid)
	if err != nil {
		log.Println("cannot get process group id: %v", err)
		return
	}
	err = syscall.Kill(-pgid, syscall.SIGTERM)
	if err != nil {
		log.Println("cannot terminate process %d: %v", cmd.Process.Pid, err)
		return
	}
	log.Printf("Process %d terminated\n", cmd.Process.Pid)
}
<commit_msg>Removed platform depended code.<commit_after>package service

import (
	"fmt"
	"log"
	"net"
	"net/url"
	"os/exec"
	"time"

	"github.com/aandryashin/selenoid/config"
)

type Driver struct {
	Service *config.Browser
}

func (d *Driver) StartWithCancel() (*url.URL, func(), error) {
	slice, ok := d.Service.Image.([]interface{})
	if !ok {
		return nil, nil, fmt.Errorf("configuration error: image is not an array: %v", d.Service.Image)
	}
	cmdLine := []string{}
	for _, c := range slice {
		if _, ok := c.(string); !ok {
			return nil, nil, fmt.Errorf("configuration error: value is not a string: %v", c)
		}
		cmdLine = append(cmdLine, c.(string))
	}
	if len(cmdLine) == 0 {
		return nil, nil, fmt.Errorf("configuration error: image is empty")
	}
	log.Println("Trying to allocate port")
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, fmt.Errorf("cannot bind to port: %v", err)
	}
	u := &url.URL{Scheme: "http", Host: l.Addr().String()}
	_, port, _ := net.SplitHostPort(l.Addr().String())
	log.Println("Available port is:", port)
	cmdLine = append(cmdLine, fmt.Sprintf("--port=%s", port))
	cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
	l.Close()
	log.Println("Starting process:", cmdLine)
	s := time.Now()
	err = cmd.Start()
	if err != nil {
		e := fmt.Errorf("cannot start process %v: %v", cmdLine, err)
		log.Println(e)
		return nil, nil, e
	}
	err = wait(u.String(), 10*time.Second)
	if err != nil {
		stopProcess(cmd)
		return nil, nil, err
	}
	log.Printf("Process %d started in: %v\n", cmd.Process.Pid, time.Since(s))
	log.Println("Proxying requests to:", u.String())
	return u, func() { stopProcess(cmd) }, nil
}

func stopProcess(cmd *exec.Cmd) {
	log.Println("Terminating process", cmd.Process.Pid)
	err := cmd.Process.Kill()
	if err != nil {
		log.Println("cannot terminate process %d: %v", cmd.Process.Pid, err)
		return
	}
	log.Printf("Process %d terminated\n", cmd.Process.Pid)
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	logging "github.com/op/go-logging"
)

// startMaster starts the Service as master.
func (s *Service) startMaster(runner Runner) {
	// Check HTTP server port
	containerHTTPPort, _, err := s.getHTTPServerPort()
	if err != nil {
		s.log.Fatalf("Cannot find HTTP server info: %#v", err)
	}
	if !IsPortOpen(containerHTTPPort) {
		s.log.Fatalf("Port %d is already in use", containerHTTPPort)
	}

	// Start HTTP listener
	s.startHTTPServer()

	// Permanent loop:
	s.log.Infof("Serving as master with ID '%s' on %s:%d...", s.ID, s.OwnAddress, s.announcePort)

	if s.AgencySize == 1 {
		s.myPeers.Peers = []Peer{
			Peer{
				ID:         s.ID,
				Address:    s.OwnAddress,
				Port:       s.announcePort,
				PortOffset: 0,
				DataDir:    s.DataDir,
				HasAgent:   !s.isSingleMode(),
			},
		}
		s.myPeers.AgencySize = s.AgencySize
		s.saveSetup()
		s.log.Info("Starting service...")
		s.startRunning(runner)
		return
	}

	wg := sync.WaitGroup{}
	if s.StartLocalSlaves {
		// Start additional local slaves
		s.createAndStartLocalSlaves(&wg)
	} else {
		// Show commands needed to start slaves
		s.log.Infof("Waiting for %d servers to show up.\n", s.AgencySize)
		s.showSlaveStartCommands(runner)
	}

	for {
		time.Sleep(time.Second)
		select {
		case <-s.startRunningWaiter.Done():
			s.saveSetup()
			s.log.Info("Starting service...")
			s.startRunning(runner)
			return
		default:
		}
		if s.stop {
			break
		}
	}
	// Wait for any local slaves to return.
	wg.Wait()
}

// showSlaveStartCommands prints out the commands needed to start additional slaves.
func (s *Service) showSlaveStartCommands(runner Runner) {
	s.log.Infof("Use the following commands to start other servers:")
	fmt.Println()
	for index := 2; index <= s.AgencySize; index++ {
		port := ""
		if s.announcePort != s.MasterPort {
			port = strconv.Itoa(s.announcePort)
		}
		fmt.Println(runner.CreateStartArangodbCommand(index, s.OwnAddress, port))
		fmt.Println()
	}
}

// mustCreateIDLogger creates a logger that includes the given ID in each log line.
func (s *Service) mustCreateIDLogger(id string) *logging.Logger {
	backend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	formattedBackend := logging.NewBackendFormatter(backend, logging.MustStringFormatter(fmt.Sprintf("[%s] %%{message}", id)))
	log := logging.MustGetLogger(s.log.Module)
	log.SetBackend(logging.AddModuleLevel(formattedBackend))
	return log
}
<commit_msg>Fixed IsSecure flag<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	logging "github.com/op/go-logging"
)

// startMaster starts the Service as master.
func (s *Service) startMaster(runner Runner) {
	// Check HTTP server port
	containerHTTPPort, _, err := s.getHTTPServerPort()
	if err != nil {
		s.log.Fatalf("Cannot find HTTP server info: %#v", err)
	}
	if !IsPortOpen(containerHTTPPort) {
		s.log.Fatalf("Port %d is already in use", containerHTTPPort)
	}

	// Start HTTP listener
	s.startHTTPServer()

	// Permanent loop:
	s.log.Infof("Serving as master with ID '%s' on %s:%d...", s.ID, s.OwnAddress, s.announcePort)

	if s.AgencySize == 1 {
		s.myPeers.Peers = []Peer{
			Peer{
				ID:         s.ID,
				Address:    s.OwnAddress,
				Port:       s.announcePort,
				PortOffset: 0,
				DataDir:    s.DataDir,
				HasAgent:   !s.isSingleMode(),
				IsSecure:   s.IsSecure(),
			},
		}
		s.myPeers.AgencySize = s.AgencySize
		s.saveSetup()
		s.log.Info("Starting service...")
		s.startRunning(runner)
		return
	}

	wg := sync.WaitGroup{}
	if s.StartLocalSlaves {
		// Start additional local slaves
		s.createAndStartLocalSlaves(&wg)
	} else {
		// Show commands needed to start slaves
		s.log.Infof("Waiting for %d servers to show up.\n", s.AgencySize)
		s.showSlaveStartCommands(runner)
	}

	for {
		time.Sleep(time.Second)
		select {
		case <-s.startRunningWaiter.Done():
			s.saveSetup()
			s.log.Info("Starting service...")
			s.startRunning(runner)
			return
		default:
		}
		if s.stop {
			break
		}
	}
	// Wait for any local slaves to return.
	wg.Wait()
}

// showSlaveStartCommands prints out the commands needed to start additional slaves.
func (s *Service) showSlaveStartCommands(runner Runner) {
	s.log.Infof("Use the following commands to start other servers:")
	fmt.Println()
	for index := 2; index <= s.AgencySize; index++ {
		port := ""
		if s.announcePort != s.MasterPort {
			port = strconv.Itoa(s.announcePort)
		}
		fmt.Println(runner.CreateStartArangodbCommand(index, s.OwnAddress, port))
		fmt.Println()
	}
}

// mustCreateIDLogger creates a logger that includes the given ID in each log line.
func (s *Service) mustCreateIDLogger(id string) *logging.Logger {
	backend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	formattedBackend := logging.NewBackendFormatter(backend, logging.MustStringFormatter(fmt.Sprintf("[%s] %%{message}", id)))
	log := logging.MustGetLogger(s.log.Module)
	log.SetBackend(logging.AddModuleLevel(formattedBackend))
	return log
}
<|endoftext|>


<commit_before>package lib

import (
	"bufio"
	"bytes"
	// "errors"
	// "log"
	"io"
	"os/exec"
)

type Callback func(line string)

func Shell(command string, callback Callback) error {
	cmd := exec.Command("/bin/sh", "-c", command)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
		//		log.Fatalf("Unable to stdoutpipe %s: %s", command, err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
		// log.Fatalf("Unable to stderrpipe %s: %s", command, err)
	}

	multi := io.MultiReader(stdout, stderr)

	scanner := bufio.NewScanner(multi)

	if err := cmd.Start(); err != nil {
		return err
		// log.Fatal("Unable to start command: ", err)
	}

	for scanner.Scan() {
		callback(scanner.Text())
	}

	// Wait for the result of the command; also closes our end of the pipe
	err = cmd.Wait()
	if err != nil {
		return err
		// log.Fatal("Unable to wait for process to finish: ", err)
	}

	return nil
}

func Pipeline(cmds ...*exec.Cmd) (pipeLineOutput, collectedStandardError []byte, pipeLineError error) {
	// Require at least one command
	if len(cmds) < 1 {
		return nil, nil, nil
	}

	// Collect the output from the command(s)
	var output bytes.Buffer
	var stderr bytes.Buffer

	last := len(cmds) - 1
	for i, cmd := range cmds[:last] {
		var err error
		// Connect each command's stdin to the previous command's stdout
		if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
			return nil, nil, err
		}
		// Connect each command's stderr to a buffer
		cmd.Stderr = &stderr
	}

	// Connect the output and error for the last command
	cmds[last].Stdout, cmds[last].Stderr = &output, &stderr

	// Start each command
	for _, cmd := range cmds {
		if err := cmd.Start(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Wait for each command to complete
	for _, cmd := range cmds {
		if err := cmd.Wait(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Return the pipeline output and the collected standard error
	return output.Bytes(), stderr.Bytes(), nil
}
<commit_msg>- Redirect stderr to a logging function providing by the calling party<commit_after>package lib

import (
	"bufio"
	"bytes"
	// "errors"
	// "log"
	"io"
	"os/exec"
)

type Callback func(line string)
type StderrWriter func(format string, a ...interface{})

type Streamer struct {
	buf    *bytes.Buffer
	writer StderrWriter
	prefix string
}

func NewStreamer(writer StderrWriter, prefix string) *Streamer {
	return &Streamer{
		buf:    bytes.NewBuffer([]byte("")),
		writer: writer,
		prefix: prefix,
	}
}

func (s *Streamer) Write(p []byte) (n int, err error) {
	if n, err = s.buf.Write(p); err != nil {
		return
	}

	for {
		var line string
		line, err = s.buf.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return
		}

		// l.readLines += line
		s.writer("%s: %s", s.prefix, line)
	}

	return
}

func Shell(command string, writer StderrWriter, prefix string, callback Callback) error {
	cmd := exec.Command("/bin/sh", "-c", command)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
		//		log.Fatalf("Unable to stdoutpipe %s: %s", command, err)
	}

	cmd.Stderr = NewStreamer(writer, prefix)

	// stderr, err := cmd.StderrPipe()
	// if err != nil {
	// 	return err
	// 	// log.Fatalf("Unable to stderrpipe %s: %s", command, err)
	// }

	// multi := io.MultiReader(stdout, stderr)

	scanner := bufio.NewScanner(stdout)

	if err := cmd.Start(); err != nil {
		return err
		// log.Fatal("Unable to start command: ", err)
	}

	for scanner.Scan() {
		callback(scanner.Text())
	}

	// Wait for the result of the command; also closes our end of the pipe
	err = cmd.Wait()
	if err != nil {
		return err
		// log.Fatal("Unable to wait for process to finish: ", err)
	}

	return nil
}

func Pipeline(cmds ...*exec.Cmd) (pipeLineOutput, collectedStandardError []byte, pipeLineError error) {
	// Require at least one command
	if len(cmds) < 1 {
		return nil, nil, nil
	}

	// Collect the output from the command(s)
	var output bytes.Buffer
	var stderr bytes.Buffer

	last := len(cmds) - 1
	for i, cmd := range cmds[:last] {
		var err error
		// Connect each command's stdin to the previous command's stdout
		if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
			return nil, nil, err
		}
		// Connect each command's stderr to a buffer
		cmd.Stderr = &stderr
	}

	// Connect the output and error for the last command
	cmds[last].Stdout, cmds[last].Stderr = &output, &stderr

	// Start each command
	for _, cmd := range cmds {
		if err := cmd.Start(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Wait for each command to complete
	for _, cmd := range cmds {
		if err := cmd.Wait(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Return the pipeline output and the collected standard error
	return output.Bytes(), stderr.Bytes(), nil
}
<|endoftext|>


<commit_before>// +build windows

/*
Copyright 2017 The Kubernetes 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 mount

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/golang/glog"
)

// Mounter provides the default implementation of mount.Interface
// for the windows platform.  This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type Mounter struct {
	mounterPath string
}

// New returns a mount.Interface for the current system.
// It provides options to override the default mounter behavior.
// mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface {
	return &Mounter{
		mounterPath: mounterPath,
	}
}

// Mount : mounts source to target as NTFS with given options.
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	target = normalizeWindowsPath(target)

	if source == "tmpfs" {
		glog.V(3).Infof("azureMount: mounting source (%q), target (%q), with options (%q)", source, target, options)
		return os.MkdirAll(target, 0755)
	}

	parentDir := filepath.Dir(target)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return err
	}

	glog.V(4).Infof("azureMount: mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
		options, source, target, fstype)
	bindSource := ""

	// tell it's going to mount azure disk or azure file according to options
	if bind, _ := isBind(options); bind {
		// mount azure disk
		bindSource = normalizeWindowsPath(source)
	} else {
		if len(options) < 2 {
			glog.Warningf("azureMount: mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
				options, len(options), source, target)
			return nil
		}

		// currently only cifs mount is supported
		if strings.ToLower(fstype) != "cifs" {
			return fmt.Errorf("azureMount: only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
		}

		cmdLine := fmt.Sprintf(`$User = "%s";$PWord = ConvertTo-SecureString -String "%s" -AsPlainText -Force;`+
			`$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord`,
			options[0], options[1])

		driverLetter, err := getAvailableDriveLetter()
		if err != nil {
			return err
		}
		bindSource = driverLetter + ":"
		cmdLine += fmt.Sprintf(";New-SmbGlobalMapping -LocalPath %s -RemotePath %s -Credential $Credential", bindSource, source)

		if output, err := exec.Command("powershell", "/c", cmdLine).CombinedOutput(); err != nil {
			// we don't return error here, even though New-SmbGlobalMapping failed, we still make it successful,
			// will return error when Windows 2016 RS3 is ready on azure
			glog.Errorf("azureMount: SmbGlobalMapping failed: %v, only SMB mount is supported now, output: %q", err, string(output))
			return os.MkdirAll(target, 0755)
		}
	}

	if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
		glog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
		return err
	}

	return nil
}

// Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error {
	glog.V(4).Infof("azureMount: Unmount target (%q)", target)
	target = normalizeWindowsPath(target)
	if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
		glog.Errorf("rmdir failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// List returns a list of all mounted filesystems. todo
func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

// IsMountPointMatch determines if the mountpoint matches the dir
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	return mp.Path == dir
}

// IsNotMountPoint determines if a directory is a mountpoint.
func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(mounter, dir)
}

// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	stat, err := os.Lstat(file)
	if err != nil {
		return true, err
	}
	// If current file is a symlink, then it is a mountpoint.
	if stat.Mode()&os.ModeSymlink != 0 {
		return false, nil
	}

	return true, nil
}

// GetDeviceNameFromMount given a mnt point, find the device
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(mounter, mountPath, pluginDir)
}

// DeviceOpened determines if the device is in use elsewhere
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
	return false, nil
}

// PathIsDevice determines if a path is a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
	return false, nil
}

// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. Empty implementation here.
func (mounter *Mounter) MakeRShared(path string) error {
	return nil
}

// GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	info, err := os.Stat(pathname)
	if os.IsNotExist(err) {
		return pathType, fmt.Errorf("path %q does not exist", pathname)
	}
	// err in call to os.Stat
	if err != nil {
		return pathType, err
	}

	mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
	switch mode & syscall.S_IFMT {
	case syscall.S_IFSOCK:
		return FileTypeSocket, nil
	case syscall.S_IFBLK:
		return FileTypeBlockDev, nil
	case syscall.S_IFCHR:
		return FileTypeCharDev, nil
	case syscall.S_IFDIR:
		return FileTypeDirectory, nil
	case syscall.S_IFREG:
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

// MakeFile creates a new directory
func (mounter *Mounter) MakeDir(pathname string) error {
	err := os.MkdirAll(pathname, os.FileMode(0755))
	if err != nil {
		if !os.IsExist(err) {
			return err
		}
	}
	return nil
}

// MakeFile creates an empty file
func (mounter *Mounter) MakeFile(pathname string) error {
	f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
	defer f.Close()
	if err != nil {
		if !os.IsExist(err) {
			return err
		}
	}
	return nil
}

// ExistsPath checks whether the path exists
func (mounter *Mounter) ExistsPath(pathname string) bool {
	_, err := os.Stat(pathname)
	if err != nil {
		return false
	}
	return true
}

func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
	// Try to mount the disk
	glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)

	if err := ValidateDiskNumber(source); err != nil {
		glog.Errorf("azureMount: formatAndMount failed, err: %v\n", err)
		return err
	}

	driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
	if err != nil {
		return err
	}
	driverPath := driveLetter + ":"
	target = normalizeWindowsPath(target)
	glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
	if output, err := mounter.Exec.Run("cmd", "/c", "mklink", "/D", target, driverPath); err != nil {
		glog.Errorf("mklink failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

func normalizeWindowsPath(path string) string {
	normalizedPath := strings.Replace(path, "/", "\\", -1)
	if strings.HasPrefix(normalizedPath, "\\") {
		normalizedPath = "c:" + normalizedPath
	}
	return normalizedPath
}

func getAvailableDriveLetter() (string, error) {
	cmd := "$used = Get-PSDrive | Select-Object -Expand Name | Where-Object { $_.Length -eq 1 }"
	cmd += ";$drive = 67..90 | ForEach-Object { [string][char]$_ } | Where-Object { $used -notcontains $_ } | Select-Object -First 1;$drive"
	output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("getAvailableDriveLetter failed: %v, output: %q", err, string(output))
	}

	if len(output) == 0 {
		return "", fmt.Errorf("azureMount: there is no available drive letter now")
	}
	return string(output)[:1], nil
}

// ValidateDiskNumber : disk number should be a number in [0, 99]
func ValidateDiskNumber(disk string) error {
	diskNum, err := strconv.Atoi(disk)
	if err != nil {
		return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
	}

	if diskNum < 0 || diskNum > 99 {
		return fmt.Errorf("disk number out of range: %q", disk)
	}

	return nil
}

// Get drive letter according to windows disk number
func getDriveLetterByDiskNumber(diskNum string, exec Exec) (string, error) {
	cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
	}
	if len(string(output)) < 1 {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
	}
	return string(output)[:1], nil
}
<commit_msg>not necessary to use disk letter in azure file mount<commit_after>// +build windows

/*
Copyright 2017 The Kubernetes 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 mount

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/golang/glog"
)

// Mounter provides the default implementation of mount.Interface
// for the windows platform.  This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type Mounter struct {
	mounterPath string
}

// New returns a mount.Interface for the current system.
// It provides options to override the default mounter behavior.
// mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface {
	return &Mounter{
		mounterPath: mounterPath,
	}
}

// Mount : mounts source to target as NTFS with given options.
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	target = normalizeWindowsPath(target)

	if source == "tmpfs" {
		glog.V(3).Infof("azureMount: mounting source (%q), target (%q), with options (%q)", source, target, options)
		return os.MkdirAll(target, 0755)
	}

	parentDir := filepath.Dir(target)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return err
	}

	glog.V(4).Infof("azureMount: mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
		options, source, target, fstype)
	bindSource := ""

	// tell it's going to mount azure disk or azure file according to options
	if bind, _ := isBind(options); bind {
		// mount azure disk
		bindSource = normalizeWindowsPath(source)
	} else {
		if len(options) < 2 {
			glog.Warningf("azureMount: mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
				options, len(options), source, target)
			return nil
		}

		// currently only cifs mount is supported
		if strings.ToLower(fstype) != "cifs" {
			return fmt.Errorf("azureMount: only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
		}

		cmdLine := fmt.Sprintf(`$User = "%s";$PWord = ConvertTo-SecureString -String "%s" -AsPlainText -Force;`+
			`$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord`,
			options[0], options[1])

		bindSource = source
		cmdLine += fmt.Sprintf(";New-SmbGlobalMapping -RemotePath %s -Credential $Credential", source)

		if output, err := exec.Command("powershell", "/c", cmdLine).CombinedOutput(); err != nil {
			// we don't return error here, even though New-SmbGlobalMapping failed, we still make it successful,
			// will return error when Windows 2016 RS3 is ready on azure
			glog.Errorf("azureMount: SmbGlobalMapping failed: %v, only SMB mount is supported now, output: %q", err, string(output))
			return os.MkdirAll(target, 0755)
		}
	}

	if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
		glog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
		return err
	}

	return nil
}

// Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error {
	glog.V(4).Infof("azureMount: Unmount target (%q)", target)
	target = normalizeWindowsPath(target)
	if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
		glog.Errorf("rmdir failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

// List returns a list of all mounted filesystems. todo
func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

// IsMountPointMatch determines if the mountpoint matches the dir
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	return mp.Path == dir
}

// IsNotMountPoint determines if a directory is a mountpoint.
func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(mounter, dir)
}

// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	stat, err := os.Lstat(file)
	if err != nil {
		return true, err
	}
	// If current file is a symlink, then it is a mountpoint.
	if stat.Mode()&os.ModeSymlink != 0 {
		return false, nil
	}

	return true, nil
}

// GetDeviceNameFromMount given a mnt point, find the device
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(mounter, mountPath, pluginDir)
}

// DeviceOpened determines if the device is in use elsewhere
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
	return false, nil
}

// PathIsDevice determines if a path is a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
	return false, nil
}

// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. Empty implementation here.
func (mounter *Mounter) MakeRShared(path string) error {
	return nil
}

// GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	info, err := os.Stat(pathname)
	if os.IsNotExist(err) {
		return pathType, fmt.Errorf("path %q does not exist", pathname)
	}
	// err in call to os.Stat
	if err != nil {
		return pathType, err
	}

	mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
	switch mode & syscall.S_IFMT {
	case syscall.S_IFSOCK:
		return FileTypeSocket, nil
	case syscall.S_IFBLK:
		return FileTypeBlockDev, nil
	case syscall.S_IFCHR:
		return FileTypeCharDev, nil
	case syscall.S_IFDIR:
		return FileTypeDirectory, nil
	case syscall.S_IFREG:
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

// MakeFile creates a new directory
func (mounter *Mounter) MakeDir(pathname string) error {
	err := os.MkdirAll(pathname, os.FileMode(0755))
	if err != nil {
		if !os.IsExist(err) {
			return err
		}
	}
	return nil
}

// MakeFile creates an empty file
func (mounter *Mounter) MakeFile(pathname string) error {
	f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
	defer f.Close()
	if err != nil {
		if !os.IsExist(err) {
			return err
		}
	}
	return nil
}

// ExistsPath checks whether the path exists
func (mounter *Mounter) ExistsPath(pathname string) bool {
	_, err := os.Stat(pathname)
	if err != nil {
		return false
	}
	return true
}

func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
	// Try to mount the disk
	glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)

	if err := ValidateDiskNumber(source); err != nil {
		glog.Errorf("azureMount: formatAndMount failed, err: %v\n", err)
		return err
	}

	driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
	if err != nil {
		return err
	}
	driverPath := driveLetter + ":"
	target = normalizeWindowsPath(target)
	glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
	if output, err := mounter.Exec.Run("cmd", "/c", "mklink", "/D", target, driverPath); err != nil {
		glog.Errorf("mklink failed: %v, output: %q", err, string(output))
		return err
	}
	return nil
}

func normalizeWindowsPath(path string) string {
	normalizedPath := strings.Replace(path, "/", "\\", -1)
	if strings.HasPrefix(normalizedPath, "\\") {
		normalizedPath = "c:" + normalizedPath
	}
	return normalizedPath
}

func getAvailableDriveLetter() (string, error) {
	cmd := "$used = Get-PSDrive | Select-Object -Expand Name | Where-Object { $_.Length -eq 1 }"
	cmd += ";$drive = 67..90 | ForEach-Object { [string][char]$_ } | Where-Object { $used -notcontains $_ } | Select-Object -First 1;$drive"
	output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("getAvailableDriveLetter failed: %v, output: %q", err, string(output))
	}

	if len(output) == 0 {
		return "", fmt.Errorf("azureMount: there is no available drive letter now")
	}
	return string(output)[:1], nil
}

// ValidateDiskNumber : disk number should be a number in [0, 99]
func ValidateDiskNumber(disk string) error {
	diskNum, err := strconv.Atoi(disk)
	if err != nil {
		return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
	}

	if diskNum < 0 || diskNum > 99 {
		return fmt.Errorf("disk number out of range: %q", disk)
	}

	return nil
}

// Get drive letter according to windows disk number
func getDriveLetterByDiskNumber(diskNum string, exec Exec) (string, error) {
	cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
	}
	if len(string(output)) < 1 {
		return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
	}
	return string(output)[:1], nil
}
<|endoftext|>


<commit_before>package mpool

import (
	"sync/atomic"
)

// Message encapsulates the messages that we exchange back and forth.
type Message struct {
	Body    []byte
	bodyBuf []byte

	slabSize int
	refCount int32
}

type messageSlab struct {
	maxBody int
	ch      chan *Message
}

var messagePool = []messageSlab{
	{maxBody: 256, ch: make(chan *Message, 1024)},  // 128K
	{maxBody: 1024, ch: make(chan *Message, 1024)}, // 1 MB
	{maxBody: 8192, ch: make(chan *Message, 256)},  // 2 MB
	{maxBody: 65536, ch: make(chan *Message, 64)},  // 4 MB
}

// Free decrements the reference count on a message, and releases its
// resources if no further references remain.  While this is not
// strictly necessary thanks to GC, doing so allows for the resources to
// be recycled without engaging GC.  This can have rather substantial
// benefits for performance.
func (this *Message) Free() (recycled bool) {
	refCount := atomic.AddInt32(&this.refCount, -1)
	if refCount > 0 {
		return false
	} else if refCount < 0 {
		// should never happen
		return true
	}

	// safe to put back message pool for later reuse
	var ch chan *Message
	for _, slab := range messagePool {
		if this.slabSize == slab.maxBody {
			ch = slab.ch
			break
		}
	}

	select {
	case ch <- this:
	default:
		// message pool is full, silently drop
	}
	return true
}

func (this *Message) Clone() *Message {
	atomic.AddInt32(&this.refCount, 1)
	return this
}

// NewMessage is the supported way to obtain a new Message.  This makes
// use of a "slab allocator" which greatly reduces the load on the
// garbage collector.
func NewMessage(sz int) *Message {
	var msg *Message
	var ch chan *Message
	for _, slab := range messagePool {
		if sz <= slab.maxBody {
			ch = slab.ch
			sz = slab.maxBody
			break
		}
	}

	select {
	case msg = <-ch:
	default:
		// message pool empty
		msg = &Message{}
		msg.slabSize = sz
		msg.bodyBuf = make([]byte, 0, msg.slabSize)
	}

	msg.refCount = 1
	msg.Body = msg.bodyBuf
	return msg
}
<commit_msg>discard the message refCount mechanism: it will not be cloned<commit_after>package mpool

// Message encapsulates the messages that we exchange back and forth.
type Message struct {
	Body    []byte
	bodyBuf []byte

	slabSize int
}

type messageSlab struct {
	maxBody int
	ch      chan *Message
}

var messagePool = []messageSlab{
	{maxBody: 256, ch: make(chan *Message, 1024)},  // 128K
	{maxBody: 1024, ch: make(chan *Message, 1024)}, // 1 MB
	{maxBody: 8192, ch: make(chan *Message, 256)},  // 2 MB
	{maxBody: 65536, ch: make(chan *Message, 64)},  // 4 MB
}

// Free decrements the reference count on a message, and releases its
// resources if no further references remain.  While this is not
// strictly necessary thanks to GC, doing so allows for the resources to
// be recycled without engaging GC.  This can have rather substantial
// benefits for performance.
func (this *Message) Free() (recycled bool) {
	var ch chan *Message
	for _, slab := range messagePool {
		if this.slabSize == slab.maxBody {
			ch = slab.ch
			break
		}
	}

	select {
	case ch <- this:
	default:
		// message pool is full, silently drop
	}
	return true
}

// NewMessage is the supported way to obtain a new Message.  This makes
// use of a "slab allocator" which greatly reduces the load on the
// garbage collector.
func NewMessage(sz int) *Message {
	var msg *Message
	var ch chan *Message
	for _, slab := range messagePool {
		if sz <= slab.maxBody {
			ch = slab.ch
			sz = slab.maxBody
			break
		}
	}

	select {
	case msg = <-ch:
	default:
		// message pool empty
		msg = &Message{}
		msg.slabSize = sz
		msg.bodyBuf = make([]byte, 0, msg.slabSize)
	}

	msg.Body = msg.bodyBuf
	return msg
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

var graphDef = map[string](mp.Graphs){
	"multicore.cpu.#": mp.Graphs{
		Label: "MultiCore CPU",
		Unit:  "percentage",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "user", Label: "user", Diff: false, Stacked: true},
			mp.Metrics{Name: "nice", Label: "nice", Diff: false, Stacked: true},
			mp.Metrics{Name: "system", Label: "system", Diff: false, Stacked: true},
			mp.Metrics{Name: "idle", Label: "idle", Diff: false, Stacked: true},
			mp.Metrics{Name: "iowait", Label: "ioWait", Diff: false, Stacked: true},
			mp.Metrics{Name: "irq", Label: "irq", Diff: false, Stacked: true},
			mp.Metrics{Name: "softirq", Label: "softirq", Diff: false, Stacked: true},
			mp.Metrics{Name: "steal", Label: "steal", Diff: false, Stacked: true},
			mp.Metrics{Name: "guest", Label: "guest", Diff: false, Stacked: true},
		},
	},
	"multicore.average_per_core": mp.Graphs{
		Label: "MultiCore CPU Average per core",
		Unit:  "percentage",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "user", Label: "user", Diff: false, Stacked: true},
			mp.Metrics{Name: "nice", Label: "nice", Diff: false, Stacked: true},
			mp.Metrics{Name: "system", Label: "system", Diff: false, Stacked: true},
			mp.Metrics{Name: "idle", Label: "idle", Diff: false, Stacked: true},
			mp.Metrics{Name: "iowait", Label: "ioWait", Diff: false, Stacked: true},
			mp.Metrics{Name: "irq", Label: "irq", Diff: false, Stacked: true},
			mp.Metrics{Name: "softirq", Label: "softirq", Diff: false, Stacked: true},
			mp.Metrics{Name: "steal", Label: "steal", Diff: false, Stacked: true},
			mp.Metrics{Name: "guest", Label: "guest", Diff: false, Stacked: true},
		},
	},
	"multicore.loadavg_per_core": mp.Graphs{
		Label: "MultiCore loadavg5 per core",
		Unit:  "float",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "loadavg5", Label: "loadavg5", Diff: false, Stacked: true},
		},
	},
}

type saveItem struct {
	LastTime       time.Time
	ProcStatsByCPU map[string]*procStats
}

type procStats struct {
	User    float64 `json:"user"`
	Nice    float64 `json:"nice"`
	System  float64 `json:"system"`
	Idle    float64 `json:"idle"`
	IoWait  float64 `json:"iowait"`
	Irq     float64 `json:"irq"`
	SoftIrq float64 `json:"softirq"`
	Steal   float64 `json:"steal"`
	Guest   float64 `json:"guest"`
	Total   float64 `json:"total"`
}

type cpuPercentages struct {
	User    float64
	Nice    float64
	System  float64
	Idle    float64
	IoWait  float64
	Irq     float64
	SoftIrq float64
	Steal   float64
	Guest   float64
}

func getProcStat() (string, error) {
	contentbytes, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		return "", err
	}
	return string(contentbytes), nil
}

func parseFloats(values []string) ([]float64, error) {
	var result []float64
	for _, v := range values {
		f, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		result = append(result, f)
	}
	return result, nil
}

func fill(arr []float64, elementCount int) []float64 {
	arraySize := len(arr)
	if arraySize < elementCount {
		for i := arraySize; i < elementCount; i++ {
			arr = append(arr, 0.0)
		}
	}
	return arr
}

func parseProcStat(str string) (map[string]*procStats, error) {
	var result = make(map[string]*procStats)
	for _, line := range strings.Split(str, "\n") {
		if strings.HasPrefix(line, "cpu") {
			fields := strings.Fields(line)
			key := fields[0]
			values := fields[1:]

			floatValues, err := parseFloats(values)
			if err != nil {
				return nil, err
			}
			filledValues := fill(floatValues, 9)

			total := 0.0
			for _, v := range floatValues {
				total += v
			}

			ps := &procStats{
				User:    filledValues[0],
				Nice:    filledValues[1],
				System:  filledValues[2],
				Idle:    filledValues[3],
				IoWait:  filledValues[4],
				Irq:     filledValues[5],
				SoftIrq: filledValues[6],
				Steal:   filledValues[7],
				Guest:   filledValues[8],
				Total:   total,
			}
			result[key] = ps

		} else {
			break
		}
	}
	return result, nil
}

func collectProcStatValues() (map[string]*procStats, error) {
	procStats, err := getProcStat()
	if err != nil {
		return nil, err
	}
	return parseProcStat(procStats)
}

func saveValues(tempFileName string, values map[string]*procStats, now time.Time) error {
	f, err := os.Create(tempFileName)
	if err != nil {
		return err
	}
	defer f.Close()

	s := saveItem{
		LastTime:       time.Now(),
		ProcStatsByCPU: values,
	}

	encoder := json.NewEncoder(f)
	err = encoder.Encode(s)
	if err != nil {
		return err
	}

	return nil
}

func fetchLastValues(tempFileName string) (map[string]*procStats, time.Time, error) {
	f, err := os.Open(tempFileName)
	if err != nil {
		return nil, time.Now(), err
	}
	defer f.Close()

	var stat saveItem
	decoder := json.NewDecoder(f)
	err = decoder.Decode(&stat)
	if err != nil {
		return stat.ProcStatsByCPU, stat.LastTime, err
	}
	return stat.ProcStatsByCPU, stat.LastTime, nil
}

func calcCPUUsage(currentValues map[string]*procStats, now time.Time, lastValues map[string]*procStats, lastTime time.Time) (map[string]*cpuPercentages, error) {

	result := make(map[string]*cpuPercentages)
	for key, current := range currentValues {
		last, ok := lastValues[key]
		if ok {
			user, err := calcPercentage(current.User, last.User, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			nice, err := calcPercentage(current.Nice, last.Nice, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			system, err := calcPercentage(current.System, last.System, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			idle, err := calcPercentage(current.Idle, last.Idle, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			iowait, err := calcPercentage(current.IoWait, last.IoWait, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			irq, err := calcPercentage(current.Irq, last.Irq, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			softirq, err := calcPercentage(current.SoftIrq, last.SoftIrq, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			steal, err := calcPercentage(current.Steal, last.Steal, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			guest, err := calcPercentage(current.Guest, last.Guest, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}

			p := &cpuPercentages{
				User:    user,
				Nice:    nice,
				System:  system,
				Idle:    idle,
				IoWait:  iowait,
				Irq:     irq,
				SoftIrq: softirq,
				Steal:   steal,
				Guest:   guest,
			}
			result[key] = p
		}

	}

	return result, nil
}

func calcPercentage(currentValue float64, lastValue float64, currentTotal float64, lastTotal float64, now time.Time, lastTime time.Time) (float64, error) {
	value, err := calcDiff(currentValue, now, lastValue, lastTime)
	if err != nil {
		return 0.0, err
	}

	total, err := calcDiff(currentTotal, now, lastTotal, lastTime)
	if err != nil {
		return 0.0, err
	}

	return (value / total * 100.0), nil
}

func calcDiff(value float64, now time.Time, lastValue float64, lastTime time.Time) (float64, error) {
	diffTime := now.Unix() - lastTime.Unix()
	if diffTime > 600 {
		return 0.0, errors.New("Too long duration")
	}

	diff := (value - lastValue) * 60 / float64(diffTime)

	if lastValue <= value {
		return diff, nil
	}
	return 0.0, nil
}

func fetchLoadavg5() (float64, error) {
	contentbytes, err := ioutil.ReadFile("/proc/loadavg")
	if err != nil {
		return 0.0, err
	}
	content := string(contentbytes)
	cols := strings.Fields(content)

	if len(cols) > 2 {
		f, err := strconv.ParseFloat(cols[1], 64)
		if err != nil {
			return 0.0, err
		}
		return f, nil
	}
	return 0.0, errors.New("cannot fetch loadavg5.")
}

func printValue(key string, value float64, time time.Time) {
	fmt.Printf("%s\t%f\t%d\n", key, value, time.Unix())
}

func outputCPUUsage(cpuUsage map[string]*cpuPercentages, now time.Time) {
	if cpuUsage != nil {
		for key, values := range cpuUsage {
			if key != "cpu" {
				printValue(fmt.Sprintf("multicore.cpu.%s.user", key), values.User, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.nice", key), values.Nice, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.system", key), values.System, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.idle", key), values.Idle, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.iowait", key), values.IoWait, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.irq", key), values.Irq, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.softirq", key), values.SoftIrq, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.steal", key), values.Steal, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.guest", key), values.Guest, now)
			}
		}
	}
}

func outputAverageCPUUsage(cpuUsage map[string]*cpuPercentages, now time.Time) {
	if cpuUsage != nil {
		values := cpuUsage["cpu"]
		printValue("multicore.average_per_core.user", values.User, now)
		printValue("multicore.average_per_core.nice", values.Nice, now)
		printValue("multicore.average_per_core.system", values.System, now)
		printValue("multicore.average_per_core.idle", values.Idle, now)
		printValue("multicore.average_per_core.iowait", values.IoWait, now)
		printValue("multicore.average_per_core.irq", values.Irq, now)
		printValue("multicore.average_per_core.softirq", values.SoftIrq, now)
		printValue("multicore.average_per_core.steal", values.Steal, now)
		printValue("multicore.average_per_core.guest", values.Guest, now)
	}
}

func outputLoadavgPerCore(loadavgPerCore float64, now time.Time) {
	printValue("multicore.loadavg_per_core.loadavg5", loadavgPerCore, now)
}

func outputDefinitions() {
	fmt.Println("# mackerel-agent-plugin")
	var graphs mp.GraphDef
	graphs.Graphs = graphDef

	b, err := json.Marshal(graphs)
	if err != nil {
		log.Fatalln("OutputDefinitions: ", err)
	}
	fmt.Println(string(b))
}

// main function
func main() {
	optTempfile := flag.String("tempfile", "", "Temp file name")
	var tempFileName string
	if *optTempfile != "" {
		tempFileName = *optTempfile
	} else {
		tempFileName = "/tmp/mackerel-plugin-multicore"
	}
	now := time.Now()

	currentValues, _ := collectProcStatValues()
	lastValues, lastTime, err := fetchLastValues(tempFileName)
	saveValues(tempFileName, currentValues, now)
	if err != nil {
		log.Fatalln("fetchLastValues: ", err)
	}

	cpuUsage := make(map[string]*cpuPercentages)
	if lastValues != nil {
		var err error
		cpuUsage, err = calcCPUUsage(currentValues, now, lastValues, lastTime)
		if err != nil {
			log.Fatalln("calcCPUUsage: ", err)
		}
	}

	loadavg5, err := fetchLoadavg5()
	if err != nil {
		log.Fatalln("fetchLoadavg5: ", err)
	}
	loadPerCPUCount := loadavg5 / (float64(len(cpuUsage) - 1))

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		outputDefinitions()
	} else {
		outputCPUUsage(cpuUsage, now)
		outputAverageCPUUsage(cpuUsage, now)
		outputLoadavgPerCore(loadPerCPUCount, now)
	}
}
<commit_msg>remove `multicore.average_per_core`<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

var graphDef = map[string](mp.Graphs){
	"multicore.cpu.#": mp.Graphs{
		Label: "MultiCore CPU",
		Unit:  "percentage",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "user", Label: "user", Diff: false, Stacked: true},
			mp.Metrics{Name: "nice", Label: "nice", Diff: false, Stacked: true},
			mp.Metrics{Name: "system", Label: "system", Diff: false, Stacked: true},
			mp.Metrics{Name: "idle", Label: "idle", Diff: false, Stacked: true},
			mp.Metrics{Name: "iowait", Label: "ioWait", Diff: false, Stacked: true},
			mp.Metrics{Name: "irq", Label: "irq", Diff: false, Stacked: true},
			mp.Metrics{Name: "softirq", Label: "softirq", Diff: false, Stacked: true},
			mp.Metrics{Name: "steal", Label: "steal", Diff: false, Stacked: true},
			mp.Metrics{Name: "guest", Label: "guest", Diff: false, Stacked: true},
		},
	},
	"multicore.loadavg_per_core": mp.Graphs{
		Label: "MultiCore loadavg5 per core",
		Unit:  "float",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "loadavg5", Label: "loadavg5", Diff: false, Stacked: true},
		},
	},
}

type saveItem struct {
	LastTime       time.Time
	ProcStatsByCPU map[string]*procStats
}

type procStats struct {
	User    float64 `json:"user"`
	Nice    float64 `json:"nice"`
	System  float64 `json:"system"`
	Idle    float64 `json:"idle"`
	IoWait  float64 `json:"iowait"`
	Irq     float64 `json:"irq"`
	SoftIrq float64 `json:"softirq"`
	Steal   float64 `json:"steal"`
	Guest   float64 `json:"guest"`
	Total   float64 `json:"total"`
}

type cpuPercentages struct {
	User    float64
	Nice    float64
	System  float64
	Idle    float64
	IoWait  float64
	Irq     float64
	SoftIrq float64
	Steal   float64
	Guest   float64
}

func getProcStat() (string, error) {
	contentbytes, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		return "", err
	}
	return string(contentbytes), nil
}

func parseFloats(values []string) ([]float64, error) {
	var result []float64
	for _, v := range values {
		f, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		result = append(result, f)
	}
	return result, nil
}

func fill(arr []float64, elementCount int) []float64 {
	arraySize := len(arr)
	if arraySize < elementCount {
		for i := arraySize; i < elementCount; i++ {
			arr = append(arr, 0.0)
		}
	}
	return arr
}

func parseProcStat(str string) (map[string]*procStats, error) {
	var result = make(map[string]*procStats)
	for _, line := range strings.Split(str, "\n") {
		if strings.HasPrefix(line, "cpu") {
			fields := strings.Fields(line)
			key := fields[0]
			values := fields[1:]

			floatValues, err := parseFloats(values)
			if err != nil {
				return nil, err
			}
			filledValues := fill(floatValues, 9)

			total := 0.0
			for _, v := range floatValues {
				total += v
			}

			ps := &procStats{
				User:    filledValues[0],
				Nice:    filledValues[1],
				System:  filledValues[2],
				Idle:    filledValues[3],
				IoWait:  filledValues[4],
				Irq:     filledValues[5],
				SoftIrq: filledValues[6],
				Steal:   filledValues[7],
				Guest:   filledValues[8],
				Total:   total,
			}
			result[key] = ps

		} else {
			break
		}
	}
	return result, nil
}

func collectProcStatValues() (map[string]*procStats, error) {
	procStats, err := getProcStat()
	if err != nil {
		return nil, err
	}
	return parseProcStat(procStats)
}

func saveValues(tempFileName string, values map[string]*procStats, now time.Time) error {
	f, err := os.Create(tempFileName)
	if err != nil {
		return err
	}
	defer f.Close()

	s := saveItem{
		LastTime:       time.Now(),
		ProcStatsByCPU: values,
	}

	encoder := json.NewEncoder(f)
	err = encoder.Encode(s)
	if err != nil {
		return err
	}

	return nil
}

func fetchLastValues(tempFileName string) (map[string]*procStats, time.Time, error) {
	f, err := os.Open(tempFileName)
	if err != nil {
		return nil, time.Now(), err
	}
	defer f.Close()

	var stat saveItem
	decoder := json.NewDecoder(f)
	err = decoder.Decode(&stat)
	if err != nil {
		return stat.ProcStatsByCPU, stat.LastTime, err
	}
	return stat.ProcStatsByCPU, stat.LastTime, nil
}

func calcCPUUsage(currentValues map[string]*procStats, now time.Time, lastValues map[string]*procStats, lastTime time.Time) (map[string]*cpuPercentages, error) {

	result := make(map[string]*cpuPercentages)
	for key, current := range currentValues {
		last, ok := lastValues[key]
		if ok {
			user, err := calcPercentage(current.User, last.User, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			nice, err := calcPercentage(current.Nice, last.Nice, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			system, err := calcPercentage(current.System, last.System, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			idle, err := calcPercentage(current.Idle, last.Idle, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			iowait, err := calcPercentage(current.IoWait, last.IoWait, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			irq, err := calcPercentage(current.Irq, last.Irq, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			softirq, err := calcPercentage(current.SoftIrq, last.SoftIrq, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			steal, err := calcPercentage(current.Steal, last.Steal, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}
			guest, err := calcPercentage(current.Guest, last.Guest, current.Total, last.Total, now, lastTime)
			if err != nil {
				return nil, err
			}

			p := &cpuPercentages{
				User:    user,
				Nice:    nice,
				System:  system,
				Idle:    idle,
				IoWait:  iowait,
				Irq:     irq,
				SoftIrq: softirq,
				Steal:   steal,
				Guest:   guest,
			}
			result[key] = p
		}

	}

	return result, nil
}

func calcPercentage(currentValue float64, lastValue float64, currentTotal float64, lastTotal float64, now time.Time, lastTime time.Time) (float64, error) {
	value, err := calcDiff(currentValue, now, lastValue, lastTime)
	if err != nil {
		return 0.0, err
	}

	total, err := calcDiff(currentTotal, now, lastTotal, lastTime)
	if err != nil {
		return 0.0, err
	}

	return (value / total * 100.0), nil
}

func calcDiff(value float64, now time.Time, lastValue float64, lastTime time.Time) (float64, error) {
	diffTime := now.Unix() - lastTime.Unix()
	if diffTime > 600 {
		return 0.0, errors.New("Too long duration")
	}

	diff := (value - lastValue) * 60 / float64(diffTime)

	if lastValue <= value {
		return diff, nil
	}
	return 0.0, nil
}

func fetchLoadavg5() (float64, error) {
	contentbytes, err := ioutil.ReadFile("/proc/loadavg")
	if err != nil {
		return 0.0, err
	}
	content := string(contentbytes)
	cols := strings.Fields(content)

	if len(cols) > 2 {
		f, err := strconv.ParseFloat(cols[1], 64)
		if err != nil {
			return 0.0, err
		}
		return f, nil
	}
	return 0.0, errors.New("cannot fetch loadavg5.")
}

func printValue(key string, value float64, time time.Time) {
	fmt.Printf("%s\t%f\t%d\n", key, value, time.Unix())
}

func outputCPUUsage(cpuUsage map[string]*cpuPercentages, now time.Time) {
	if cpuUsage != nil {
		for key, values := range cpuUsage {
			if key != "cpu" {
				printValue(fmt.Sprintf("multicore.cpu.%s.user", key), values.User, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.nice", key), values.Nice, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.system", key), values.System, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.idle", key), values.Idle, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.iowait", key), values.IoWait, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.irq", key), values.Irq, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.softirq", key), values.SoftIrq, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.steal", key), values.Steal, now)
				printValue(fmt.Sprintf("multicore.cpu.%s.guest", key), values.Guest, now)
			}
		}
	}
}

func outputLoadavgPerCore(loadavgPerCore float64, now time.Time) {
	printValue("multicore.loadavg_per_core.loadavg5", loadavgPerCore, now)
}

func outputDefinitions() {
	fmt.Println("# mackerel-agent-plugin")
	var graphs mp.GraphDef
	graphs.Graphs = graphDef

	b, err := json.Marshal(graphs)
	if err != nil {
		log.Fatalln("OutputDefinitions: ", err)
	}
	fmt.Println(string(b))
}

// main function
func main() {
	optTempfile := flag.String("tempfile", "", "Temp file name")
	var tempFileName string
	if *optTempfile != "" {
		tempFileName = *optTempfile
	} else {
		tempFileName = "/tmp/mackerel-plugin-multicore"
	}
	now := time.Now()

	currentValues, _ := collectProcStatValues()
	lastValues, lastTime, err := fetchLastValues(tempFileName)
	saveValues(tempFileName, currentValues, now)
	if err != nil {
		log.Fatalln("fetchLastValues: ", err)
	}

	cpuUsage := make(map[string]*cpuPercentages)
	if lastValues != nil {
		var err error
		cpuUsage, err = calcCPUUsage(currentValues, now, lastValues, lastTime)
		if err != nil {
			log.Fatalln("calcCPUUsage: ", err)
		}
	}

	loadavg5, err := fetchLoadavg5()
	if err != nil {
		log.Fatalln("fetchLoadavg5: ", err)
	}
	loadPerCPUCount := loadavg5 / (float64(len(cpuUsage) - 1))

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		outputDefinitions()
	} else {
		outputCPUUsage(cpuUsage, now)
		outputLoadavgPerCore(loadPerCPUCount, now)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/labstack/echo"
	"github.com/labstack/echo-contrib/session"
	log "github.com/sirupsen/logrus"
	"github.com/ubccr/goipa"
)

func (h *Handler) SSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)
	client := c.Get(ContextKeyIPAClient).(*ipa.Client)

	sess, err := session.Get(CookieKeySession, c)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get session")
	}

	vars := map[string]interface{}{
		"flashes": sess.Flashes(),
		"user":    user,
		"csrf":    c.Get("csrf").(string),
	}

	if c.Request().Method == "POST" {
		idx := c.FormValue("index")

		err = h.removeSSHPubKey(client, user, idx)
		if err != nil {
			vars["message"] = err.Error()
		} else {
			vars["message"] = "SSH Public Key Deleted"
		}
	}

	sess.Save(c.Request(), c.Response())
	return c.Render(http.StatusOK, "ssh-pubkey.html", vars)
}

func (h *Handler) NewSSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)

	vars := map[string]interface{}{
		"user": user,
		"csrf": c.Get("csrf").(string),
	}

	return c.Render(http.StatusOK, "new-ssh-pubkey.html", vars)
}

func (h *Handler) AddSSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)
	client := c.Get(ContextKeyIPAClient).(*ipa.Client)

	sess, err := session.Get(CookieKeySession, c)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get session")
	}

	vars := map[string]interface{}{
		"user": user,
		"csrf": c.Get("csrf").(string),
	}

	file, err := c.FormFile("key_file")
	if err != nil {
		log.WithFields(log.Fields{
			"user":  string(user.Uid),
			"error": err,
		}).Error("Failed to parse multipart file upload")
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to parse multipart file")
	}

	pubKey := ""
	if file.Size > 0 {
		src, err := file.Open()
		if err != nil {
			log.WithFields(log.Fields{
				"user":  string(user.Uid),
				"error": err,
			}).Error("Failed to open multipart file upload")
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to open file")
		}
		defer src.Close()

		data, err := ioutil.ReadAll(src)
		if err != nil {
			log.WithFields(log.Fields{
				"user": string(user.Uid),
				"err":  err,
			}).Error("Failed to read ssh pub key file upload")
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to read file")
		}
		pubKey = string(data)
	} else {
		pubKey = c.FormValue("key")
	}

	err = addSSHPubKey(client, user, pubKey)
	if err == nil {
		sess.AddFlash("SSH Public Key Added")
		sess.Save(c.Request(), c.Response())
		return c.Redirect(http.StatusFound, Path("/sshpubkey"))
	}

	vars["message"] = err.Error()

	return c.Render(http.StatusOK, "new-ssh-pubkey.html", vars)
}

func addSSHPubKey(client *ipa.Client, user *ipa.UserRecord, pubKey string) error {
	if len(pubKey) == 0 {
		return errors.New("No ssh key provided. Please provide a valid ssh public key")
	}

	pubKeys := make([]string, len(user.SSHPubKeys))
	copy(pubKeys, user.SSHPubKeys)
	found := false
	for _, k := range pubKeys {
		if k == pubKey {
			found = true
		}
	}

	if found {
		return errors.New("ssh key already exists.")
	}

	pubKeys = append(pubKeys, pubKey)

	newFps, err := client.UpdateSSHPubKeys(string(user.Uid), pubKeys)
	if err != nil {
		if ierr, ok := err.(*ipa.IpaError); ok {
			// Raised when a parameter value fails a validation rule
			if ierr.Code == 3009 {
				return errors.New("Invalid ssh public key")
			}
		} else {
			log.WithFields(log.Fields{
				"error": err.Error(),
				"user":  string(user.Uid),
			}).Error("Ipa error when attempting to add new ssh public key")
			return errors.New("Fatal system error occured.")
		}
	}

	user.SSHPubKeys = pubKeys
	user.SSHPubKeyFps = newFps

	return nil
}

func (h *Handler) removeSSHPubKey(client *ipa.Client, user *ipa.UserRecord, idx string) error {
	index, err := strconv.Atoi(idx)
	if err != nil {
		return errors.New("Invalid ssh key provided")
	}
	if index < 0 || index > len(user.SSHPubKeys) {
		log.WithFields(log.Fields{
			"user":  string(user.Uid),
			"index": index,
		}).Error("Invalid ssh pub key index")
		return errors.New("Invalid ssh key provided")
	}

	pubKeys := make([]string, len(user.SSHPubKeys))
	copy(pubKeys, user.SSHPubKeys)

	// Remove key at index
	pubKeys = append(pubKeys[:index], pubKeys[index+1:]...)

	newFps, err := client.UpdateSSHPubKeys(string(user.Uid), pubKeys)
	if err != nil {
		log.WithFields(log.Fields{
			"user":  string(user.Uid),
			"index": index,
			"error": err,
		}).Error("Failed to delete ssh pub key")
		return errors.New("Fatal error removing ssh key. Please contact your administrator")
	}

	user.SSHPubKeys = pubKeys
	user.SSHPubKeyFps = newFps
	return nil
}
<commit_msg>Fix ssh key upload bug<commit_after>package server

import (
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/labstack/echo"
	"github.com/labstack/echo-contrib/session"
	log "github.com/sirupsen/logrus"
	"github.com/ubccr/goipa"
)

func (h *Handler) SSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)
	client := c.Get(ContextKeyIPAClient).(*ipa.Client)

	sess, err := session.Get(CookieKeySession, c)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get session")
	}

	vars := map[string]interface{}{
		"flashes": sess.Flashes(),
		"user":    user,
		"csrf":    c.Get("csrf").(string),
	}

	if c.Request().Method == "POST" {
		idx := c.FormValue("index")

		err = h.removeSSHPubKey(client, user, idx)
		if err != nil {
			vars["message"] = err.Error()
		} else {
			vars["message"] = "SSH Public Key Deleted"
		}
	}

	sess.Save(c.Request(), c.Response())
	return c.Render(http.StatusOK, "ssh-pubkey.html", vars)
}

func (h *Handler) NewSSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)

	vars := map[string]interface{}{
		"user": user,
		"csrf": c.Get("csrf").(string),
	}

	return c.Render(http.StatusOK, "new-ssh-pubkey.html", vars)
}

func (h *Handler) AddSSHPubKey(c echo.Context) error {
	user := c.Get(ContextKeyUser).(*ipa.UserRecord)
	client := c.Get(ContextKeyIPAClient).(*ipa.Client)

	sess, err := session.Get(CookieKeySession, c)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get session")
	}

	vars := map[string]interface{}{
		"user": user,
		"csrf": c.Get("csrf").(string),
	}

	pubKey := ""
	file, err := c.FormFile("key_file")
	if err == nil && file.Size > 0 {
		src, err := file.Open()
		if err != nil {
			log.WithFields(log.Fields{
				"user":  string(user.Uid),
				"error": err,
			}).Error("Failed to open multipart file upload")
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to open file")
		}
		defer src.Close()

		data, err := ioutil.ReadAll(src)
		if err != nil {
			log.WithFields(log.Fields{
				"user": string(user.Uid),
				"err":  err,
			}).Error("Failed to read ssh pub key file upload")
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to read file")
		}
		pubKey = string(data)
	} else {
		pubKey = c.FormValue("key")
	}

	err = addSSHPubKey(client, user, pubKey)
	if err == nil {
		sess.AddFlash("SSH Public Key Added")
		sess.Save(c.Request(), c.Response())
		return c.Redirect(http.StatusFound, Path("/sshpubkey"))
	}

	vars["message"] = err.Error()

	return c.Render(http.StatusOK, "new-ssh-pubkey.html", vars)
}

func addSSHPubKey(client *ipa.Client, user *ipa.UserRecord, pubKey string) error {
	if len(pubKey) == 0 {
		return errors.New("No ssh key provided. Please provide a valid ssh public key")
	}

	pubKeys := make([]string, len(user.SSHPubKeys))
	copy(pubKeys, user.SSHPubKeys)
	found := false
	for _, k := range pubKeys {
		if k == pubKey {
			found = true
		}
	}

	if found {
		return errors.New("ssh key already exists.")
	}

	pubKeys = append(pubKeys, pubKey)

	newFps, err := client.UpdateSSHPubKeys(string(user.Uid), pubKeys)
	if err != nil {
		if ierr, ok := err.(*ipa.IpaError); ok {
			// Raised when a parameter value fails a validation rule
			if ierr.Code == 3009 {
				return errors.New("Invalid ssh public key")
			}
		} else {
			log.WithFields(log.Fields{
				"error": err.Error(),
				"user":  string(user.Uid),
			}).Error("Ipa error when attempting to add new ssh public key")
			return errors.New("Fatal system error occured.")
		}
	}

	user.SSHPubKeys = pubKeys
	user.SSHPubKeyFps = newFps

	return nil
}

func (h *Handler) removeSSHPubKey(client *ipa.Client, user *ipa.UserRecord, idx string) error {
	index, err := strconv.Atoi(idx)
	if err != nil {
		return errors.New("Invalid ssh key provided")
	}
	if index < 0 || index > len(user.SSHPubKeys) {
		log.WithFields(log.Fields{
			"user":  string(user.Uid),
			"index": index,
		}).Error("Invalid ssh pub key index")
		return errors.New("Invalid ssh key provided")
	}

	pubKeys := make([]string, len(user.SSHPubKeys))
	copy(pubKeys, user.SSHPubKeys)

	// Remove key at index
	pubKeys = append(pubKeys[:index], pubKeys[index+1:]...)

	newFps, err := client.UpdateSSHPubKeys(string(user.Uid), pubKeys)
	if err != nil {
		log.WithFields(log.Fields{
			"user":  string(user.Uid),
			"index": index,
			"error": err,
		}).Error("Failed to delete ssh pub key")
		return errors.New("Fatal error removing ssh key. Please contact your administrator")
	}

	user.SSHPubKeys = pubKeys
	user.SSHPubKeyFps = newFps
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/jbrodriguez/mlog"
	"github.com/jbrodriguez/pubsub"
	// "jbrodriguez/unbalance/server/model"
	"jbrodriguez/unbalance/server/lib"
	"jbrodriguez/unbalance/server/services"
	"os"
	"os/signal"
	// "path/filepath"
	"fmt"
	"log"
	"syscall"
)

var Version string

func main() {
	settings, err := lib.NewSettings(Version)
	if err != nil {
		log.Printf("Unable to load settings: %s", err.Error())
		os.Exit(1)
	}

	mlog.Start(mlog.LevelInfo, settings.Log)

	mlog.Info("unBALANCE v%s starting up ...", Version)

	// mlog.Info("%+v", settings)

	var msg string
	if exists, _ := lib.Exists(settings.Conf); exists {
		msg = fmt.Sprintf("Using config file %s ...", settings.Conf)
	} else {
		msg = "No config file exists yet. Using app defaults ..."
	}
	mlog.Info(msg)

	bus := pubsub.New(623)

	socket := services.NewSocket(bus, settings)
	server := services.NewServer(bus, settings)
	core := services.NewCore(bus, settings)

	socket.Start()
	server.Start()
	core.Start()

	mlog.Info("Press Ctrl+C to stop ...")

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	mlog.Info("Received signal: (%s) ... shutting down the app now ...", <-c)

	core.Stop()
	server.Stop()
	socket.Stop()
}
<commit_msg>(back) Fatal out if sanity check fails during startup<commit_after>package main

import (
	"github.com/jbrodriguez/mlog"
	"github.com/jbrodriguez/pubsub"
	// "jbrodriguez/unbalance/server/model"
	"jbrodriguez/unbalance/server/lib"
	"jbrodriguez/unbalance/server/services"
	"os"
	"os/signal"
	// "path/filepath"
	"fmt"
	"log"
	"syscall"
)

var Version string

func main() {
	settings, err := lib.NewSettings(Version)
	if err != nil {
		log.Printf("Unable to load settings: %s", err.Error())
		os.Exit(1)
	}

	mlog.Start(mlog.LevelInfo, settings.Log)

	mlog.Info("unBALANCE v%s starting up ...", Version)

	// mlog.Info("%+v", settings)

	var msg string
	if exists, _ := lib.Exists(settings.Conf); exists {
		msg = fmt.Sprintf("Using config file %s ...", settings.Conf)
	} else {
		msg = "No config file exists yet. Using app defaults ..."
	}
	mlog.Info(msg)

	bus := pubsub.New(623)

	socket := services.NewSocket(bus, settings)
	server := services.NewServer(bus, settings)
	core := services.NewCore(bus, settings)

	socket.Start()
	server.Start()
	mlog.FatalIfError(core.Start())

	mlog.Info("Press Ctrl+C to stop ...")

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	mlog.Info("Received signal: (%s) ... shutting down the app now ...", <-c)

	core.Stop()
	server.Stop()
	socket.Stop()
}
<|endoftext|>


<commit_before>package web

// TODO: Create package 'oauth'

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

	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/util"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	google_auth2 "google.golang.org/api/oauth2/v2"
)

var _ = fmt.Print
var googleOAuthConfig = oauth2.Config{
	ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
	ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
	Endpoint:     google.Endpoint,
	RedirectURL:  "",
	Scopes: []string{
		"openid email",
		"openid profile",
	},
}

func OAuthGoogle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	state := util.RandomString(32)
	cookie := &http.Cookie{
		Name:     "oauthState",
		Value:    state,
		Path:     "/",
		Expires:  time.Now().Add(time.Minute * 30),
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)

	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}
	c := getGoogleOAuthConfig(fmt.Sprintf("%s://%s/oauth/google/callback", scheme, r.Host))
	http.Redirect(w, r, c.AuthCodeURL(state), http.StatusFound)
}

func OAuthGoogleCallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if err := checkState(r); err != nil {
		InternalServerError(w, err)
		return
	}
	token, idToken, err := exchange(r)
	if err != nil {
		InternalServerError(w, err)
		return
	}
	name, email, err := getNameAndEmail(token, idToken)
	if err != nil {
		InternalServerError(w, err)
		return
	}

	db := model.MustDb(ctx)
	user := model.User{Name: name, Email: email}
	if err := db.FirstOrCreate(&user, model.User{Email: email}).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to get or create User"))
		return
	}

	// Create and save API Token
	apiToken := util.RandomString(64)
	userApiToken := model.UserApiToken{
		UserId: user.Id,
		Token:  apiToken,
	}
	if err := db.Create(&userApiToken).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to create UserApiToken"))
		return
	}
	cookie := &http.Cookie{
		Name:     ApiTokenCookieName,
		Value:    apiToken,
		Path:     "/",
		Expires:  time.Now().Add(time.Hour * 24 * 30),
		HttpOnly: false,
	}
	http.SetCookie(w, cookie)

	//data := map[string]interface{}{
	//	"id":          user.Id,
	//	"name":        user.Name,
	//	"email":       user.Email,
	//	"accessToken": token.AccessToken,
	//	"idToken":     idToken,
	//}
	//if err := json.NewEncoder(w).Encode(data); err != nil {
	//	InternalServerError(w, errors.Errorf("Failed to encode JSON"))
	//	return
	//}

	http.Redirect(w, r, "/", http.StatusFound)
}

func checkState(r *http.Request) error {
	state := r.FormValue("state")
	oauthState, err := r.Cookie("oauthState")
	if err != nil {
		return errors.Wrap(err, "Failed to get cookie oauthState")
	}
	if state != oauthState.Value {
		return errors.Wrap(err, "state mismatch")
	}
	return nil
}

func exchange(r *http.Request) (*oauth2.Token, string, error) {
	code := r.FormValue("code")
	c := getGoogleOAuthConfig(fmt.Sprintf("http://%s/oauth/google/callback", r.Host)) // TODO: scheme
	token, err := c.Exchange(oauth2.NoContext, code)
	if err != nil {
		return nil, "", errors.Wrap(err, "Failed to exchange")
	}
	idToken, ok := token.Extra("id_token").(string)
	if !ok {
		return nil, "", errors.Errorf("Failed to get id_token")
	}
	return token, idToken, nil
}

func getNameAndEmail(token *oauth2.Token, idToken string) (string, string, error) {
	oauth2Client := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(token))
	service, err := google_auth2.New(oauth2Client)
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to create oauth2.Client")
	}

	userinfo, err := service.Userinfo.V2.Me.Get().Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get userinfo")
	}

	tokeninfo, err := service.Tokeninfo().IdToken(idToken).Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get tokeninfo")
	}

	return userinfo.Name, tokeninfo.Email, nil
}

func getGoogleOAuthConfig(redirectUrl string) oauth2.Config {
	c := googleOAuthConfig
	c.RedirectURL = redirectUrl
	return c
}
<commit_msg>Debug for heroku<commit_after>package web

// TODO: Create package 'oauth'

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

	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/util"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	google_auth2 "google.golang.org/api/oauth2/v2"
)

var _ = fmt.Print
var googleOAuthConfig = oauth2.Config{
	ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
	ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
	Endpoint:     google.Endpoint,
	RedirectURL:  "",
	Scopes: []string{
		"openid email",
		"openid profile",
	},
}

func OAuthGoogle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	state := util.RandomString(32)
	cookie := &http.Cookie{
		Name:     "oauthState",
		Value:    state,
		Path:     "/",
		Expires:  time.Now().Add(time.Minute * 30),
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)

	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}
	fmt.Println("--- header ---")
	for k, v := range r.Header {
		fmt.Printf("key = %v, value = %v\n", k, v)
	}
	c := getGoogleOAuthConfig(fmt.Sprintf("%s://%s/oauth/google/callback", scheme, r.Host))
	http.Redirect(w, r, c.AuthCodeURL(state), http.StatusFound)
}

func OAuthGoogleCallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if err := checkState(r); err != nil {
		InternalServerError(w, err)
		return
	}
	token, idToken, err := exchange(r)
	if err != nil {
		InternalServerError(w, err)
		return
	}
	name, email, err := getNameAndEmail(token, idToken)
	if err != nil {
		InternalServerError(w, err)
		return
	}

	db := model.MustDb(ctx)
	user := model.User{Name: name, Email: email}
	if err := db.FirstOrCreate(&user, model.User{Email: email}).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to get or create User"))
		return
	}

	// Create and save API Token
	apiToken := util.RandomString(64)
	userApiToken := model.UserApiToken{
		UserId: user.Id,
		Token:  apiToken,
	}
	if err := db.Create(&userApiToken).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to create UserApiToken"))
		return
	}
	cookie := &http.Cookie{
		Name:     ApiTokenCookieName,
		Value:    apiToken,
		Path:     "/",
		Expires:  time.Now().Add(time.Hour * 24 * 30),
		HttpOnly: false,
	}
	http.SetCookie(w, cookie)

	//data := map[string]interface{}{
	//	"id":          user.Id,
	//	"name":        user.Name,
	//	"email":       user.Email,
	//	"accessToken": token.AccessToken,
	//	"idToken":     idToken,
	//}
	//if err := json.NewEncoder(w).Encode(data); err != nil {
	//	InternalServerError(w, errors.Errorf("Failed to encode JSON"))
	//	return
	//}

	http.Redirect(w, r, "/", http.StatusFound)
}

func checkState(r *http.Request) error {
	state := r.FormValue("state")
	oauthState, err := r.Cookie("oauthState")
	if err != nil {
		return errors.Wrap(err, "Failed to get cookie oauthState")
	}
	if state != oauthState.Value {
		return errors.Wrap(err, "state mismatch")
	}
	return nil
}

func exchange(r *http.Request) (*oauth2.Token, string, error) {
	code := r.FormValue("code")
	c := getGoogleOAuthConfig(fmt.Sprintf("http://%s/oauth/google/callback", r.Host)) // TODO: scheme
	token, err := c.Exchange(oauth2.NoContext, code)
	if err != nil {
		return nil, "", errors.Wrap(err, "Failed to exchange")
	}
	idToken, ok := token.Extra("id_token").(string)
	if !ok {
		return nil, "", errors.Errorf("Failed to get id_token")
	}
	return token, idToken, nil
}

func getNameAndEmail(token *oauth2.Token, idToken string) (string, string, error) {
	oauth2Client := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(token))
	service, err := google_auth2.New(oauth2Client)
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to create oauth2.Client")
	}

	userinfo, err := service.Userinfo.V2.Me.Get().Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get userinfo")
	}

	tokeninfo, err := service.Tokeninfo().IdToken(idToken).Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get tokeninfo")
	}

	return userinfo.Name, tokeninfo.Email, nil
}

func getGoogleOAuthConfig(redirectUrl string) oauth2.Config {
	c := googleOAuthConfig
	c.RedirectURL = redirectUrl
	return c
}
<|endoftext|>


<commit_before>package services

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"
	"unicode/utf8"

	"github.com/khades/servbot/bot"
	"github.com/khades/servbot/httpclient"
	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type responseItem struct {
	ID    int    `json:"id"`
	Owner int    `json:"owner_id"`
	Text  string `json:"text"`
}

type vkResponse struct {
	Response response `json:"response"`
}

type response struct {
	Items []responseItem `json:"items"`
}

func Short(s string, i int) string {
	runes := []rune(s)
	if len(runes) > i {
		return string(runes[:i])
	}
	return s
}

func CheckVK() {
	log.Println("Checking")
	channels, error := repos.GetVKEnabledChannels()
	if error != nil {
		return
	}
	for _, channel := range *channels {
		result, parseError := ParseVK(&channel.VkGroupInfo)
		if parseError == nil && result.LastMessageID != channel.VkGroupInfo.LastMessageID {
			repos.PushVkGroupInfo(&channel.ChannelID, result)
			if result.NotifyOnChange == true {
				channelName, channelNameError := repos.GetUsernameByID(&channel.ChannelID)
				if channelNameError == nil && *channelName != "" {
					bot.IrcClientInstance.SendPublic(&models.OutgoingMessage{
						Channel: *channelName,
						Body:    "[VK https://vk.com/" + channel.VkGroupInfo.GroupName + "] " + result.LastMessageBody + " " + result.LastMessageURL})
				}
			}
		}
	}
}

// https://api.vk.com/method/wall.get?domain=mob5tervk&filter=owner&count=1&v=5.60
func ParseVK(vkInputGroupInfo *models.VkGroupInfo) (*models.VkGroupInfo, error) {
	vkGroupInfo := models.VkGroupInfo{GroupName: vkInputGroupInfo.GroupName,
		NotifyOnChange: vkInputGroupInfo.NotifyOnChange}
	resp, error := httpclient.Get("https://api.vk.com/method/wall.get?domain=" + vkInputGroupInfo.GroupName + "&filter=owner&count=1&v=5.60")
	if error != nil {
		log.Println(error)
		return &vkGroupInfo, error
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	vkResp := vkResponse{}
	marshallError := json.NewDecoder(resp.Body).Decode(&vkResp)
	if marshallError != nil {
		log.Println(marshallError)
		return &vkGroupInfo, marshallError
	}
	if len(vkResp.Response.Items) == 0 {
		return &vkGroupInfo, errors.New("not found")
	}
	vkPost := vkResp.Response.Items[0]
	vkPost.Text = strings.Replace(vkPost.Text, "\n", " ", -1)
	if utf8.RuneCountInString(vkPost.Text) > 300 {
		vkPost.Text = Short(vkPost.Text, 297) + "..."
	}
	vkGroupInfo.LastMessageID = vkPost.ID
	vkGroupInfo.LastMessageBody = vkPost.Text
	vkGroupInfo.LastMessageURL = fmt.Sprintf("https://vk.com/mob5tervk?w=wall%d_%d", vkPost.Owner, vkPost.ID)
	log.Println(vkGroupInfo)
	return &vkGroupInfo, nil
}
<commit_msg>Feature: VK group<commit_after>package services

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"
	"unicode/utf8"

	"github.com/khades/servbot/bot"
	"github.com/khades/servbot/httpclient"
	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type responseItem struct {
	ID    int    `json:"id"`
	Owner int    `json:"owner_id"`
	Text  string `json:"text"`
}

type vkResponse struct {
	Response response `json:"response"`
}

type response struct {
	Items []responseItem `json:"items"`
}

func Short(s string, i int) string {
	runes := []rune(s)
	if len(runes) > i {
		return string(runes[:i])
	}
	return s
}

func CheckVK() {
	log.Println("Checking")
	channels, error := repos.GetVKEnabledChannels()
	if error != nil {
		return
	}
	for _, channel := range *channels {
		go checkOne(&channel)
	}
}
func checkOne(channel *models.ChannelInfo) {
	result, parseError := ParseVK(&channel.VkGroupInfo)
	if parseError != nil || result.LastMessageID == channel.VkGroupInfo.LastMessageID {
		return
	}
	repos.PushVkGroupInfo(&channel.ChannelID, result)
	if result.NotifyOnChange == false {
		return
	}
	channelName, channelNameError := repos.GetUsernameByID(&channel.ChannelID)

	if channelNameError == nil && *channelName != "" {
		log.Println("SENDING MESSAGE")
		bot.IrcClientInstance.SendPublic(&models.OutgoingMessage{
			Channel: *channelName,
			Body:    "[VK https://vk.com/" + channel.VkGroupInfo.GroupName + "] " + result.LastMessageBody + " " + result.LastMessageURL})
	}
}

// https://api.vk.com/method/wall.get?domain=mob5tervk&filter=owner&count=1&v=5.60
func ParseVK(vkInputGroupInfo *models.VkGroupInfo) (*models.VkGroupInfo, error) {
	vkGroupInfo := models.VkGroupInfo{GroupName: vkInputGroupInfo.GroupName,
		NotifyOnChange: vkInputGroupInfo.NotifyOnChange}
	resp, error := httpclient.Get("https://api.vk.com/method/wall.get?domain=" + vkInputGroupInfo.GroupName + "&filter=owner&count=1&v=5.60")
	if error != nil {
		log.Println(error)
		return &vkGroupInfo, error
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	vkResp := vkResponse{}
	marshallError := json.NewDecoder(resp.Body).Decode(&vkResp)
	if marshallError != nil {
		log.Println(marshallError)
		return &vkGroupInfo, marshallError
	}
	if len(vkResp.Response.Items) == 0 {
		return &vkGroupInfo, errors.New("not found")
	}
	vkPost := vkResp.Response.Items[0]
	vkPost.Text = strings.Replace(vkPost.Text, "\n", " ", -1)
	if utf8.RuneCountInString(vkPost.Text) > 300 {
		vkPost.Text = Short(vkPost.Text, 297) + "..."
	}
	vkGroupInfo.LastMessageID = vkPost.ID
	vkGroupInfo.LastMessageBody = vkPost.Text
	vkGroupInfo.LastMessageURL = fmt.Sprintf("https://vk.com/mob5tervk?w=wall%d_%d", vkPost.Owner, vkPost.ID)
	log.Println(vkGroupInfo)
	return &vkGroupInfo, nil
}
<|endoftext|>