<commit_before>package bot

import (
	"fmt"
	"log"
	"strings"
)

// Cmd holds the parsed user's input for easier handling of commands
type Cmd struct {
	Raw     string   // Raw is full string passed to the command
	Channel string   // Channel where the command was called
	Nick    string   // User who sent the message
	Message string   // Full string without the prefix
	Command string   // Command is the first argument passed to the bot
	FullArg string   // Full argument as a single string
	Args    []string // Arguments as array
}

// Passive Cmd have the basice information for passive commands to work on
type PassiveCmd struct {
	Raw     string // Raw message sent to the channel
	Channel string // Channel which the message was sent to
	Nick    string // Nick of the user which sent the message
}

type customCommand struct {
	Cmd         string
	CmdFunc     func(cmd *Cmd) (string, error)
	Description string
	ExampleArgs string
}

type incomingMessage struct {
	Channel        string
	Text           string
	SenderNick     string
	BotCurrentNick string
}

const (
	commandNotAvailable   = "Command %v not available."
	noCommandsAvailable   = "No commands available."
	errorExecutingCommand = "Error executing %s: %s"
	helpDescripton        = "Description: %s"
	helpUsage             = "Usage: %s%s %s"
	availableCommands     = "Available commands: %v"
	helpAboutCommand      = "Type: '%shelp <command>' to see details about a specific command."
	helpCommand           = "help"
)

type passiveCmdFunc func(cmd *PassiveCmd) (string, error)

var (
	commands        = make(map[string]*customCommand)
	passiveCommands = make(map[string]passiveCmdFunc)
)

// RegisterCommand adds a new command to the bot.
// The command(s) should be registered in the Ini() func of your package
// command: String which the user will use to execute the command, example: reverse
// decription: Description of the command to use in !help, example: Reverses a string
// exampleArgs: Example args to be displayed in !help <command>, example: string to be reversed
// cmdFunc: Function which will be executed. It will received a parsed command as a Cmd value
func RegisterCommand(command, description, exampleArgs string, cmdFunc func(cmd *Cmd) (string, error)) {
	commands[command] = &customCommand{
		Cmd:         command,
		CmdFunc:     cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterPassiveCommand adds a new passive command to the bot.
// The command(s) should be registered in the Ini() func of your package
// Passive commands receives all the text posted to a channel without any parsing
// command: String used to identify the command, for internal use only (ex: logs)
// cmdFunc: Function which will be executed. It will received the raw message, channel and nick
func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (string, error)) {
	passiveCommands[command] = cmdFunc
}

func isPrivateMsg(channel, currentNick string) bool {
	return channel == currentNick
}

func messageReceived(channel, text, senderNick string, conn ircConnection) {
	if isPrivateMsg(channel, conn.GetNick()) {
		channel = senderNick // should reply in private
	}

	command := parse(text, channel, senderNick)
	if command == nil {
		handleMessage(&PassiveCmd{
			Raw:     text,
			Channel: channel,
			Nick:    senderNick,
		})
		return
	}

	if command.Command == helpCommand {
		help(command, channel, senderNick, conn)
	} else {
		handleCmd(command, conn)
	}

}

func handleMessage(c *PassiveCmd) {
	for k, v := range passiveCommands {
		log.Println("Executando: ", k)
		s, err := v(c)
		if err != nil {
			log.Println(err)
		}
		irccon.Privmsg(c.Channel, s)
	}
}

func handleCmd(c *Cmd, conn ircConnection) {
	cmd := commands[c.Command]

	if cmd == nil {
		log.Printf("Command not found %v", c.Command)
		return
	}

	log.Printf("HandleCmd %v %v", c.Command, c.FullArg)

	result, err := cmd.CmdFunc(c)

	if err != nil {
		errorMsg := fmt.Sprintf(errorExecutingCommand, c.Command, err.Error())
		log.Printf(errorMsg)
		conn.Privmsg(c.Channel, errorMsg)
	}

	if result != "" {
		conn.Privmsg(c.Channel, result)
	}
}

func help(c *Cmd, channel, senderNick string, conn ircConnection) {
	cmd := parse(CmdPrefix+c.FullArg, channel, senderNick)
	if cmd == nil {
		showAvailabeCommands(channel, conn)
		return
	}

	command := commands[cmd.Command]
	if command == nil {
		showAvailabeCommands(c.Channel, conn)
		return
	}

	showHelp(cmd, command, conn)
}

func showHelp(c *Cmd, help *customCommand, conn ircConnection) {
	if help.Description != "" {
		conn.Privmsg(c.Channel, fmt.Sprintf(helpDescripton, help.Description))
	}
	conn.Privmsg(c.Channel, fmt.Sprintf(helpUsage, CmdPrefix, c.Command, help.ExampleArgs))
}

func showAvailabeCommands(channel string, conn ircConnection) {
	cmds := make([]string, 0)
	for k := range commands {
		cmds = append(cmds, k)
	}
	conn.Privmsg(channel, fmt.Sprintf(helpAboutCommand, CmdPrefix))
	conn.Privmsg(channel, fmt.Sprintf(availableCommands, strings.Join(cmds, ", ")))
}
<commit_msg>Fixed passive commands description<commit_after>package bot

import (
	"fmt"
	"log"
	"strings"
)

// Cmd holds the parsed user's input for easier handling of commands
type Cmd struct {
	Raw     string   // Raw is full string passed to the command
	Channel string   // Channel where the command was called
	Nick    string   // User who sent the message
	Message string   // Full string without the prefix
	Command string   // Command is the first argument passed to the bot
	FullArg string   // Full argument as a single string
	Args    []string // Arguments as array
}

// PassiveCmd holds the information which will be passed to passive commands when receiving a message on the channel
type PassiveCmd struct {
	Raw     string // Raw message sent to the channel
	Channel string // Channel which the message was sent to
	Nick    string // Nick of the user which sent the message
}

type customCommand struct {
	Cmd         string
	CmdFunc     func(cmd *Cmd) (string, error)
	Description string
	ExampleArgs string
}

type incomingMessage struct {
	Channel        string
	Text           string
	SenderNick     string
	BotCurrentNick string
}

const (
	commandNotAvailable   = "Command %v not available."
	noCommandsAvailable   = "No commands available."
	errorExecutingCommand = "Error executing %s: %s"
	helpDescripton        = "Description: %s"
	helpUsage             = "Usage: %s%s %s"
	availableCommands     = "Available commands: %v"
	helpAboutCommand      = "Type: '%shelp <command>' to see details about a specific command."
	helpCommand           = "help"
)

type passiveCmdFunc func(cmd *PassiveCmd) (string, error)

var (
	commands        = make(map[string]*customCommand)
	passiveCommands = make(map[string]passiveCmdFunc)
)

// RegisterCommand adds a new command to the bot.
// The command(s) should be registered in the Ini() func of your package
// command: String which the user will use to execute the command, example: reverse
// decription: Description of the command to use in !help, example: Reverses a string
// exampleArgs: Example args to be displayed in !help <command>, example: string to be reversed
// cmdFunc: Function which will be executed. It will received a parsed command as a Cmd value
func RegisterCommand(command, description, exampleArgs string, cmdFunc func(cmd *Cmd) (string, error)) {
	commands[command] = &customCommand{
		Cmd:         command,
		CmdFunc:     cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterPassiveCommand adds a new passive command to the bot.
// The command(s) should be registered in the Ini() func of your package
// Passive commands receives all the text posted to a channel without any parsing
// command: String used to identify the command, for internal use only (ex: logs)
// cmdFunc: Function which will be executed. It will received the raw message, channel and nick
func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (string, error)) {
	passiveCommands[command] = cmdFunc
}

func isPrivateMsg(channel, currentNick string) bool {
	return channel == currentNick
}

func messageReceived(channel, text, senderNick string, conn ircConnection) {
	if isPrivateMsg(channel, conn.GetNick()) {
		channel = senderNick // should reply in private
	}

	command := parse(text, channel, senderNick)
	if command == nil {
		handleMessage(&PassiveCmd{
			Raw:     text,
			Channel: channel,
			Nick:    senderNick,
		})
		return
	}

	if command.Command == helpCommand {
		help(command, channel, senderNick, conn)
	} else {
		handleCmd(command, conn)
	}

}

func handleMessage(c *PassiveCmd) {
	for k, v := range passiveCommands {
		log.Println("Executando: ", k)
		s, err := v(c)
		if err != nil {
			log.Println(err)
		}
		irccon.Privmsg(c.Channel, s)
	}
}

func handleCmd(c *Cmd, conn ircConnection) {
	cmd := commands[c.Command]

	if cmd == nil {
		log.Printf("Command not found %v", c.Command)
		return
	}

	log.Printf("HandleCmd %v %v", c.Command, c.FullArg)

	result, err := cmd.CmdFunc(c)

	if err != nil {
		errorMsg := fmt.Sprintf(errorExecutingCommand, c.Command, err.Error())
		log.Printf(errorMsg)
		conn.Privmsg(c.Channel, errorMsg)
	}

	if result != "" {
		conn.Privmsg(c.Channel, result)
	}
}

func help(c *Cmd, channel, senderNick string, conn ircConnection) {
	cmd := parse(CmdPrefix+c.FullArg, channel, senderNick)
	if cmd == nil {
		showAvailabeCommands(channel, conn)
		return
	}

	command := commands[cmd.Command]
	if command == nil {
		showAvailabeCommands(c.Channel, conn)
		return
	}

	showHelp(cmd, command, conn)
}

func showHelp(c *Cmd, help *customCommand, conn ircConnection) {
	if help.Description != "" {
		conn.Privmsg(c.Channel, fmt.Sprintf(helpDescripton, help.Description))
	}
	conn.Privmsg(c.Channel, fmt.Sprintf(helpUsage, CmdPrefix, c.Command, help.ExampleArgs))
}

func showAvailabeCommands(channel string, conn ircConnection) {
	cmds := make([]string, 0)
	for k := range commands {
		cmds = append(cmds, k)
	}
	conn.Privmsg(channel, fmt.Sprintf(helpAboutCommand, CmdPrefix))
	conn.Privmsg(channel, fmt.Sprintf(availableCommands, strings.Join(cmds, ", ")))
}
<|endoftext|>


<commit_before>package daemon

import (
	"io"
	"testing"

	"src.elv.sh/pkg/daemon/daemondefs"
	"src.elv.sh/pkg/testutil"
)

func TestActivate_WhenServerExists(t *testing.T) {
	setup(t)
	startServer(t)
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "sock", RunDir: "."})
	if err != nil {
		t.Errorf("got error %v, want nil", err)
	}
}

func TestActivate_FailsIfCannotStatSock(t *testing.T) {
	setup(t)
	testutil.MustCreateEmpty("not-dir")
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "not-dir/sock", RunDir: "."})
	if err == nil {
		t.Errorf("got error nil, want non-nil")
	}
}

func TestActivate_FailsIfCannotDialSock(t *testing.T) {
	setup(t)
	testutil.MustCreateEmpty("sock")
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "sock", RunDir: "."})
	if err == nil {
		t.Errorf("got error nil, want non-nil")
	}
}
<commit_msg>Disable test that may have inadvertently triggered daemon spawning.<commit_after>package daemon

import (
	"io"
	"testing"

	"src.elv.sh/pkg/daemon/daemondefs"
	"src.elv.sh/pkg/testutil"
)

func TestActivate_WhenServerExists(t *testing.T) {
	setup(t)
	startServer(t)
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "sock", RunDir: "."})
	if err != nil {
		t.Errorf("got error %v, want nil", err)
	}
}

func TestActivate_FailsIfCannotStatSock(t *testing.T) {
	t.Skip()
	setup(t)
	testutil.MustCreateEmpty("not-dir")
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "not-dir/sock", RunDir: "."})
	if err == nil {
		t.Errorf("got error nil, want non-nil")
	}
}

func TestActivate_FailsIfCannotDialSock(t *testing.T) {
	setup(t)
	testutil.MustCreateEmpty("sock")
	_, err := Activate(io.Discard,
		&daemondefs.SpawnConfig{DbPath: "db", SockPath: "sock", RunDir: "."})
	if err == nil {
		t.Errorf("got error nil, want non-nil")
	}
}
<|endoftext|>


<commit_before>package container

import (
	"github.com/blang/semver"
	"github.com/containerum/kube-client/pkg/model"
)

type ContainerList []Container

func (containers ContainerList) Images() []string {
	images := make([]string, 0, len(containers))
	for _, container := range containers {
		images = append(images, container.Image)
	}
	return images
}

func (containers ContainerList) GetByName(name string) (Container, bool) {
	for _, container := range containers {
		if container.Name == name {
			return container, true
		}
	}
	return Container{}, false
}

func (containers ContainerList) Names() []string {
	names := make([]string, 0, len(containers))
	for _, container := range containers {
		names = append(names, container.Name)
	}
	return names
}

func (list ContainerList) ConfigMountsMap() map[string]model.ContainerVolume {
	var mounts = make(map[string]model.ContainerVolume, len(list))
	for _, container := range list {
		for _, config := range container.ConfigMaps {
			mounts[config.Name] = config
		}
	}
	return mounts
}

func (list ContainerList) VolumeMountsMap() map[string]model.ContainerVolume {
	var mounts = make(map[string]model.ContainerVolume, len(list))
	for _, container := range list {
		for _, volume := range container.VolumeMounts {
			mounts[volume.Name] = volume
		}
	}
	return mounts
}

func (list ContainerList) Copy() ContainerList {
	var cp = make(ContainerList, 0, len(list))
	for _, cont := range cp {
		cp = append(cp, cont.Copy())
	}
	return cp
}

func (list ContainerList) Replace(cont Container) (ContainerList, bool) {
	var updated = list.Copy()
	for i, c := range updated {
		if c.Name == cont.Name {
			updated[i] = cont.Copy()
			return updated, true
		}
	}
	return updated, false
}

func (list ContainerList) Filter(pred func(cont Container) bool) ContainerList {
	var filtered = make(ContainerList, 0, len(list))
	for _, cont := range list {
		if pred(cont.Copy()) {
			filtered = append(filtered, cont.Copy())
		}
	}
	return filtered
}

func (list ContainerList) DeleteByName(name string) ContainerList {
	var filtered = make(ContainerList, 0, len(list))
	for i, cont := range list {
		if cont.Name == name {
			return append(append(filtered, list[:i]...), list[i+1:]...)
		}
	}
	return filtered
}

func (list ContainerList) SemanticVersions() []string {
	var versions = make([]string, 0, len(list))
	for _, cont := range list {
		var version, err = semver.ParseTolerant(cont.ToKube().Version())
		if err == nil {
			versions = append(versions, version.String())
		}
	}
	return versions
}
<commit_msg>fix ContainerList.Copy<commit_after>package container

import (
	"github.com/blang/semver"
	"github.com/containerum/kube-client/pkg/model"
)

type ContainerList []Container

func (containers ContainerList) Images() []string {
	images := make([]string, 0, len(containers))
	for _, container := range containers {
		images = append(images, container.Image)
	}
	return images
}

func (containers ContainerList) GetByName(name string) (Container, bool) {
	for _, container := range containers {
		if container.Name == name {
			return container, true
		}
	}
	return Container{}, false
}

func (containers ContainerList) Names() []string {
	names := make([]string, 0, len(containers))
	for _, container := range containers {
		names = append(names, container.Name)
	}
	return names
}

func (list ContainerList) ConfigMountsMap() map[string]model.ContainerVolume {
	var mounts = make(map[string]model.ContainerVolume, len(list))
	for _, container := range list {
		for _, config := range container.ConfigMaps {
			mounts[config.Name] = config
		}
	}
	return mounts
}

func (list ContainerList) VolumeMountsMap() map[string]model.ContainerVolume {
	var mounts = make(map[string]model.ContainerVolume, len(list))
	for _, container := range list {
		for _, volume := range container.VolumeMounts {
			mounts[volume.Name] = volume
		}
	}
	return mounts
}

func (list ContainerList) Copy() ContainerList {
	var cp = make(ContainerList, 0, len(list))
	for _, cont := range list {
		cp = append(cp, cont.Copy())
	}
	return cp
}

func (list ContainerList) Replace(cont Container) (ContainerList, bool) {
	var updated = list.Copy()
	for i, c := range updated {
		if c.Name == cont.Name {
			updated[i] = cont.Copy()
			return updated, true
		}
	}
	return updated, false
}

func (list ContainerList) Filter(pred func(cont Container) bool) ContainerList {
	var filtered = make(ContainerList, 0, len(list))
	for _, cont := range list {
		if pred(cont.Copy()) {
			filtered = append(filtered, cont.Copy())
		}
	}
	return filtered
}

func (list ContainerList) DeleteByName(name string) ContainerList {
	var filtered = make(ContainerList, 0, len(list))
	for i, cont := range list {
		if cont.Name == name {
			return append(append(filtered, list[:i]...), list[i+1:]...)
		}
	}
	return filtered
}

func (list ContainerList) SemanticVersions() []string {
	var versions = make([]string, 0, len(list))
	for _, cont := range list {
		var version, err = semver.ParseTolerant(cont.ToKube().Version())
		if err == nil {
			versions = append(versions, version.String())
		}
	}
	return versions
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package pod

import (
	"context"
	"io"
	"os"
	"time"

	"net/http"

	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/log"
	"github.com/lastbackend/lastbackend/pkg/node/envs"
	"github.com/lastbackend/lastbackend/pkg/node/events"
	"github.com/lastbackend/lastbackend/pkg/util/cleaner"
	)

const (
	BUFFER_SIZE = 1024
	logLevel    = 3
)

func Manage(ctx context.Context, key string, manifest *types.PodManifest) error {
	log.V(logLevel).Debugf("Provision pod: %s", key)

	//==========================================================================
	// Destroy pod =============================================================
	//==========================================================================

	// Call destroy pod
	if manifest.State.Destroy {

		if task := envs.Get().GetState().Tasks().GetTask(key); task != nil {
			log.V(logLevel).Debugf("Cancel pod creating: %s", key)
			task.Cancel()
		}

		p := envs.Get().GetState().Pods().GetPod(key)
		if p == nil {

			ps := types.NewPodStatus()
			ps.SetDestroyed()
			envs.Get().GetState().Pods().AddPod(key, ps)
			events.NewPodStatusEvent(ctx, key)

			return nil
		}

		log.V(logLevel).Debugf("Pod found > destroy it: %s", key)

		Destroy(ctx, key, p)

		p.SetDestroyed()
		envs.Get().GetState().Pods().SetPod(key, p)
		events.NewPodStatusEvent(ctx, key)
		return nil
	}

	//==========================================================================
	// Check containers pod status =============================================
	//==========================================================================

	// Get pod list from current state
	p := envs.Get().GetState().Pods().GetPod(key)
	if p != nil {
		if p.State != types.StateWarning {
			events.NewPodStatusEvent(ctx, key)
			return nil
		}

		if p.State == types.StateError {
			Destroy(ctx, key, p)
		}
	}

	log.V(logLevel).Debugf("Pod not found > create it: %s", key)

	ctx, cancel := context.WithCancel(context.Background())
	envs.Get().GetState().Tasks().AddTask(key, &types.NodeTask{Cancel: cancel})

	status, err := Create(ctx, key, manifest)
	if err != nil {
		log.Errorf("Can not create pod: %s err: %s", key, err.Error())
		status.SetError(err)
	}

	envs.Get().GetState().Pods().SetPod(key, status)
	events.NewPodStatusEvent(ctx, key)
	return nil
}

func Create(ctx context.Context, key string, manifest *types.PodManifest) (*types.PodStatus, error) {

	var (
		err    error
		status = types.NewPodStatus()
	)

	log.V(logLevel).Debugf("Create pod: %s", key)

	//==========================================================================
	// Pull image ==============================================================
	//==========================================================================

	status.SetPull()

	envs.Get().GetState().Pods().AddPod(key, status)
	events.NewPodStatusEvent(ctx, key)

	log.V(logLevel).Debugf("Have %d containers", len(manifest.Template.Containers))
	for _, c := range manifest.Template.Containers {

		log.V(logLevel).Debug("Pull images for pod if needed")

		var secret *types.Secret

		if c.Image.Secret != types.EmptyString {

			log.V(logLevel).Debug("Get secret info from api")

			vs, err := envs.Get().GetRestClient().Secret(c.Image.Secret).Get(ctx)
			if err != nil {
				log.Errorf("Can-not get secret from api: %s", err)
				status.SetError(err)
				Clean(context.Background(), status)
				return status, err
			}

			secret = vs.Decode()
		}

		r, err := envs.Get().GetCRI().ImagePull(ctx, &c.Image, secret)
		if err != nil {
			log.Errorf("Can-not pull image: %s", err)
			status.SetError(err)
			Clean(context.Background(), status)
			return status, err
		}

		io.Copy(os.Stdout, r)
	}

	//==========================================================================
	// Run container ===========================================================
	//==========================================================================

	status.SetStarting()
	status.Steps[types.StepPull] = types.PodStep{
		Ready:     true,
		Timestamp: time.Now().UTC(),
	}

	envs.Get().GetState().Pods().SetPod(key, status)
	events.NewPodStatusEvent(ctx, key)

	var secrets = make(map[string]*types.Secret)
	for _, s := range manifest.Template.Containers {
		for _, e := range s.EnvVars {
			if e.From.Name != types.EmptyString {
				log.V(logLevel).Debug("Get secret info from api")

				vs, err := envs.Get().GetRestClient().Secret(e.From.Name).Get(ctx)
				if err != nil {
					log.Errorf("Can-not get secret from api: %s", err)
					status.SetError(err)
					Clean(context.Background(), status)
					return status, err
				}

				secret := vs.Decode()
				secrets[secret.Meta.Name] = secret
			}
		}
	}

	for _, s := range manifest.Template.Containers {

		//==========================================================================
		// Create container ========================================================
		//==========================================================================

		var c = new(types.PodContainer)
		c.ID, err = envs.Get().GetCRI().ContainerCreate(ctx, &s, secrets)
		if err != nil {
			switch err {
			case context.Canceled:
				log.Errorf("Stop creating container: %s", err.Error())
				Clean(context.Background(), status)
				return status, nil
			}

			log.Errorf("Can-not create container: %s", err)
			c.State.Error = types.PodContainerStateError{
				Error:   true,
				Message: err.Error(),
				Exit: types.PodContainerStateExit{
					Timestamp: time.Now().UTC(),
				},
			}
			return status, err
		}

		if err := containerInspect(context.Background(), status, c); err != nil {
			log.Errorf("Inspect container after create: err %s", err.Error())
			Clean(context.Background(), status)
			return status, err
		}

		//==========================================================================
		// Start container =========================================================
		//==========================================================================

		c.State.Created = types.PodContainerStateCreated{
			Created: time.Now().UTC(),
		}
		status.Containers[c.ID] = c
		envs.Get().GetState().Pods().SetPod(key, status)
		log.V(logLevel).Debugf("Container created: %#v", c)

		if err := envs.Get().GetCRI().ContainerStart(ctx, c.ID); err != nil {
			switch err {
			case context.Canceled:
				log.Errorf("Stop starting container err: %s", err.Error())
				Clean(context.Background(), status)
				return status, nil
			}

			log.Errorf("Can-not start container: %s", err)
			c.State.Error = types.PodContainerStateError{
				Error:   true,
				Message: err.Error(),
				Exit: types.PodContainerStateExit{
					Timestamp: time.Now().UTC(),
				},
			}

			status.Containers[c.ID] = c
			return status, err
		}

		if err := containerInspect(context.Background(), status, c); err != nil {
			log.Errorf("Inspect container after create: err %s", err.Error())
			return status, err
		}

		c.Ready = true
		c.State.Started = types.PodContainerStateStarted{
			Started:   true,
			Timestamp: time.Now().UTC(),
		}
		status.Containers[c.ID] = c
		envs.Get().GetState().Pods().SetPod(key, status)
	}

	status.SetRunning()
	status.Steps[types.StepReady] = types.PodStep{
		Ready:     true,
		Timestamp: time.Now().UTC(),
	}

	status.Network.HostIP = envs.Get().GetCNI().Info(ctx).Addr
	envs.Get().GetState().Pods().SetPod(key, status)
	return status, nil
}

func Clean(ctx context.Context, status *types.PodStatus) {

	for _, c := range status.Containers {
		log.V(logLevel).Debugf("Remove unnecessary container: %s", c.ID)
		if err := envs.Get().GetCRI().ContainerRemove(ctx, c.ID, true, true); err != nil {
			log.Warnf("Can-not remove unnecessary container %s: %s", c.ID, err)
		}
	}

	for _, c := range status.Containers {
		log.V(logLevel).Debugf("Try to clean image: %s", c.Image.Name)
		if err := envs.Get().GetCRI().ImageRemove(ctx, c.Image.Name); err != nil {
			log.Warnf("Can-not remove unnecessary image %s: %s", c.Image.Name, err)
		}
	}
}

func Destroy(ctx context.Context, pod string, status *types.PodStatus) {
	log.V(logLevel).Debugf("Try to remove pod: %s", pod)
	Clean(ctx, status)
	envs.Get().GetState().Pods().DelPod(pod)
}

func Restore(ctx context.Context) error {

	log.V(logLevel).Debug("Runtime restore state")

	cl, err := envs.Get().GetCRI().ContainerList(ctx, true)
	if err != nil {
		log.Errorf("Pods restore error: %s", err)
		return err
	}

	for _, c := range cl {

		log.V(logLevel).Debugf("Pod [%s] > container restore %s", c.Pod, c.ID)

		status := envs.Get().GetState().Pods().GetPod(c.Pod)
		if status == nil {
			status = types.NewPodStatus()
		}

		key := c.Pod

		cs := &types.PodContainer{
			ID: c.ID,
			Image: types.PodContainerImage{
				Name: c.Image,
			},
		}

		switch c.State {
		case types.StateCreated:
			cs.State = types.PodContainerState{
				Created: types.PodContainerStateCreated{
					Created: time.Now().UTC(),
				},
			}
		case types.StateStarted:
			cs.State = types.PodContainerState{
				Started: types.PodContainerStateStarted{
					Started:   true,
					Timestamp: time.Now().UTC(),
				},
			}
			cs.State.Stopped.Stopped = false
		case types.StatusStopped:
			cs.State.Stopped.Stopped = true
			cs.State.Stopped.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
		case types.StateError:

			cs.State.Error.Error = true
			cs.State.Error.Message = c.Status
			cs.State.Error.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
			cs.State.Stopped.Stopped = false
			cs.State.Stopped.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
		}

		if c.Status == types.StatusStopped {
			cs.State.Stopped = types.PodContainerStateStopped{
				Stopped: true,
				Exit: types.PodContainerStateExit{
					Code:      c.ExitCode,
					Timestamp: time.Now().UTC(),
				},
			}
		}

		cs.Ready = true
		status.Containers[cs.ID] = cs

		log.V(logLevel).Debugf("Container restored %s", c.ID)
		envs.Get().GetState().Pods().SetPod(key, status)
		log.V(logLevel).Debugf("Pod restored %#v", status)
	}

	return nil
}

func Logs(ctx context.Context, id string, follow bool, s io.Writer, doneChan chan bool) error {

	log.V(logLevel).Debugf("Get container [%s] logs streaming", id)

	var (
		cri    = envs.Get().GetCRI()
		buffer = make([]byte, BUFFER_SIZE)
		done   = make(chan bool, 1)
	)

	req, err := cri.ContainerLogs(ctx, id, true, true, follow)
	if err != nil {
		log.Errorf("Error get logs stream %s", err)
		return err
	}
	defer func() {
		log.V(logLevel).Debugf("Stop container [%s] logs streaming", id)
		ctx.Done()
		close(done)
		req.Close()
	}()

	go func() {
		for {
			select {
			case <-done:
				req.Close()
				return
			default:

				n, err := cleaner.NewReader(req).Read(buffer)
				if err != nil {

					if err == context.Canceled {
						log.V(logLevel).Debug("Stream is canceled")
						return
					}

					log.Errorf("Error read bytes from stream %s", err)
					doneChan <- true
					return
				}

				_, err = func(p []byte) (n int, err error) {
					n, err = s.Write(p)
					if err != nil {
						log.Errorf("Error write bytes to stream %s", err)
						return n, err
					}

					if f, ok := s.(http.Flusher); ok {
						f.Flush()
					}
					return n, nil
				}(buffer[0:n])

				if err != nil {
					log.Errorf("Error written to stream %s", err)
					done <- true
					return
				}

				for i := 0; i < n; i++ {
					buffer[i] = 0
				}
			}
		}
	}()

	<-doneChan

	return nil
}

func containerInspect(ctx context.Context, status *types.PodStatus, container *types.PodContainer) error {
	info, err := envs.Get().GetCRI().ContainerInspect(ctx, container.ID)
	if err != nil {
		switch err {
		case context.Canceled:
			log.Errorf("Stop inspect container %s: %s", err.Error())
			return nil
		}
		log.Errorf("Can-not inspect container: %s", err)
		return err
	} else {
		container.Image = types.PodContainerImage{
			Name: info.Image,
		}
		if info.Status == types.StatusStopped {
			container.State.Stopped = types.PodContainerStateStopped{
				Stopped: true,
				Exit: types.PodContainerStateExit{
					Code:      info.ExitCode,
					Timestamp: time.Now().UTC(),
				},
			}
		}
	}

	if status.Network.PodIP == "" {
		status.Network.PodIP = info.Network.IPAddress
	}

	return nil
}
<commit_msg>fix types<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package pod

import (
	"context"
	"io"
	"os"
	"time"

	"net/http"

	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/log"
	"github.com/lastbackend/lastbackend/pkg/node/envs"
	"github.com/lastbackend/lastbackend/pkg/node/events"
	"github.com/lastbackend/lastbackend/pkg/util/cleaner"
	)

const (
	BUFFER_SIZE = 1024
	logLevel    = 3
)

func Manage(ctx context.Context, key string, manifest *types.PodManifest) error {
	log.V(logLevel).Debugf("Provision pod: %s", key)

	//==========================================================================
	// Destroy pod =============================================================
	//==========================================================================

	// Call destroy pod
	if manifest.State.Destroy {

		if task := envs.Get().GetState().Tasks().GetTask(key); task != nil {
			log.V(logLevel).Debugf("Cancel pod creating: %s", key)
			task.Cancel()
		}

		p := envs.Get().GetState().Pods().GetPod(key)
		if p == nil {

			ps := types.NewPodStatus()
			ps.SetDestroyed()
			envs.Get().GetState().Pods().AddPod(key, ps)
			events.NewPodStatusEvent(ctx, key)

			return nil
		}

		log.V(logLevel).Debugf("Pod found > destroy it: %s", key)

		Destroy(ctx, key, p)

		p.SetDestroyed()
		envs.Get().GetState().Pods().SetPod(key, p)
		events.NewPodStatusEvent(ctx, key)
		return nil
	}

	//==========================================================================
	// Check containers pod status =============================================
	//==========================================================================

	// Get pod list from current state
	p := envs.Get().GetState().Pods().GetPod(key)
	if p != nil {
		if p.State != types.StateWarning {
			events.NewPodStatusEvent(ctx, key)
			return nil
		}

		if p.State == types.StateError {
			Destroy(ctx, key, p)
		}
	}

	log.V(logLevel).Debugf("Pod not found > create it: %s", key)

	ctx, cancel := context.WithCancel(context.Background())
	envs.Get().GetState().Tasks().AddTask(key, &types.NodeTask{Cancel: cancel})

	status, err := Create(ctx, key, manifest)
	if err != nil {
		log.Errorf("Can not create pod: %s err: %s", key, err.Error())
		status.SetError(err)
	}

	envs.Get().GetState().Pods().SetPod(key, status)
	events.NewPodStatusEvent(ctx, key)
	return nil
}

func Create(ctx context.Context, key string, manifest *types.PodManifest) (*types.PodStatus, error) {

	var (
		err    error
		status = types.NewPodStatus()
	)

	log.V(logLevel).Debugf("Create pod: %s", key)

	//==========================================================================
	// Pull image ==============================================================
	//==========================================================================

	status.SetPull()

	envs.Get().GetState().Pods().AddPod(key, status)
	events.NewPodStatusEvent(ctx, key)

	log.V(logLevel).Debugf("Have %d containers", len(manifest.Template.Containers))
	for _, c := range manifest.Template.Containers {

		log.V(logLevel).Debug("Pull images for pod if needed")

		var secret *types.Secret

		if c.Image.Secret != types.EmptyString {

			log.V(logLevel).Debug("Get secret info from api")

			vs, err := envs.Get().GetRestClient().Secret(c.Image.Secret).Get(ctx)
			if err != nil {
				log.Errorf("Can-not get secret from api: %s", err)
				status.SetError(err)
				Clean(context.Background(), status)
				return status, err
			}

			secret = vs.Decode()
		}

		r, err := envs.Get().GetCRI().ImagePull(ctx, &c.Image, secret)
		if err != nil {
			log.Errorf("Can-not pull image: %s", err)
			status.SetError(err)
			Clean(context.Background(), status)
			return status, err
		}

		io.Copy(os.Stdout, r)
	}

	//==========================================================================
	// Run container ===========================================================
	//==========================================================================

	status.SetStarting()
	status.Steps[types.StepPull] = types.PodStep{
		Ready:     true,
		Timestamp: time.Now().UTC(),
	}

	envs.Get().GetState().Pods().SetPod(key, status)
	events.NewPodStatusEvent(ctx, key)

	var secrets = make(map[string]*types.Secret)
	for _, s := range manifest.Template.Containers {
		for _, e := range s.EnvVars {
			if e.From.Name != types.EmptyString {
				log.V(logLevel).Debug("Get secret info from api")

				vs, err := envs.Get().GetRestClient().Secret(e.From.Name).Get(ctx)
				if err != nil {
					log.Errorf("Can-not get secret from api: %s", err)
					status.SetError(err)
					Clean(context.Background(), status)
					return status, err
				}

				secret := vs.Decode()
				secrets[secret.Meta.Name] = secret
			}
		}
	}

	for _, s := range manifest.Template.Containers {

		//==========================================================================
		// Create container ========================================================
		//==========================================================================

		var c = new(types.PodContainer)
		c.ID, err = envs.Get().GetCRI().ContainerCreate(ctx, s, secrets)
		if err != nil {
			switch err {
			case context.Canceled:
				log.Errorf("Stop creating container: %s", err.Error())
				Clean(context.Background(), status)
				return status, nil
			}

			log.Errorf("Can-not create container: %s", err)
			c.State.Error = types.PodContainerStateError{
				Error:   true,
				Message: err.Error(),
				Exit: types.PodContainerStateExit{
					Timestamp: time.Now().UTC(),
				},
			}
			return status, err
		}

		if err := containerInspect(context.Background(), status, c); err != nil {
			log.Errorf("Inspect container after create: err %s", err.Error())
			Clean(context.Background(), status)
			return status, err
		}

		//==========================================================================
		// Start container =========================================================
		//==========================================================================

		c.State.Created = types.PodContainerStateCreated{
			Created: time.Now().UTC(),
		}
		status.Containers[c.ID] = c
		envs.Get().GetState().Pods().SetPod(key, status)
		log.V(logLevel).Debugf("Container created: %#v", c)

		if err := envs.Get().GetCRI().ContainerStart(ctx, c.ID); err != nil {
			switch err {
			case context.Canceled:
				log.Errorf("Stop starting container err: %s", err.Error())
				Clean(context.Background(), status)
				return status, nil
			}

			log.Errorf("Can-not start container: %s", err)
			c.State.Error = types.PodContainerStateError{
				Error:   true,
				Message: err.Error(),
				Exit: types.PodContainerStateExit{
					Timestamp: time.Now().UTC(),
				},
			}

			status.Containers[c.ID] = c
			return status, err
		}

		if err := containerInspect(context.Background(), status, c); err != nil {
			log.Errorf("Inspect container after create: err %s", err.Error())
			return status, err
		}

		c.Ready = true
		c.State.Started = types.PodContainerStateStarted{
			Started:   true,
			Timestamp: time.Now().UTC(),
		}
		status.Containers[c.ID] = c
		envs.Get().GetState().Pods().SetPod(key, status)
	}

	status.SetRunning()
	status.Steps[types.StepReady] = types.PodStep{
		Ready:     true,
		Timestamp: time.Now().UTC(),
	}

	status.Network.HostIP = envs.Get().GetCNI().Info(ctx).Addr
	envs.Get().GetState().Pods().SetPod(key, status)
	return status, nil
}

func Clean(ctx context.Context, status *types.PodStatus) {

	for _, c := range status.Containers {
		log.V(logLevel).Debugf("Remove unnecessary container: %s", c.ID)
		if err := envs.Get().GetCRI().ContainerRemove(ctx, c.ID, true, true); err != nil {
			log.Warnf("Can-not remove unnecessary container %s: %s", c.ID, err)
		}
	}

	for _, c := range status.Containers {
		log.V(logLevel).Debugf("Try to clean image: %s", c.Image.Name)
		if err := envs.Get().GetCRI().ImageRemove(ctx, c.Image.Name); err != nil {
			log.Warnf("Can-not remove unnecessary image %s: %s", c.Image.Name, err)
		}
	}
}

func Destroy(ctx context.Context, pod string, status *types.PodStatus) {
	log.V(logLevel).Debugf("Try to remove pod: %s", pod)
	Clean(ctx, status)
	envs.Get().GetState().Pods().DelPod(pod)
}

func Restore(ctx context.Context) error {

	log.V(logLevel).Debug("Runtime restore state")

	cl, err := envs.Get().GetCRI().ContainerList(ctx, true)
	if err != nil {
		log.Errorf("Pods restore error: %s", err)
		return err
	}

	for _, c := range cl {

		log.V(logLevel).Debugf("Pod [%s] > container restore %s", c.Pod, c.ID)

		status := envs.Get().GetState().Pods().GetPod(c.Pod)
		if status == nil {
			status = types.NewPodStatus()
		}

		key := c.Pod

		cs := &types.PodContainer{
			ID: c.ID,
			Image: types.PodContainerImage{
				Name: c.Image,
			},
		}

		switch c.State {
		case types.StateCreated:
			cs.State = types.PodContainerState{
				Created: types.PodContainerStateCreated{
					Created: time.Now().UTC(),
				},
			}
		case types.StateStarted:
			cs.State = types.PodContainerState{
				Started: types.PodContainerStateStarted{
					Started:   true,
					Timestamp: time.Now().UTC(),
				},
			}
			cs.State.Stopped.Stopped = false
		case types.StatusStopped:
			cs.State.Stopped.Stopped = true
			cs.State.Stopped.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
		case types.StateError:

			cs.State.Error.Error = true
			cs.State.Error.Message = c.Status
			cs.State.Error.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
			cs.State.Stopped.Stopped = false
			cs.State.Stopped.Exit = types.PodContainerStateExit{
				Code:      c.ExitCode,
				Timestamp: time.Now().UTC(),
			}
			cs.State.Started.Started = false
		}

		if c.Status == types.StatusStopped {
			cs.State.Stopped = types.PodContainerStateStopped{
				Stopped: true,
				Exit: types.PodContainerStateExit{
					Code:      c.ExitCode,
					Timestamp: time.Now().UTC(),
				},
			}
		}

		cs.Ready = true
		status.Containers[cs.ID] = cs

		log.V(logLevel).Debugf("Container restored %s", c.ID)
		envs.Get().GetState().Pods().SetPod(key, status)
		log.V(logLevel).Debugf("Pod restored %#v", status)
	}

	return nil
}

func Logs(ctx context.Context, id string, follow bool, s io.Writer, doneChan chan bool) error {

	log.V(logLevel).Debugf("Get container [%s] logs streaming", id)

	var (
		cri    = envs.Get().GetCRI()
		buffer = make([]byte, BUFFER_SIZE)
		done   = make(chan bool, 1)
	)

	req, err := cri.ContainerLogs(ctx, id, true, true, follow)
	if err != nil {
		log.Errorf("Error get logs stream %s", err)
		return err
	}
	defer func() {
		log.V(logLevel).Debugf("Stop container [%s] logs streaming", id)
		ctx.Done()
		close(done)
		req.Close()
	}()

	go func() {
		for {
			select {
			case <-done:
				req.Close()
				return
			default:

				n, err := cleaner.NewReader(req).Read(buffer)
				if err != nil {

					if err == context.Canceled {
						log.V(logLevel).Debug("Stream is canceled")
						return
					}

					log.Errorf("Error read bytes from stream %s", err)
					doneChan <- true
					return
				}

				_, err = func(p []byte) (n int, err error) {
					n, err = s.Write(p)
					if err != nil {
						log.Errorf("Error write bytes to stream %s", err)
						return n, err
					}

					if f, ok := s.(http.Flusher); ok {
						f.Flush()
					}
					return n, nil
				}(buffer[0:n])

				if err != nil {
					log.Errorf("Error written to stream %s", err)
					done <- true
					return
				}

				for i := 0; i < n; i++ {
					buffer[i] = 0
				}
			}
		}
	}()

	<-doneChan

	return nil
}

func containerInspect(ctx context.Context, status *types.PodStatus, container *types.PodContainer) error {
	info, err := envs.Get().GetCRI().ContainerInspect(ctx, container.ID)
	if err != nil {
		switch err {
		case context.Canceled:
			log.Errorf("Stop inspect container %s: %s", err.Error())
			return nil
		}
		log.Errorf("Can-not inspect container: %s", err)
		return err
	} else {
		container.Image = types.PodContainerImage{
			Name: info.Image,
		}
		if info.Status == types.StatusStopped {
			container.State.Stopped = types.PodContainerStateStopped{
				Stopped: true,
				Exit: types.PodContainerStateExit{
					Code:      info.ExitCode,
					Timestamp: time.Now().UTC(),
				},
			}
		}
	}

	if status.Network.PodIP == "" {
		status.Network.PodIP = info.Network.IPAddress
	}

	return nil
}
<|endoftext|>


<commit_before>package worker

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"time"

	"github.com/ncarlier/webhookd/pkg/logger"
	"github.com/ncarlier/webhookd/pkg/tools"
)

// ChanWriter is a simple writer to a channel of byte.
type ChanWriter struct {
	ByteChan chan []byte
}

func (c *ChanWriter) Write(p []byte) (int, error) {
	c.ByteChan <- p
	return len(p), nil
}

var (
	workingdir = os.Getenv("APP_WORKING_DIR")
)

func runScript(work *WorkRequest) (string, error) {
	if workingdir == "" {
		workingdir = os.TempDir()
	}

	logger.Info.Println("Executing script", work.Script, "...")
	binary, err := exec.LookPath(work.Script)
	if err != nil {
		return "", err
	}

	// Exec script with args...
	cmd := exec.Command(binary, work.Payload)
	// with env variables...
	cmd.Env = append(os.Environ(), work.Args...)

	// Open the out file for writing
	logFilename := path.Join(workingdir, fmt.Sprintf("%s_%s.txt", tools.ToSnakeCase(work.Name), time.Now().Format("20060102_1504")))
	logFile, err := os.Create(logFilename)
	if err != nil {
		return "", err
	}
	defer logFile.Close()
	logger.Debug.Println("Writing output to file: ", logFilename, "...")

	wLogFile := bufio.NewWriter(logFile)

	r, w := io.Pipe()
	cmd.Stdout = w
	cmd.Stderr = w

	// Start the script...
	err = cmd.Start()
	if err != nil {
		return logFilename, err
	}

	// Write script output to log file and the work message cahnnel.
	go func(reader io.Reader) {
		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {
			if work.Closed {
				logger.Error.Println("Unable to write into the work channel. Work request closed.")
				return
			}
			// writing to the work channel
			line := scanner.Text()
			work.MessageChan <- []byte(line)
			// writing to outfile
			if _, err := wLogFile.WriteString(line + "\n"); err != nil {
				logger.Error.Println("Error while writing into the log file:", logFilename, err)
			}
			if err = wLogFile.Flush(); err != nil {
				logger.Error.Println("Error while flushing the log file:", logFilename, err)
			}
		}
		if err := scanner.Err(); err != nil {
			logger.Error.Println("Error scanning the script stdout: ", logFilename, err)
		}
	}(r)

	timer := time.AfterFunc(time.Duration(work.Timeout)*time.Second, func() {
		logger.Warning.Printf("Timeout reached (%ds). Killing script: %s\n", work.Timeout, work.Script)
		cmd.Process.Kill()
	})
	err = cmd.Wait()
	if err != nil {
		timer.Stop()
		logger.Info.Println("Script", work.Script, "executed with ERROR.")
		return logFilename, err
	}
	timer.Stop()
	logger.Info.Println("Script", work.Script, "executed with SUCCESS")
	return logFilename, nil
}
<commit_msg>fix(script): kill script process and sub process on timeout<commit_after>package worker

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"syscall"
	"time"

	"github.com/ncarlier/webhookd/pkg/logger"
	"github.com/ncarlier/webhookd/pkg/tools"
)

// ChanWriter is a simple writer to a channel of byte.
type ChanWriter struct {
	ByteChan chan []byte
}

func (c *ChanWriter) Write(p []byte) (int, error) {
	c.ByteChan <- p
	return len(p), nil
}

var (
	workingdir = os.Getenv("APP_WORKING_DIR")
)

func runScript(work *WorkRequest) (string, error) {
	if workingdir == "" {
		workingdir = os.TempDir()
	}

	logger.Info.Println("Executing script", work.Script, "...")
	binary, err := exec.LookPath(work.Script)
	if err != nil {
		return "", err
	}

	// Exec script with args...
	cmd := exec.Command(binary, work.Payload)
	// with env variables...
	cmd.Env = append(os.Environ(), work.Args...)
	// using a process group...
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	// Open the out file for writing
	logFilename := path.Join(workingdir, fmt.Sprintf("%s_%s.txt", tools.ToSnakeCase(work.Name), time.Now().Format("20060102_1504")))
	logFile, err := os.Create(logFilename)
	if err != nil {
		return "", err
	}
	defer logFile.Close()
	logger.Debug.Println("Writing output to file: ", logFilename, "...")

	wLogFile := bufio.NewWriter(logFile)

	r, w := io.Pipe()
	cmd.Stdout = w
	cmd.Stderr = w

	// Start the script...
	err = cmd.Start()
	if err != nil {
		return logFilename, err
	}

	// Write script output to log file and the work message cahnnel.
	go func(reader io.Reader) {
		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {
			if work.Closed {
				logger.Error.Println("Unable to write into the work channel. Work request closed.")
				return
			}
			// writing to the work channel
			line := scanner.Text()
			work.MessageChan <- []byte(line)
			// writing to outfile
			if _, err := wLogFile.WriteString(line + "\n"); err != nil {
				logger.Error.Println("Error while writing into the log file:", logFilename, err)
			}
			if err = wLogFile.Flush(); err != nil {
				logger.Error.Println("Error while flushing the log file:", logFilename, err)
			}
		}
		if err := scanner.Err(); err != nil {
			logger.Error.Println("Error scanning the script stdout: ", logFilename, err)
		}
	}(r)

	timer := time.AfterFunc(time.Duration(work.Timeout)*time.Second, func() {
		logger.Warning.Printf("Timeout reached (%ds). Killing script: %s\n", work.Timeout, work.Script)
		syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
	})
	err = cmd.Wait()
	if err != nil {
		timer.Stop()
		logger.Info.Println("Script", work.Script, "executed with ERROR.")
		return logFilename, err
	}
	timer.Stop()
	logger.Info.Println("Script", work.Script, "executed with SUCCESS")
	return logFilename, nil
}
<|endoftext|>


<commit_before>package tests

import (
	"bytes"
	"context"
	"net/http"
	"testing"
	"time"

	"github.com/keel-hq/keel/types"

	apps_v1 "k8s.io/api/apps/v1"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	log "github.com/sirupsen/logrus"
)

var dockerHub0150Webhook = `{
	"push_data": {
		"pushed_at": 1497467660,
		"images": [],
		"tag": "0.0.15",
		"pusher": "karolisr"
	},
	"repository": {
		"status": "Active",
		"description": "",
		"is_trusted": false,
		"repo_url": "https://hub.docker.com/r/webhook-demo",
		"owner": "karolisr",
		"is_official": false,
		"is_private": false,
		"name": "keel",
		"namespace": "karolisr",
		"star_count": 0,
		"comment_count": 0,
		"date_created": 1497032538,
		"repo_name": "karolisr/webhook-demo"
	}
}`

func TestWebhooksSemverUpdate(t *testing.T) {

	// stop := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	// defer close(ctx)
	defer cancel()

	// go startKeel(ctx)
	keel := &KeelCmd{}
	go func() {
		err := keel.Start(ctx)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to start Keel process")
		}
	}()

	defer func() {
		err := keel.Stop()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to stop Keel process")
		}
	}()

	_, kcs := getKubernetesClient()

	t.Run("UpdateThroughDockerHubWebhook", func(t *testing.T) {

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:        "deployment-1",
				Namespace:   testNamespace,
				Labels:      map[string]string{types.KeelPolicyLabel: "all"},
				Annotations: map[string]string{},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "karolisr/webhook-demo:0.0.14",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		// giving some time to get started
		// TODO: replace with a readiness check function to wait for 1/1 READY
		time.Sleep(2 * time.Second)

		// sending webhook
		client := http.DefaultClient
		buf := bytes.NewBufferString(dockerHub0150Webhook)
		req, err := http.NewRequest("POST", "http://localhost:9300/v1/webhooks/dockerhub", buf)
		if err != nil {
			t.Fatalf("failed to create req: %s", err)
		}
		resp, err := client.Do(req)
		if err != nil {
			t.Errorf("failed to make a webhook request to keel: %s", err)
		}

		if resp.StatusCode != 200 {
			t.Errorf("unexpected webhook response from keel: %d", resp.StatusCode)
		}

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "karolisr/webhook-demo:0.0.15")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})
}

func TestPollingSemverUpdate(t *testing.T) {

	// stop := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	// defer close(ctx)
	defer cancel()

	// go startKeel(ctx)
	keel := &KeelCmd{}
	go func() {
		err := keel.Start(ctx)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to start Keel process")
		}
	}()

	defer func() {
		err := keel.Stop()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to stop Keel process")
		}
	}()

	_, kcs := getKubernetesClient()

	t.Run("UpdateThroughDockerHubPollingA", func(t *testing.T) {
		// UpdateThroughDockerHubPollingA tests a polling trigger when we have a higher version
		// but without a pre-release tag and a lower version with pre-release. The version of the deployment
		// is with pre-prerealse so we should upgrade to that one.

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:      "deployment-1",
				Namespace: testNamespace,
				Labels: map[string]string{
					types.KeelPolicyLabel:  "major",
					types.KeelTriggerLabel: "poll",
				},
				Annotations: map[string]string{
					types.KeelPollScheduleAnnotation: "@every 2s",
				},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "keelhq/push-workflow-example:0.1.0-dev",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "keelhq/push-workflow-example:0.5.0-dev")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})

	t.Run("UpdateThroughDockerHubPollingB", func(t *testing.T) {
		// UpdateThroughDockerHubPollingA tests a polling trigger when we have a higher version
		// but without a pre-release tag and a lower version with pre-release. The version of the deployment
		// is without pre-prerealse

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:      "deployment-2",
				Namespace: testNamespace,
				Labels: map[string]string{
					types.KeelPolicyLabel:  "major",
					types.KeelTriggerLabel: "poll",
				},
				Annotations: map[string]string{
					types.KeelPollScheduleAnnotation: "@every 2s",
				},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "keelhq/push-workflow-example:0.1.0",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "keelhq/push-workflow-example:0.10.0")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})
}
<commit_msg>approvals require test<commit_after>package tests

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"testing"
	"time"

	"github.com/keel-hq/keel/types"

	apps_v1 "k8s.io/api/apps/v1"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	log "github.com/sirupsen/logrus"
)

var dockerHub0150Webhook = `{
	"push_data": {
		"pushed_at": 1497467660,
		"images": [],
		"tag": "0.0.15",
		"pusher": "karolisr"
	},
	"repository": {
		"status": "Active",
		"description": "",
		"is_trusted": false,
		"repo_url": "https://hub.docker.com/r/webhook-demo",
		"owner": "karolisr",
		"is_official": false,
		"is_private": false,
		"name": "keel",
		"namespace": "karolisr",
		"star_count": 0,
		"comment_count": 0,
		"date_created": 1497032538,
		"repo_name": "karolisr/webhook-demo"
	}
}`

func TestWebhooksSemverUpdate(t *testing.T) {

	// stop := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	// defer close(ctx)
	defer cancel()

	// go startKeel(ctx)
	keel := &KeelCmd{}
	go func() {
		err := keel.Start(ctx)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to start Keel process")
		}
	}()

	defer func() {
		err := keel.Stop()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to stop Keel process")
		}
	}()

	_, kcs := getKubernetesClient()

	t.Run("UpdateThroughDockerHubWebhook", func(t *testing.T) {

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:        "deployment-1",
				Namespace:   testNamespace,
				Labels:      map[string]string{types.KeelPolicyLabel: "all"},
				Annotations: map[string]string{},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "karolisr/webhook-demo:0.0.14",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		// giving some time to get started
		// TODO: replace with a readiness check function to wait for 1/1 READY
		time.Sleep(2 * time.Second)

		// sending webhook
		client := http.DefaultClient
		buf := bytes.NewBufferString(dockerHub0150Webhook)
		req, err := http.NewRequest("POST", "http://localhost:9300/v1/webhooks/dockerhub", buf)
		if err != nil {
			t.Fatalf("failed to create req: %s", err)
		}
		resp, err := client.Do(req)
		if err != nil {
			t.Errorf("failed to make a webhook request to keel: %s", err)
		}

		if resp.StatusCode != 200 {
			t.Errorf("unexpected webhook response from keel: %d", resp.StatusCode)
		}

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "karolisr/webhook-demo:0.0.15")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})
}

func TestPollingSemverUpdate(t *testing.T) {

	// stop := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	// defer close(ctx)
	defer cancel()

	// go startKeel(ctx)
	keel := &KeelCmd{}
	go func() {
		err := keel.Start(ctx)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to start Keel process")
		}
	}()

	defer func() {
		err := keel.Stop()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to stop Keel process")
		}
	}()

	_, kcs := getKubernetesClient()

	t.Run("UpdateThroughDockerHubPollingA", func(t *testing.T) {
		// UpdateThroughDockerHubPollingA tests a polling trigger when we have a higher version
		// but without a pre-release tag and a lower version with pre-release. The version of the deployment
		// is with pre-prerealse so we should upgrade to that one.

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:      "deployment-1",
				Namespace: testNamespace,
				Labels: map[string]string{
					types.KeelPolicyLabel:  "major",
					types.KeelTriggerLabel: "poll",
				},
				Annotations: map[string]string{
					types.KeelPollScheduleAnnotation: "@every 2s",
				},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "keelhq/push-workflow-example:0.1.0-dev",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "keelhq/push-workflow-example:0.5.0-dev")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})

	t.Run("UpdateThroughDockerHubPollingB", func(t *testing.T) {
		// UpdateThroughDockerHubPollingA tests a polling trigger when we have a higher version
		// but without a pre-release tag and a lower version with pre-release. The version of the deployment
		// is without pre-prerealse

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:      "deployment-2",
				Namespace: testNamespace,
				Labels: map[string]string{
					types.KeelPolicyLabel:  "major",
					types.KeelTriggerLabel: "poll",
				},
				Annotations: map[string]string{
					types.KeelPollScheduleAnnotation: "@every 2s",
				},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "keelhq/push-workflow-example:0.1.0",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "keelhq/push-workflow-example:0.10.0")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})
}

func TestApprovals(t *testing.T) {

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// go startKeel(ctx)
	keel := &KeelCmd{}
	go func() {
		err := keel.Start(ctx)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to start Keel process")
		}
	}()

	defer func() {
		err := keel.Stop()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Error("failed to stop Keel process")
		}
	}()

	_, kcs := getKubernetesClient()

	t.Run("CreateDeploymentWithApprovals", func(t *testing.T) {

		testNamespace := createNamespaceForTest()
		defer deleteTestNamespace(testNamespace)

		dep := &apps_v1.Deployment{
			meta_v1.TypeMeta{},
			meta_v1.ObjectMeta{
				Name:      "deployment-1",
				Namespace: testNamespace,
				Labels: map[string]string{
					types.KeelPolicyLabel:           "all",
					types.KeelMinimumApprovalsLabel: "1",
					types.KeelApprovalDeadlineLabel: "5",
				},
				Annotations: map[string]string{},
			},
			apps_v1.DeploymentSpec{
				Selector: &meta_v1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "wd-1",
					},
				},
				Template: v1.PodTemplateSpec{
					ObjectMeta: meta_v1.ObjectMeta{
						Labels: map[string]string{
							"app":     "wd-1",
							"release": "1",
						},
					},
					Spec: v1.PodSpec{
						Containers: []v1.Container{
							v1.Container{
								Name:  "wd-1",
								Image: "karolisr/webhook-demo:0.0.14",
							},
						},
					},
				},
			},
			apps_v1.DeploymentStatus{},
		}

		_, err := kcs.AppsV1().Deployments(testNamespace).Create(dep)
		if err != nil {
			t.Fatalf("failed to create deployment: %s", err)
		}
		// giving some time to get started
		// TODO: replace with a readiness check function to wait for 1/1 READY
		time.Sleep(2 * time.Second)

		// sending webhook
		client := http.DefaultClient
		buf := bytes.NewBufferString(dockerHub0150Webhook)
		req, err := http.NewRequest("POST", "http://localhost:9300/v1/webhooks/dockerhub", buf)
		if err != nil {
			t.Fatalf("failed to create req: %s", err)
		}
		resp, err := client.Do(req)
		if err != nil {
			t.Errorf("failed to make a webhook request to keel: %s", err)
		}

		if resp.StatusCode != 200 {
			t.Errorf("unexpected webhook response from keel: %d", resp.StatusCode)
		}

		time.Sleep(2 * time.Second)

		// req2, err := http.NewRequest("GET", "http://localhost:9300/v1/approvals", nil)

		resp, err = client.Get("http://localhost:9300/v1/approvals")
		if err != nil {
			t.Fatalf("failed to get approvals: %s", err)
		}

		var approvals []*types.Approval
		dec := json.NewDecoder(resp.Body)
		defer resp.Body.Close()
		err = dec.Decode(&approvals)
		if err != nil {
			t.Fatalf("failed to decode approvals resp: %s", err)
		}

		if len(approvals) != 1 {
			t.Errorf("expected to find 1 approval, got: %d", len(approvals))
		} else {
			if approvals[0].VotesRequired != 1 {
				t.Errorf("expected 1 required vote, got: %d", approvals[0].VotesRequired)
			}
			log.Infof("approvals deadline: %s, time since: %v", approvals[0].Deadline, time.Since(approvals[0].Deadline))
			if time.Since(approvals[0].Deadline) > -4*time.Hour && time.Since(approvals[0].Deadline) < -5*time.Hour {
				t.Errorf("deadline is for: %s", approvals[0].Deadline)
			}
		}

		ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
		defer cancel()

		err = waitFor(ctx, kcs, testNamespace, dep.ObjectMeta.Name, "karolisr/webhook-demo:0.0.14")
		if err != nil {
			t.Errorf("update failed: %s", err)
		}
	})
}
<|endoftext|>


<commit_before>package system

import (
	"os"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
)

type PS interface {
	CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error)
	DiskUsage(mountPointFilter []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error)
	NetIO() ([]net.IOCountersStat, error)
	NetProto() ([]net.ProtoCountersStat, error)
	DiskIO(names []string) (map[string]disk.IOCountersStat, error)
	VMStat() (*mem.VirtualMemoryStat, error)
	SwapStat() (*mem.SwapMemoryStat, error)
	NetConnections() ([]net.ConnectionStat, error)
}

type PSDiskDeps interface {
	Partitions(all bool) ([]disk.PartitionStat, error)
	OSGetenv(key string) string
	OSStat(name string) (os.FileInfo, error)
	PSDiskUsage(path string) (*disk.UsageStat, error)
}

func add(acc telegraf.Accumulator,
	name string, val float64, tags map[string]string) {
	if val >= 0 {
		acc.AddFields(name, map[string]interface{}{"value": val}, tags)
	}
}

func newSystemPS() *systemPS {
	return &systemPS{&systemPSDisk{}}
}

type systemPS struct {
	PSDiskDeps
}

type systemPSDisk struct{}

func (s *systemPS) CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error) {
	var cpuTimes []cpu.TimesStat
	if perCPU {
		if perCPUTimes, err := cpu.Times(true); err == nil {
			cpuTimes = append(cpuTimes, perCPUTimes...)
		} else {
			return nil, err
		}
	}
	if totalCPU {
		if totalCPUTimes, err := cpu.Times(false); err == nil {
			cpuTimes = append(cpuTimes, totalCPUTimes...)
		} else {
			return nil, err
		}
	}
	return cpuTimes, nil
}

func (s *systemPS) DiskUsage(
	mountPointFilter []string,
	fstypeExclude []string,
) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
	parts, err := s.Partitions(true)
	if err != nil {
		return nil, nil, err
	}

	// Make a "set" out of the filter slice
	mountPointFilterSet := make(map[string]bool)
	for _, filter := range mountPointFilter {
		mountPointFilterSet[filter] = true
	}
	fstypeExcludeSet := make(map[string]bool)
	for _, filter := range fstypeExclude {
		fstypeExcludeSet[filter] = true
	}

	var usage []*disk.UsageStat
	var partitions []*disk.PartitionStat

	for i := range parts {
		p := parts[i]

		if len(mountPointFilter) > 0 {
			// If the mount point is not a member of the filter set,
			// don't gather info on it.
			if _, ok := mountPointFilterSet[p.Mountpoint]; !ok {
				continue
			}
		}

		// If the mount point is a member of the exclude set,
		// don't gather info on it.
		if _, ok := fstypeExcludeSet[p.Fstype]; ok {
			continue
		}

		mountpoint := s.OSGetenv("HOST_MOUNT_PREFIX") + p.Mountpoint
		if _, err := s.OSStat(mountpoint); err != nil {
			continue
		}
		du, err := s.PSDiskUsage(mountpoint)
		if err != nil {
			continue
		}
		du.Path = p.Mountpoint
		du.Fstype = p.Fstype
		usage = append(usage, du)
		partitions = append(partitions, &p)
	}

	return usage, partitions, nil
}

func (s *systemPS) NetProto() ([]net.ProtoCountersStat, error) {
	return net.ProtoCounters(nil)
}

func (s *systemPS) NetIO() ([]net.IOCountersStat, error) {
	return net.IOCounters(true)
}

func (s *systemPS) NetConnections() ([]net.ConnectionStat, error) {
	return net.Connections("all")
}

func (s *systemPS) DiskIO(names []string) (map[string]disk.IOCountersStat, error) {
	m, err := disk.IOCounters(names...)
	if err == internal.NotImplementedError {
		return nil, nil
	}

	return m, err
}

func (s *systemPS) VMStat() (*mem.VirtualMemoryStat, error) {
	return mem.VirtualMemory()
}

func (s *systemPS) SwapStat() (*mem.SwapMemoryStat, error) {
	return mem.SwapMemory()
}

func (s *systemPSDisk) Partitions(all bool) ([]disk.PartitionStat, error) {
	return disk.Partitions(all)
}

func (s *systemPSDisk) OSGetenv(key string) string {
	return os.Getenv(key)
}

func (s *systemPSDisk) OSStat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func (s *systemPSDisk) PSDiskUsage(path string) (*disk.UsageStat, error) {
	return disk.Usage(path)
}
<commit_msg>Always ignore autofs filesystems in disk input (#3440)<commit_after>package system

import (
	"os"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
)

type PS interface {
	CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error)
	DiskUsage(mountPointFilter []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error)
	NetIO() ([]net.IOCountersStat, error)
	NetProto() ([]net.ProtoCountersStat, error)
	DiskIO(names []string) (map[string]disk.IOCountersStat, error)
	VMStat() (*mem.VirtualMemoryStat, error)
	SwapStat() (*mem.SwapMemoryStat, error)
	NetConnections() ([]net.ConnectionStat, error)
}

type PSDiskDeps interface {
	Partitions(all bool) ([]disk.PartitionStat, error)
	OSGetenv(key string) string
	OSStat(name string) (os.FileInfo, error)
	PSDiskUsage(path string) (*disk.UsageStat, error)
}

func add(acc telegraf.Accumulator,
	name string, val float64, tags map[string]string) {
	if val >= 0 {
		acc.AddFields(name, map[string]interface{}{"value": val}, tags)
	}
}

func newSystemPS() *systemPS {
	return &systemPS{&systemPSDisk{}}
}

type systemPS struct {
	PSDiskDeps
}

type systemPSDisk struct{}

func (s *systemPS) CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error) {
	var cpuTimes []cpu.TimesStat
	if perCPU {
		if perCPUTimes, err := cpu.Times(true); err == nil {
			cpuTimes = append(cpuTimes, perCPUTimes...)
		} else {
			return nil, err
		}
	}
	if totalCPU {
		if totalCPUTimes, err := cpu.Times(false); err == nil {
			cpuTimes = append(cpuTimes, totalCPUTimes...)
		} else {
			return nil, err
		}
	}
	return cpuTimes, nil
}

func (s *systemPS) DiskUsage(
	mountPointFilter []string,
	fstypeExclude []string,
) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
	parts, err := s.Partitions(true)
	if err != nil {
		return nil, nil, err
	}

	// Make a "set" out of the filter slice
	mountPointFilterSet := make(map[string]bool)
	for _, filter := range mountPointFilter {
		mountPointFilterSet[filter] = true
	}
	fstypeExcludeSet := make(map[string]bool)
	for _, filter := range fstypeExclude {
		fstypeExcludeSet[filter] = true
	}

	// Autofs mounts indicate a potential mount, the partition will also be
	// listed with the actual filesystem when mounted.  Ignore the autofs
	// partition to avoid triggering a mount.
	fstypeExcludeSet["autofs"] = true

	var usage []*disk.UsageStat
	var partitions []*disk.PartitionStat

	for i := range parts {
		p := parts[i]

		if len(mountPointFilter) > 0 {
			// If the mount point is not a member of the filter set,
			// don't gather info on it.
			if _, ok := mountPointFilterSet[p.Mountpoint]; !ok {
				continue
			}
		}

		// If the mount point is a member of the exclude set,
		// don't gather info on it.
		if _, ok := fstypeExcludeSet[p.Fstype]; ok {
			continue
		}

		mountpoint := s.OSGetenv("HOST_MOUNT_PREFIX") + p.Mountpoint
		if _, err := s.OSStat(mountpoint); err != nil {
			continue
		}
		du, err := s.PSDiskUsage(mountpoint)
		if err != nil {
			continue
		}
		du.Path = p.Mountpoint
		du.Fstype = p.Fstype
		usage = append(usage, du)
		partitions = append(partitions, &p)
	}

	return usage, partitions, nil
}

func (s *systemPS) NetProto() ([]net.ProtoCountersStat, error) {
	return net.ProtoCounters(nil)
}

func (s *systemPS) NetIO() ([]net.IOCountersStat, error) {
	return net.IOCounters(true)
}

func (s *systemPS) NetConnections() ([]net.ConnectionStat, error) {
	return net.Connections("all")
}

func (s *systemPS) DiskIO(names []string) (map[string]disk.IOCountersStat, error) {
	m, err := disk.IOCounters(names...)
	if err == internal.NotImplementedError {
		return nil, nil
	}

	return m, err
}

func (s *systemPS) VMStat() (*mem.VirtualMemoryStat, error) {
	return mem.VirtualMemory()
}

func (s *systemPS) SwapStat() (*mem.SwapMemoryStat, error) {
	return mem.SwapMemory()
}

func (s *systemPSDisk) Partitions(all bool) ([]disk.PartitionStat, error) {
	return disk.Partitions(all)
}

func (s *systemPSDisk) OSGetenv(key string) string {
	return os.Getenv(key)
}

func (s *systemPSDisk) OSStat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func (s *systemPSDisk) PSDiskUsage(path string) (*disk.UsageStat, error) {
	return disk.Usage(path)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"io"
	"fmt"
	"encoding/csv"
	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
)

func main() {
	var encoding string
	flag.StringVar(&encoding, "enc", "", "input encoding, e.g. latin9, defaults to UTF-8")

	flag.Parse()

	var f *os.File
	var err error
	if len(flag.Args()) != 0 {
		f, err = os.Open(flag.Arg(0))
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}
	} else {
		f = os.Stdin
	}
	var inputReader io.Reader
	if encoding != "" {
		inputReader, err = charset.NewReader(encoding, f)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(20)
		}
	} else {
		inputReader = f
	}
	r := csv.NewReader(inputReader)
	r.Comma = ';'
	r.TrailingComma = true
	r.TrimLeadingSpace = true

	data, err := r.ReadAll()
	if len(os.Args) == 2 {
		f.Close()
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(30)
	}
	if len(data) == 0 || len(data[0]) == 0 {
		os.Exit(0)
	}

	colLens := make(map[int]int)
	for _, row := range data {
		for i, col := range row {
			cl := len(col)
			l, ex := colLens[i]
			if !ex || cl > l {
				colLens[i] = cl
			}
		}
	}

	for _, row := range data {
		for i, col := range row {
			fmt.Printf(fmt.Sprint("%-", colLens[i] + 1, "s"), col)
			if i != len(colLens) {
				fmt.Print("| ")
			}
		}
		fmt.Print("\n")
	}
}
<commit_msg>add seperator flag<commit_after>package main

import (
	"flag"
	"os"
	"io"
	"fmt"
	"encoding/csv"
	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
)

func main() {
	var encoding, seperator string
	flag.StringVar(&encoding, "e", "", "input encoding, e.g. latin9, defaults to UTF-8")
	flag.StringVar(&seperator, "s", "|", "seperator string")
	// TODO
	//var alignRight bool
	//flag.BoolVar(&alignRight, "r", false, "align values to the right instead to the left")

	flag.Parse()

	var f *os.File
	var err error
	if len(flag.Args()) != 0 {
		f, err = os.Open(flag.Arg(0))
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}
	} else {
		f = os.Stdin
	}
	var inputReader io.Reader
	if encoding != "" {
		inputReader, err = charset.NewReader(encoding, f)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(20)
		}
	} else {
		inputReader = f
	}
	r := csv.NewReader(inputReader)
	r.Comma = ';'
	r.TrailingComma = true
	r.TrimLeadingSpace = true

	data, err := r.ReadAll()
	if len(os.Args) == 2 {
		f.Close()
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(30)
	}
	if len(data) == 0 || len(data[0]) == 0 {
		os.Exit(0)
	}

	colLens := make(map[int]int)
	for _, row := range data {
		for i, col := range row {
			cl := len(col)
			l, ex := colLens[i]
			if !ex || cl > l {
				colLens[i] = cl
			}
		}
	}

	for _, row := range data {
		for i, col := range row {
			fmt.Printf(fmt.Sprint("%-", colLens[i] + 1, "s"), col)
			if i != len(colLens) {
				fmt.Printf("%s ", seperator)
			}
		}
		fmt.Print("\n")
	}
}
<|endoftext|>