<commit_before>package main

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/fatih/color"
	"github.com/yuin/gopher-lua"
)

//go:generate ./build_lib.sh

// FindTaskFiles locates task scripts in the project dir.
func FindTaskFiles(dir string) ([]string, error) {
	var luaFiles []string
	join := filepath.Join
	files, err := filepath.Glob(join(dir, "lark.lua"))
	if err != nil {
		return nil, fmt.Errorf("lark.lua: %v", err)
	}
	luaFiles = append(luaFiles, files...)
	files, err = filepath.Glob(join(dir, "lark_tasks/*.lua"))
	if err != nil {
		return nil, fmt.Errorf("lark_tasks: %v", err)
	}
	luaFiles = append(luaFiles, files...)
	return luaFiles, nil

}

// LuaConfig contains options for a new Lua virtual machine.
type LuaConfig struct {
}

// LoadVM creates a lua.State from conf and returns it.
func LoadVM(conf *LuaConfig) (s *lua.LState, err error) {
	s = lua.NewState()
	defer func() {
		if err != nil {
			log.Print(err)
			s.Close()
		}
	}()

	return s, nil
}

// InitLark initializes the lark library and loads files.
func InitLark(c *Context, files []string) error {
	err := LoadLarkLib(c)
	if err != nil {
		return err
	}

	// This needs to come after LoadLarkLib but can't because the primary
	// library is a file.
	if len(files) > 0 {
		log.Printf("loading files: %v", files)
	}
	for _, file := range files {
		err := c.Lua.DoFile(file)
		if err != nil {
			return err
		}
	}

	return nil
}

// LoadFiles loads the given files into state
func LoadFiles(state *lua.LState, files []string) error {
	for _, file := range files {
		err := state.DoFile(file)
		if err != nil {
			return err
		}
	}
	return nil
}

// LoadLarkLib loads the default lark module.
func LoadLarkLib(c *Context) error {
	err := c.Lua.DoString(LarkLib)
	if err != nil {
		return nil
	}

	lark := c.Lua.GetGlobal("lark")
	c.Lua.SetField(lark, "log", c.Lua.NewFunction(LuaLog))
	c.Lua.SetField(lark, "verbose", lua.LBool(c.Verbose))
	return nil
}

// LuaLog logs a message from lua.
func LuaLog(state *lua.LState) int {
	opt := &logOpt{}
	var msg string
	v1 := state.Get(1)
	if v1.Type() == lua.LTTable {
		arr := luaTableArray(state, v1.(*lua.LTable))
		if len(arr) > 0 {
			msg = fmt.Sprint(arr[0])
		}
	} else if v1.Type() == lua.LTString {
		msg = string(string(v1.(lua.LString)))
	}

	lcolor, ok := state.GetField(v1, "color").(lua.LString)
	if ok {
		opt.Color = string(lcolor)
	}

	logLark(msg, opt)

	return 0
}

func luaTableArray(state *lua.LState, t *lua.LTable) []lua.LValue {
	var vals []lua.LValue
	t.ForEach(func(kv, vv lua.LValue) {
		if kv.Type() == lua.LTNumber {
			vals = append(vals, vv)
		}
	})
	return vals
}

type logOpt struct {
	Color string
}

func logLark(msg string, opt *logOpt) {
	if opt == nil {
		opt = &logOpt{}
	}

	var esc func(format string, v ...interface{}) string
	if opt.Color != "" {
		esc = colorMap[opt.Color]
	}

	if esc != nil {
		msg = esc("%s", msg)
	}
	log.Print(msg)
}

var colorMap = map[string]func(format string, v ...interface{}) string{
	"black":   color.BlackString,
	"blue":    color.BlueString,
	"cyan":    color.CyanString,
	"green":   color.GreenString,
	"magenta": color.MagentaString,
	"red":     color.RedString,
	"white":   color.WhiteString,
	"yellow":  color.YellowString,
}
<commit_msg>only log which files are loaded when the -v flag has been provided<commit_after>package main

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/fatih/color"
	"github.com/yuin/gopher-lua"
)

//go:generate ./build_lib.sh

// FindTaskFiles locates task scripts in the project dir.
func FindTaskFiles(dir string) ([]string, error) {
	var luaFiles []string
	join := filepath.Join
	files, err := filepath.Glob(join(dir, "lark.lua"))
	if err != nil {
		return nil, fmt.Errorf("lark.lua: %v", err)
	}
	luaFiles = append(luaFiles, files...)
	files, err = filepath.Glob(join(dir, "lark_tasks/*.lua"))
	if err != nil {
		return nil, fmt.Errorf("lark_tasks: %v", err)
	}
	luaFiles = append(luaFiles, files...)
	return luaFiles, nil

}

// LuaConfig contains options for a new Lua virtual machine.
type LuaConfig struct {
}

// LoadVM creates a lua.State from conf and returns it.
func LoadVM(conf *LuaConfig) (s *lua.LState, err error) {
	s = lua.NewState()
	defer func() {
		if err != nil {
			log.Print(err)
			s.Close()
		}
	}()

	return s, nil
}

// InitLark initializes the lark library and loads files.
func InitLark(c *Context, files []string) error {
	err := LoadLarkLib(c)
	if err != nil {
		return err
	}

	// This needs to come after LoadLarkLib but can't because the primary
	// library is a file.
	if c.Verbose && len(files) > 0 {
		log.Printf("loading files: %v", files)
	}
	for _, file := range files {
		err := c.Lua.DoFile(file)
		if err != nil {
			return err
		}
	}

	return nil
}

// LoadFiles loads the given files into state
func LoadFiles(state *lua.LState, files []string) error {
	for _, file := range files {
		err := state.DoFile(file)
		if err != nil {
			return err
		}
	}
	return nil
}

// LoadLarkLib loads the default lark module.
func LoadLarkLib(c *Context) error {
	err := c.Lua.DoString(LarkLib)
	if err != nil {
		return nil
	}

	lark := c.Lua.GetGlobal("lark")
	c.Lua.SetField(lark, "log", c.Lua.NewFunction(LuaLog))
	c.Lua.SetField(lark, "verbose", lua.LBool(c.Verbose))
	return nil
}

// LuaLog logs a message from lua.
func LuaLog(state *lua.LState) int {
	opt := &logOpt{}
	var msg string
	v1 := state.Get(1)
	if v1.Type() == lua.LTTable {
		arr := luaTableArray(state, v1.(*lua.LTable))
		if len(arr) > 0 {
			msg = fmt.Sprint(arr[0])
		}
	} else if v1.Type() == lua.LTString {
		msg = string(string(v1.(lua.LString)))
	}

	lcolor, ok := state.GetField(v1, "color").(lua.LString)
	if ok {
		opt.Color = string(lcolor)
	}

	logLark(msg, opt)

	return 0
}

func luaTableArray(state *lua.LState, t *lua.LTable) []lua.LValue {
	var vals []lua.LValue
	t.ForEach(func(kv, vv lua.LValue) {
		if kv.Type() == lua.LTNumber {
			vals = append(vals, vv)
		}
	})
	return vals
}

type logOpt struct {
	Color string
}

func logLark(msg string, opt *logOpt) {
	if opt == nil {
		opt = &logOpt{}
	}

	var esc func(format string, v ...interface{}) string
	if opt.Color != "" {
		esc = colorMap[opt.Color]
	}

	if esc != nil {
		msg = esc("%s", msg)
	}
	log.Print(msg)
}

var colorMap = map[string]func(format string, v ...interface{}) string{
	"black":   color.BlackString,
	"blue":    color.BlueString,
	"cyan":    color.CyanString,
	"green":   color.GreenString,
	"magenta": color.MagentaString,
	"red":     color.RedString,
	"white":   color.WhiteString,
	"yellow":  color.YellowString,
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * 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 cmd

import (
	"fmt"
	"github.com/s3git/s3git-go"
	"github.com/spf13/cobra"
)

var dedupe bool
var presignedUrls bool
var jsonOutput bool

// snapshotCmd represents the snapshot command
var snapshotCmd = &cobra.Command{
	Use:   "snapshot",
	Short: "Manage snapshots",
	Long: "Create, checkout and list snapshots",
}

var snapshotCreateCmd = &cobra.Command{
	Use:   "create [directory]",
	Short: "Create a snapshot",
	Long: "Create a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		} else if message == "" {
			er("Commit message for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		key, nothing, err := repo.SnapshotCreate(args[0], message)
		if err != nil {
			er(err)
		}
		if nothing {
			fmt.Println("No changes to snapshot")
		} else {
			fmt.Printf("[commit %s]\n", key)
		}
	},
}

var snapshotCheckoutCmd = &cobra.Command{
	Use:   "checkout [directory] ([commit])",
	Short: "Checkout a snapshot",
	Long: "Checkout a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		// TODO: Partial checkout would be nice (eg specify path as filter)

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 2 {
			commit = args[1]
		}

		err = repo.SnapshotCheckout(args[0], commit, dedupe)
		if err != nil {
			er(err)
		}
	},
}

var snapshotListCmd = &cobra.Command{
	Use:   "ls ([commit])",
	Short: "List a snapshot",
	Long: "List a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Commit for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 1 {
			commit = args[0]
		}

		// TODO: Dump result in JSON format
		err = repo.SnapshotList(commit, presignedUrls)
		if err != nil {
			er(err)
		}

	},
}

var snapshotStatusCmd = &cobra.Command{
	Use:   "status [directory] ([commit])",
	Short: "Show changes for snapshot",
	Long: "Show changes for snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 2 {
			commit = args[1]
		}

		err = repo.SnapshotStatus(args[0], commit)
		if err != nil {
			er(err)
		}
	},
}

func init() {
	RootCmd.AddCommand(snapshotCmd)
	snapshotCmd.AddCommand(snapshotCreateCmd)
	snapshotCmd.AddCommand(snapshotCheckoutCmd)
	snapshotCmd.AddCommand(snapshotListCmd)
	snapshotCmd.AddCommand(snapshotStatusCmd)

	// Local flags for create
	snapshotCreateCmd.Flags().StringVarP(&message, "message", "m", "", "Message for the commit of create snapshot")

	// Local flags for checkout
	snapshotCheckoutCmd.Flags().BoolVar(&dedupe, "dedupe", false, "Checkout in deduped (pointers) format")

	// Local flags for list
	snapshotListCmd.Flags().BoolVar(&presignedUrls, "presigned", false, "Generate presigned urls for direct access from S3")
	snapshotListCmd.Flags().BoolVar(&presignedUrls, "json", false, "Output result in JSON")
}
<commit_msg>Fix buglet in json flag<commit_after>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * 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 cmd

import (
	"fmt"
	"github.com/s3git/s3git-go"
	"github.com/spf13/cobra"
)

var dedupe bool
var presignedUrls bool
var jsonOutput bool

// snapshotCmd represents the snapshot command
var snapshotCmd = &cobra.Command{
	Use:   "snapshot",
	Short: "Manage snapshots",
	Long: "Create, checkout and list snapshots",
}

var snapshotCreateCmd = &cobra.Command{
	Use:   "create [directory]",
	Short: "Create a snapshot",
	Long: "Create a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		} else if message == "" {
			er("Commit message for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		key, nothing, err := repo.SnapshotCreate(args[0], message)
		if err != nil {
			er(err)
		}
		if nothing {
			fmt.Println("No changes to snapshot")
		} else {
			fmt.Printf("[commit %s]\n", key)
		}
	},
}

var snapshotCheckoutCmd = &cobra.Command{
	Use:   "checkout [directory] ([commit])",
	Short: "Checkout a snapshot",
	Long: "Checkout a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		// TODO: Partial checkout would be nice (eg specify path as filter)

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 2 {
			commit = args[1]
		}

		err = repo.SnapshotCheckout(args[0], commit, dedupe)
		if err != nil {
			er(err)
		}
	},
}

var snapshotListCmd = &cobra.Command{
	Use:   "ls ([commit])",
	Short: "List a snapshot",
	Long: "List a snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Commit for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 1 {
			commit = args[0]
		}

		// TODO: Dump result in JSON format
		err = repo.SnapshotList(commit, presignedUrls)
		if err != nil {
			er(err)
		}

	},
}

var snapshotStatusCmd = &cobra.Command{
	Use:   "status [directory] ([commit])",
	Short: "Show changes for snapshot",
	Long: "Show changes for snapshot",
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) == 0 {
			er("Directory for snapshot must be specified")
		}

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			er(err)
		}

		var commit string
		if len(args) == 2 {
			commit = args[1]
		}

		err = repo.SnapshotStatus(args[0], commit)
		if err != nil {
			er(err)
		}
	},
}

func init() {
	RootCmd.AddCommand(snapshotCmd)
	snapshotCmd.AddCommand(snapshotCreateCmd)
	snapshotCmd.AddCommand(snapshotCheckoutCmd)
	snapshotCmd.AddCommand(snapshotListCmd)
	snapshotCmd.AddCommand(snapshotStatusCmd)

	// Local flags for create
	snapshotCreateCmd.Flags().StringVarP(&message, "message", "m", "", "Message for the commit of create snapshot")

	// Local flags for checkout
	snapshotCheckoutCmd.Flags().BoolVar(&dedupe, "dedupe", false, "Checkout in deduped (pointers) format")

	// Local flags for list
	snapshotListCmd.Flags().BoolVar(&presignedUrls, "presigned", false, "Generate presigned urls for direct access from S3")
	snapshotListCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output result in JSON")
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/pressly/stackup/client"
	"github.com/pressly/stackup/config"
	"github.com/pressly/stackup/terminal"

	"github.com/pressly/prefixer"

	gossh "golang.org/x/crypto/ssh"
	"gopkg.in/yaml.v2"
)

// usage prints help for an arg and exits.
func usage(conf *config.Config, arg int) {
	log.Println("Usage: sup <network> <target/command>\n")
	switch arg {
	case 1:
		// <network> missing, print available hosts.
		log.Println("Available networks (from Supfile):")
		for name, network := range conf.Networks {
			log.Printf("- %v\n", name)
			for _, host := range network.Hosts {
				log.Printf("   - %v\n", host)
			}
		}
	case 2:
		// <target/command> not found or missing,
		// print available targets/commands.
		log.Println("Available targets (from Supfile):")
		for name, commands := range conf.Targets {
			log.Printf("- %v", name)
			for _, cmd := range commands {
				log.Printf("\t%v\n", cmd)
			}
		}
		log.Println()
		log.Println("Available commands (from Supfile):")
		for name, cmd := range conf.Commands {
			log.Printf("- %v\t%v", name, cmd.Desc)
		}
	}
	os.Exit(1)
}

// parseArgs parses os.Args for network and commands to be run.
func parseArgs(conf *config.Config) (config.Network, []config.Command) {
	var commands []config.Command

	// Check for the first argument first
	if len(os.Args) < 2 {
		usage(conf, len(os.Args))
	}
	// Does the <network> exist?
	network, ok := conf.Networks[os.Args[1]]
	if !ok {
		log.Printf("Unknown network \"%v\"\n\n", os.Args[1])
		usage(conf, 1)
	}

	// Does <network> have any hosts?
	if len(network.Hosts) == 0 {
		log.Printf("No hosts specified for network \"%v\"", os.Args[1])
		usage(conf, 1)
	}

	// Check for the second argument
	if len(os.Args) < 3 {
		usage(conf, len(os.Args))
	}
	// Does the <target/command> exist?
	target, isTarget := conf.Targets[os.Args[2]]
	if isTarget {
		// It's the target. Loop over its commands.
		for _, cmd := range target {
			// Does the target's command exist?
			command, isCommand := conf.Commands[cmd]
			if !isCommand {
				log.Printf("Unknown command \"%v\" (from target \"%v\": %v)\n\n", cmd, os.Args[2], target)
				usage(conf, 2)
			}
			command.Name = cmd
			commands = append(commands, command)
		}
	} else {
		// It's probably a command. Does it exist?
		command, isCommand := conf.Commands[os.Args[2]]
		if !isCommand {
			// Not a target, nor command.
			log.Printf("Unknown target/command \"%v\"\n\n", os.Args[2])
			usage(conf, 2)
		}
		command.Name = os.Args[2]
		commands = append(commands, command)
	}

	// Check for extra arguments
	if len(os.Args) != 3 {
		usage(conf, len(os.Args))
	}

	return network, commands
}

func main() {
	var (
		conf       config.Config
		paddingLen int
	)

	// Read configuration file.
	data, _ := ioutil.ReadFile("./Supfile")
	if err := yaml.Unmarshal(data, &conf); err != nil {
		log.Fatal(err)
	}

	// Parse network and commands to be run from os.Args.
	network, commands := parseArgs(&conf)

	// Process all ENVs into a string of form
	// `export FOO="bar"; export BAR="baz";`.
	env := ``
	for name, value := range conf.Env {
		env += `export ` + name + `="` + value + `";`
	}
	for name, value := range network.Env {
		env += `export ` + name + `="` + value + `";`
	}

	// Create clients for every host (either SSH or Localhost).
	var clients []client.Client
	for _, host := range network.Hosts {
		var c client.Client

		if host == "localhost" { // LocalhostClient

			localhostClient := &client.LocalhostClient{
				Env: env,
			}
			if err := localhostClient.Connect(host); err != nil {
				log.Fatal(err)
			}

			c = localhostClient

		} else { // SSHClient

			sshClient := &client.SSHClient{
				Env: env,
			}
			if err := sshClient.Connect(host); err != nil {
				log.Fatal(err)
			}
			defer sshClient.Close()

			c = sshClient
		}

		len := len(c.Prefix())
		if len > paddingLen {
			paddingLen = len
		}

		clients = append(clients, c)
	}

	// Run the command(s) remotely on all hosts in parallel.
	// Run multiple commands (from) sequentally.
	for _, cmd := range commands {
		tasks, err := client.TasksFromConfigCommand(cmd)
		if err != nil {
			log.Fatalf("TasksFromConfigCommand(): ", err)
		}

		for _, task := range tasks {
			// Run the command on all hosts in parallel.
			for i, c := range clients {
				padding := strings.Repeat(" ", paddingLen-(len(c.Prefix())))
				color := terminal.Colors[i%len(terminal.Colors)]

				prefix := color + padding + c.Prefix() + " | "
				err := c.Run(task)
				if err != nil {
					log.Fatalf("%sexit %v", prefix, err)
				}

				go func(c client.Client) {
					switch t := c.(type) {
					case *client.SSHClient:
						if _, err := io.Copy(os.Stdout, prefixer.New(t.RemoteStdout, prefix)); err != nil {
							log.Printf("%sSTDOUT: %v", t.Prefix(), err)
						}
					case *client.LocalhostClient:
						if _, err := io.Copy(os.Stdout, prefixer.New(t.Stdout, prefix)); err != nil {
							log.Printf("%sSTDOUT: %v", t.Prefix(), err)
						}
					}
				}(c)

				go func(c client.Client) {
					switch t := c.(type) {
					case *client.SSHClient:
						if _, err := io.Copy(os.Stderr, prefixer.New(t.RemoteStderr, prefix)); err != nil {
							log.Printf("%sSTDERR: %v", t.Prefix(), err)
						}
					case *client.LocalhostClient:
						if _, err := io.Copy(os.Stderr, prefixer.New(t.Stderr, prefix)); err != nil {
							log.Printf("%sSTDERR: %v", t.Prefix(), err)
						}
					}
				}(c)
			}

			// Wait for all hosts to finish.
			for _, c := range clients {
				if err := c.Wait(); err != nil {
					//TODO: Handle the SSH ExitError in ssh pkg
					e, ok := err.(*gossh.ExitError)
					if ok && e.ExitStatus() != 15 {
						log.Fatalf("%sexit %v", c.Prefix(), e.ExitStatus())
					}
					log.Fatalf("%s: %v", c.Prefix(), err)
				}
			}

		}
	}

	//TODO: We should wait for all io.Copy() goroutines.
	//TODO: We should not exit 0, if there was an error.
}
<commit_msg>Add some more comments to the main logic<commit_after>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/pressly/stackup/client"
	"github.com/pressly/stackup/config"
	"github.com/pressly/stackup/terminal"

	"github.com/pressly/prefixer"

	gossh "golang.org/x/crypto/ssh"
	"gopkg.in/yaml.v2"
)

// usage prints help for an arg and exits.
func usage(conf *config.Config, arg int) {
	log.Println("Usage: sup <network> <target/command>\n")
	switch arg {
	case 1:
		// <network> missing, print available hosts.
		log.Println("Available networks (from Supfile):")
		for name, network := range conf.Networks {
			log.Printf("- %v\n", name)
			for _, host := range network.Hosts {
				log.Printf("   - %v\n", host)
			}
		}
	case 2:
		// <target/command> not found or missing,
		// print available targets/commands.
		log.Println("Available targets (from Supfile):")
		for name, commands := range conf.Targets {
			log.Printf("- %v", name)
			for _, cmd := range commands {
				log.Printf("\t%v\n", cmd)
			}
		}
		log.Println()
		log.Println("Available commands (from Supfile):")
		for name, cmd := range conf.Commands {
			log.Printf("- %v\t%v", name, cmd.Desc)
		}
	}
	os.Exit(1)
}

// parseArgs parses os.Args for network and commands to be run.
func parseArgs(conf *config.Config) (config.Network, []config.Command) {
	var commands []config.Command

	// Check for the first argument first
	if len(os.Args) < 2 {
		usage(conf, len(os.Args))
	}
	// Does the <network> exist?
	network, ok := conf.Networks[os.Args[1]]
	if !ok {
		log.Printf("Unknown network \"%v\"\n\n", os.Args[1])
		usage(conf, 1)
	}

	// Does <network> have any hosts?
	if len(network.Hosts) == 0 {
		log.Printf("No hosts specified for network \"%v\"", os.Args[1])
		usage(conf, 1)
	}

	// Check for the second argument
	if len(os.Args) < 3 {
		usage(conf, len(os.Args))
	}
	// Does the <target/command> exist?
	target, isTarget := conf.Targets[os.Args[2]]
	if isTarget {
		// It's the target. Loop over its commands.
		for _, cmd := range target {
			// Does the target's command exist?
			command, isCommand := conf.Commands[cmd]
			if !isCommand {
				log.Printf("Unknown command \"%v\" (from target \"%v\": %v)\n\n", cmd, os.Args[2], target)
				usage(conf, 2)
			}
			command.Name = cmd
			commands = append(commands, command)
		}
	} else {
		// It's probably a command. Does it exist?
		command, isCommand := conf.Commands[os.Args[2]]
		if !isCommand {
			// Not a target, nor command.
			log.Printf("Unknown target/command \"%v\"\n\n", os.Args[2])
			usage(conf, 2)
		}
		command.Name = os.Args[2]
		commands = append(commands, command)
	}

	// Check for extra arguments
	if len(os.Args) != 3 {
		usage(conf, len(os.Args))
	}

	return network, commands
}

func main() {
	var (
		conf       config.Config
		paddingLen int
	)

	// Read configuration file.
	data, _ := ioutil.ReadFile("./Supfile")
	if err := yaml.Unmarshal(data, &conf); err != nil {
		log.Fatal(err)
	}

	// Parse network and commands to be run from os.Args.
	network, commands := parseArgs(&conf)

	// Process all ENVs into a string of form
	// `export FOO="bar"; export BAR="baz";`.
	env := ``
	for name, value := range conf.Env {
		env += `export ` + name + `="` + value + `";`
	}
	for name, value := range network.Env {
		env += `export ` + name + `="` + value + `";`
	}

	// Create clients for every host (either SSH or Localhost).
	var clients []client.Client
	for _, host := range network.Hosts {
		var c client.Client

		if host == "localhost" { // LocalhostClient

			localhostClient := &client.LocalhostClient{
				Env: env,
			}
			if err := localhostClient.Connect(host); err != nil {
				log.Fatal(err)
			}

			c = localhostClient

		} else { // SSHClient

			sshClient := &client.SSHClient{
				Env: env,
			}
			if err := sshClient.Connect(host); err != nil {
				log.Fatal(err)
			}
			defer sshClient.Close()

			c = sshClient
		}

		len := len(c.Prefix())
		if len > paddingLen {
			paddingLen = len
		}

		clients = append(clients, c)
	}

	// Run command or run multiple commands defined by target sequentally.
	for _, cmd := range commands {
		// Translate command into task(s).
		tasks, err := client.TasksFromConfigCommand(cmd)
		if err != nil {
			log.Fatalf("TasksFromConfigCommand(): ", err)
		}

		// Run tasks sequentally.
		for _, task := range tasks {

			// Run task in parallel.
			for i, c := range clients {
				padding := strings.Repeat(" ", paddingLen-(len(c.Prefix())))
				color := terminal.Colors[i%len(terminal.Colors)]

				prefix := color + padding + c.Prefix() + " | "
				err := c.Run(task)
				if err != nil {
					log.Fatalf("%sexit %v", prefix, err)
				}

				// Copy over tasks's STDOUT.
				go func(c client.Client) {
					switch t := c.(type) {
					case *client.SSHClient:
						if _, err := io.Copy(os.Stdout, prefixer.New(t.RemoteStdout, prefix)); err != nil {
							log.Printf("%sSTDOUT: %v", t.Prefix(), err)
						}
					case *client.LocalhostClient:
						if _, err := io.Copy(os.Stdout, prefixer.New(t.Stdout, prefix)); err != nil {
							log.Printf("%sSTDOUT: %v", t.Prefix(), err)
						}
					}
				}(c)

				// Copy over tasks's STDERR.
				go func(c client.Client) {
					switch t := c.(type) {
					case *client.SSHClient:
						if _, err := io.Copy(os.Stderr, prefixer.New(t.RemoteStderr, prefix)); err != nil {
							log.Printf("%sSTDERR: %v", t.Prefix(), err)
						}
					case *client.LocalhostClient:
						if _, err := io.Copy(os.Stderr, prefixer.New(t.Stderr, prefix)); err != nil {
							log.Printf("%sSTDERR: %v", t.Prefix(), err)
						}
					}
				}(c)
			}

			// Wait for every client to finish the task.
			for _, c := range clients {
				if err := c.Wait(); err != nil {
					//TODO: Handle the SSH ExitError in ssh pkg
					e, ok := err.(*gossh.ExitError)
					if ok && e.ExitStatus() != 15 {
						log.Fatalf("%sexit %v", c.Prefix(), e.ExitStatus())
					}
					log.Fatalf("%s: %v", c.Prefix(), err)
				}
			}
		}
	}

	//TODO: We should wait for all io.Copy() goroutines.
	//TODO: We should not exit 0, if there was an error.
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

// testFlagSpec defines a flag we know about.
type testFlagSpec struct {
	boolVar    bool // True if the flag is type bool
	passToTest bool // pass to Test
	passToAll  bool // pass to test plugin and test binary
	present    bool // The flag has been seen
}

// testFlagDefn is the set of flags we process.
var testFlagDefn = map[string]*testFlagSpec{
	// local to the test plugin
	"q":         {boolVar: true, passToAll: true},
	"v":         {boolVar: true, passToAll: true},
	"cover":     {boolVar: true},
	"coverpkg":  {},
	"covermode": {},

	// Passed to the test binary
	"bench":            {passToTest: true},
	"benchmem":         {boolVar: true, passToTest: true},
	"benchtime":        {passToTest: true},
	"coverprofile":     {passToTest: true},
	"cpu":              {passToTest: true},
	"cpuprofile":       {passToTest: true},
	"memprofile":       {passToTest: true},
	"memprofilerate":   {passToTest: true},
	"blockprofile":     {passToTest: true},
	"blockprofilerate": {passToTest: true},
	"outputdir":        {passToTest: true},
	"parallel":         {passToTest: true},
	"run":              {passToTest: true},
	"short":            {boolVar: true, passToTest: true},
	"timeout":          {passToTest: true},
}

// TestFlags appends "-test." for flags that are passed to the test binary.
func TestFlags(testArgs []string) []string {
	var targs []string
	for _, arg := range testArgs {
		var nArg, nVal, fArg string
		fArg = arg
		if !strings.Contains(arg, "-test.") {
			nArg = strings.TrimPrefix(arg, "-")
			if strings.Contains(nArg, "=") {
				nArgVal := strings.Split(nArg, "=")
				nArg, nVal = nArgVal[0], nArgVal[1]
			}
			if val, ok := testFlagDefn[nArg]; ok {
				// Special handling for -q, needs to be -test.v when passed to the test
				if nArg == "q" {
					nArg = "v"
				}
				if val.passToTest || val.passToAll {
					fArg = "-test." + nArg
				}
			}
			if nVal != "" {
				fArg = fArg + "=" + nVal
			}
		}
		targs = append(targs, fArg)
	}
	return targs
}

// TestFlagsExtraParse is used to separate known arguments from unknown
// arguments passed on the command line. Returns a string slice of test plugin
// arguments (parseArgs), and a slice of string arguments for the test binary
// (extraArgs). An error is returned if an argument is used twice, or an
// argument value is incorrect.
func TestFlagsExtraParse(args []string) (parseArgs []string, extraArgs []string, err error) {
	argsLen := len(args)

	for x := 0; x < argsLen; x++ {
		nArg := args[x]
		val, ok := testFlagDefn[strings.TrimPrefix(nArg, "-")]
		if !strings.HasPrefix(nArg, "-") || (ok && !val.passToTest) {
			err = setArgFound(nArg)
			if err != nil {
				return
			}
			parseArgs = append(parseArgs, nArg)
			if ok && val.passToAll {
				extraArgs = append(extraArgs, nArg)
			}
			continue
		}

		var hadTestPrefix bool
		hasEqual := strings.Contains(nArg, "=")
		if !hasEqual && (x+1 < argsLen && !strings.HasPrefix(args[x+1], "-")) {
			if strings.Contains(nArg, "-test.") {
				hadTestPrefix = true
				nArg = strings.TrimPrefix(nArg, "-test.")
			} else {
				nArg = strings.TrimPrefix(nArg, "-")
			}
			err = setArgFound(nArg)
			if err != nil {
				return
			}
			// Check the spec for arguments that consume the next argument
			if val, ok := testFlagDefn[nArg]; ok {
				if !val.boolVar {
					nArg = nArg + "=" + args[x+1]
					x++
				}
			}
		} else if hasEqual {
			// The argument has an embedded value, here we can do some basic
			// checking.
			sArgs := strings.Split(nArg, "=")
			tArg, tVal := strings.TrimPrefix(sArgs[0], "-"), sArgs[1]
			if val, ok := testFlagDefn[tArg]; ok {
				if val.boolVar {
					if err = checkBoolFlag(tVal); err != nil {
						return
					}
				}
				if !val.passToTest {
					parseArgs = append(parseArgs, nArg)
					continue
				}
			}
		}

		// Append "-" to the argument, and "-test." if "-test." was previously
		// trimmed.
		if nArg[0] != '-' {
			pre := "-"
			if hadTestPrefix {
				pre = "-test."
			}
			nArg = pre + nArg
		}
		extraArgs = append(extraArgs, nArg)
	}

	return
}

// setArgFound checks the argument spec to see if arg has already been
// encountered. If it has, then an error is returned.
func setArgFound(arg string) error {
	var err error
	nArg := strings.TrimPrefix(arg, "-")
	if val, ok := testFlagDefn[nArg]; ok {
		if val.present {
			err = fmt.Errorf("%q flag may be set only once", arg)
		} else {
			testFlagDefn[nArg].present = true
		}
	}
	return err
}

// checkBoolFlag checks the value to ensure it is a boolean, if not an error is
// returned.
func checkBoolFlag(value string) error {
	var nErr error
	_, err := strconv.ParseBool(value)
	if err != nil {
		nErr = errors.New("illegal bool flag value " + value)
	}
	return nErr
}
<commit_msg>Improve gb test verbose output.<commit_after>package cmd

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

// testFlagSpec defines a flag we know about.
type testFlagSpec struct {
	boolVar    bool // True if the flag is type bool
	passToTest bool // pass to Test
	passToAll  bool // pass to test plugin and test binary
	present    bool // The flag has been seen
}

// testFlagDefn is the set of flags we process.
var testFlagDefn = map[string]*testFlagSpec{
	// local to the test plugin
	"q":         {boolVar: true, passToTest: true, passToAll: true},
	"v":         {boolVar: true, passToTest: true, passToAll: true},
	"cover":     {boolVar: true},
	"coverpkg":  {},
	"covermode": {},

	// Passed to the test binary
	"bench":            {passToTest: true},
	"benchmem":         {boolVar: true, passToTest: true},
	"benchtime":        {passToTest: true},
	"coverprofile":     {passToTest: true},
	"cpu":              {passToTest: true},
	"cpuprofile":       {passToTest: true},
	"memprofile":       {passToTest: true},
	"memprofilerate":   {passToTest: true},
	"blockprofile":     {passToTest: true},
	"blockprofilerate": {passToTest: true},
	"outputdir":        {passToTest: true},
	"parallel":         {passToTest: true},
	"run":              {passToTest: true},
	"short":            {boolVar: true, passToTest: true},
	"timeout":          {passToTest: true},
}

// TestFlags appends "-test." for flags that are passed to the test binary.
func TestFlags(testArgs []string) []string {
	var targs []string
	for _, arg := range testArgs {
		var nArg, nVal, fArg string
		fArg = arg
		if !strings.Contains(arg, "-test.") {
			nArg = strings.TrimPrefix(arg, "-")
			if strings.Contains(nArg, "=") {
				nArgVal := strings.Split(nArg, "=")
				nArg, nVal = nArgVal[0], nArgVal[1]
			}
			if val, ok := testFlagDefn[nArg]; ok {
				// Special handling for -q, needs to be -test.v when passed to the test
				if nArg == "q" {
					nArg = "v"
				}
				if val.passToTest || val.passToAll {
					fArg = "-test." + nArg
				}
			}
			if nVal != "" {
				fArg = fArg + "=" + nVal
			}
		}
		targs = append(targs, fArg)
	}
	return targs
}

// TestFlagsExtraParse is used to separate known arguments from unknown
// arguments passed on the command line. Returns a string slice of test plugin
// arguments (parseArgs), and a slice of string arguments for the test binary
// (extraArgs). An error is returned if an argument is used twice, or an
// argument value is incorrect.
func TestFlagsExtraParse(args []string) (parseArgs []string, extraArgs []string, err error) {
	argsLen := len(args)

	for x := 0; x < argsLen; x++ {
		nArg := args[x]
		val, ok := testFlagDefn[strings.TrimPrefix(nArg, "-")]
		if !strings.HasPrefix(nArg, "-") || (ok && !val.passToTest) {
			err = setArgFound(nArg)
			if err != nil {
				return
			}
			parseArgs = append(parseArgs, nArg)
			if ok && val.passToAll {
				extraArgs = append(extraArgs, nArg)
			}
			continue
		}

		var hadTestPrefix bool
		hasEqual := strings.Contains(nArg, "=")
		if !hasEqual && (x+1 < argsLen && !strings.HasPrefix(args[x+1], "-")) {
			if strings.Contains(nArg, "-test.") {
				hadTestPrefix = true
				nArg = strings.TrimPrefix(nArg, "-test.")
			} else {
				nArg = strings.TrimPrefix(nArg, "-")
			}
			err = setArgFound(nArg)
			if err != nil {
				return
			}
			// Check the spec for arguments that consume the next argument
			if val, ok := testFlagDefn[nArg]; ok {
				if !val.boolVar {
					nArg = nArg + "=" + args[x+1]
					x++
				}
			}
		} else if hasEqual {
			// The argument has an embedded value, here we can do some basic
			// checking.
			sArgs := strings.Split(nArg, "=")
			tArg, tVal := strings.TrimPrefix(sArgs[0], "-"), sArgs[1]
			if val, ok := testFlagDefn[tArg]; ok {
				if val.boolVar {
					if err = checkBoolFlag(tVal); err != nil {
						return
					}
				}
				if !val.passToTest {
					parseArgs = append(parseArgs, nArg)
					continue
				}
			}
		}

		// Append "-" to the argument, and "-test." if "-test." was previously
		// trimmed.
		if nArg[0] != '-' {
			pre := "-"
			if hadTestPrefix {
				pre = "-test."
			}
			nArg = pre + nArg
		}
		extraArgs = append(extraArgs, nArg)
	}

	return
}

// setArgFound checks the argument spec to see if arg has already been
// encountered. If it has, then an error is returned.
func setArgFound(arg string) error {
	var err error
	nArg := strings.TrimPrefix(arg, "-")
	if val, ok := testFlagDefn[nArg]; ok {
		if val.present {
			err = fmt.Errorf("%q flag may be set only once", arg)
		} else {
			testFlagDefn[nArg].present = true
		}
	}
	return err
}

// checkBoolFlag checks the value to ensure it is a boolean, if not an error is
// returned.
func checkBoolFlag(value string) error {
	var nErr error
	_, err := strconv.ParseBool(value)
	if err != nil {
		nErr = errors.New("illegal bool flag value " + value)
	}
	return nErr
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.

package cmd

import (
	"archive/zip"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/renstrom/dedent"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/wso2/wum-uc/util"
	"github.com/wso2/wum-uc/constant"
)

var (
	validateCmdUse = "validate <update_loc> <dist_loc>"
	validateCmdShortDesc = "A brief description of your command"
	validateCmdLongDesc = dedent.Dedent(`
		A longer description that spans multiple lines and likely contains
		examples and usage of using your command.`)
)

// validateCmd represents the validate command
var validateCmd = &cobra.Command{
	Use: validateCmdUse,
	Short: validateCmdShortDesc,
	Long: validateCmdLongDesc,
	Run: initializeValidateCommand,
}

func init() {
	RootCmd.AddCommand(validateCmd)
	validateCmd.Flags().BoolVarP(&isDebugLogsEnabled, "debug", "d", false, "Enable debug logs")
	validateCmd.Flags().BoolVarP(&isTraceLogsEnabled, "trace", "t", false, "Enable trace logs")
}

func initializeValidateCommand(cmd *cobra.Command, args []string) {
	if len(args) < 2 || len(args) > 2 {
		util.PrintErrorAndExit("Invalid number of argumants. Run 'wum-uc validate --help' to view help.")
	}
	startValidation(args[0], args[1])
}

//Entry point of  the validate command
func startValidation(updateFilePath, distributionLocation string) {

	updateFileMap := make(map[string]bool)
	distributionFileMap := make(map[string]bool)

	//Check update location
	//Check 1
	if !strings.HasSuffix(updateFilePath, ".zip") {
		util.PrintErrorAndExit("Entered update location does not have a 'zip' extention.")
	}
	//Check 2
	exists, err := util.IsFileExists(updateFilePath)
	util.HandleError(err, "")
	if !exists {
		util.PrintErrorAndExit("Update file '" + updateFilePath + "' does not exist.")
	}

	exists, err = util.IsDistributionExists(distributionLocation)
	util.HandleError(err, "Error occurred while checking '" + distributionLocation + "'")
	if !exists {
		util.PrintErrorAndExit("Distribution does not exist at ", distributionLocation)
	}

	//Check 3
	locationInfo, err := os.Stat(updateFilePath)
	util.HandleError(err, "")
	match, err := regexp.MatchString(constant.FILENAME_REGEX, locationInfo.Name())
	if !match {
		util.PrintErrorAndExit("Update file name does not match '" + constant.FILENAME_REGEX + "' regular expression.")
	}
	updateName := strings.TrimSuffix(locationInfo.Name(), ".zip")
	viper.Set(constant.UPDATE_NAME, updateName)

	updateFileMap, err = readUpdateZip(updateFilePath)
	util.HandleError(err)
	logger.Debug(updateFileMap)

	//Check dist location
	if strings.HasSuffix(distributionLocation, ".zip") {
		locationInfo, err := os.Stat(distributionLocation)
		util.HandleError(err, "")
		viper.Set(constant.PRODUCT_NAME, strings.TrimSuffix(locationInfo.Name(), ".zip"))

		distributionFileMap, err = readDistributionZip(distributionLocation)
	} else {

	}

	err = compare(updateFileMap, distributionFileMap)
	util.HandleError(err)

	util.PrintInfo("'" + updateName + "' validation successfully finished. No issues found.")
}

func compare(updateFileMap, distributionFileMap map[string]bool) error {
	for filePath := range updateFileMap {
		logger.Debug("Searching:", filePath)
		_, found := distributionFileMap[filePath]
		if !found {
			return &util.CustomError{What: "File not found in the distribution: '" + filePath + "'" }
		}
	}
	return nil
}

func readUpdateZip(filename string) (map[string]bool, error) {
	fileMap := make(map[string]bool)
	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(filename)
	if err != nil {
	}
	defer zipReader.Close()

	updateName := viper.GetString(constant.UPDATE_NAME)
	logger.Debug("updateName:", updateName)
	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		if file.FileInfo().IsDir() {
			logger.Debug("dir:", file.Name)
			logger.Debug("dir:", file.FileInfo().Name())
			if file.FileInfo().Name() != updateName {
				logger.Debug("Checking:", file.FileInfo().Name())
				//Check 4
				prefix := filepath.Join(updateName, constant.CARBON_HOME)
				hasPrefix := strings.HasPrefix(file.Name, prefix)
				if !hasPrefix {
					return nil, &util.CustomError{What: "Unknown directory found: '" + file.Name + "'" }
				}
			}
		} else {
			logger.Debug("file:", file.Name)
			logger.Debug("file:", file.FileInfo().Name())
			fullPath := filepath.Join(updateName, file.FileInfo().Name())
			logger.Debug("fullPath:", fullPath)
			switch file.FileInfo().Name() {
			case constant.UPDATE_DESCRIPTOR_FILE:
				//todo: read update descriptor and validate
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.UPDATE_DESCRIPTOR_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			case constant.LICENSE_FILE:
				//todo: read license and validate
				//This security update is licensed by WSO2 Inc. under Apache License 2.0.
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.LICENSE_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			case constant.INSTRUCTIONS_FILE:
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.INSTRUCTIONS_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			case constant.NOT_A_CONTRIBUTION_FILE:
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.NOT_A_CONTRIBUTION_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			default:
				prefix := filepath.Join(updateName, constant.CARBON_HOME)
				hasPrefix := strings.HasPrefix(file.Name, prefix)
				if !hasPrefix {
					return nil, &util.CustomError{What: "Unknown file found: '" + file.Name + "'" }
				}
				relativePath := strings.TrimPrefix(file.Name, prefix)
				fileMap[relativePath] = false
			}
		}
	}
	return fileMap, nil
}

func readDistributionZip(filename string) (map[string]bool, error) {
	fileMap := make(map[string]bool)
	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		return nil, err
	}
	defer zipReader.Close()

	productName := viper.GetString(constant.PRODUCT_NAME)
	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		relativePath := strings.TrimPrefix(file.Name, productName)
		if !file.FileInfo().IsDir() {
			fileMap[relativePath] = false
		}
	}
	return fileMap, nil
}<commit_msg>Add 'update-description' validation<commit_after>// Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.

package cmd

import (
	"archive/zip"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/renstrom/dedent"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/wso2/wum-uc/util"
	"github.com/wso2/wum-uc/constant"
	"gopkg.in/yaml.v2"
)

var (
	validateCmdUse = "validate <update_loc> <dist_loc>"
	validateCmdShortDesc = "A brief description of your command"
	validateCmdLongDesc = dedent.Dedent(`
		A longer description that spans multiple lines and likely contains
		examples and usage of using your command.`)
)

// validateCmd represents the validate command
var validateCmd = &cobra.Command{
	Use: validateCmdUse,
	Short: validateCmdShortDesc,
	Long: validateCmdLongDesc,
	Run: initializeValidateCommand,
}

func init() {
	RootCmd.AddCommand(validateCmd)
	validateCmd.Flags().BoolVarP(&isDebugLogsEnabled, "debug", "d", false, "Enable debug logs")
	validateCmd.Flags().BoolVarP(&isTraceLogsEnabled, "trace", "t", false, "Enable trace logs")
}

func initializeValidateCommand(cmd *cobra.Command, args []string) {
	if len(args) < 2 || len(args) > 2 {
		util.PrintErrorAndExit("Invalid number of argumants. Run 'wum-uc validate --help' to view help.")
	}
	startValidation(args[0], args[1])
}

//Entry point of  the validate command
func startValidation(updateFilePath, distributionLocation string) {

	setLogLevel()
	logger.Debug("validate command called")

	updateFileMap := make(map[string]bool)
	distributionFileMap := make(map[string]bool)

	//Check update location
	//Check 1
	if !strings.HasSuffix(updateFilePath, ".zip") {
		util.PrintErrorAndExit("Entered update location does not have a 'zip' extention.")
	}
	//Check 2
	exists, err := util.IsFileExists(updateFilePath)
	util.HandleError(err, "")
	if !exists {
		util.PrintErrorAndExit("Update file '" + updateFilePath + "' does not exist.")
	}

	exists, err = util.IsDistributionExists(distributionLocation)
	util.HandleError(err, "Error occurred while checking '" + distributionLocation + "'")
	if !exists {
		util.PrintErrorAndExit("Distribution does not exist at ", distributionLocation)
	}

	//Check 3
	locationInfo, err := os.Stat(updateFilePath)
	util.HandleError(err, "")
	match, err := regexp.MatchString(constant.FILENAME_REGEX, locationInfo.Name())
	if !match {
		util.PrintErrorAndExit("Update file name does not match '" + constant.FILENAME_REGEX + "' regular expression.")
	}
	updateName := strings.TrimSuffix(locationInfo.Name(), ".zip")
	viper.Set(constant.UPDATE_NAME, updateName)

	updateFileMap, err = readUpdateZip(updateFilePath)
	util.HandleError(err)
	logger.Debug(updateFileMap)

	//Check dist location
	if strings.HasSuffix(distributionLocation, ".zip") {
		locationInfo, err := os.Stat(distributionLocation)
		util.HandleError(err, "")
		viper.Set(constant.PRODUCT_NAME, strings.TrimSuffix(locationInfo.Name(), ".zip"))

		distributionFileMap, err = readDistributionZip(distributionLocation)
	} else {

	}

	err = compare(updateFileMap, distributionFileMap)
	util.HandleError(err)

	util.PrintInfo("'" + updateName + "' validation successfully finished. No issues found.")
}

func compare(updateFileMap, distributionFileMap map[string]bool) error {
	for filePath := range updateFileMap {
		logger.Debug("Searching:", filePath)
		_, found := distributionFileMap[filePath]
		if !found {
			return &util.CustomError{What: "File not found in the distribution: '" + filePath + "'" }
		}
	}
	return nil
}

func readUpdateZip(filename string) (map[string]bool, error) {
	fileMap := make(map[string]bool)
	updateDescriptor := util.UpdateDescriptor{}

	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(filename)
	if err != nil {
	}
	defer zipReader.Close()

	updateName := viper.GetString(constant.UPDATE_NAME)
	logger.Debug("updateName:", updateName)
	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		if file.FileInfo().IsDir() {
			logger.Debug("dir:", file.Name)
			logger.Debug("dir:", file.FileInfo().Name())
			if file.FileInfo().Name() != updateName {
				logger.Debug("Checking:", file.FileInfo().Name())
				//Check 4
				prefix := filepath.Join(updateName, constant.CARBON_HOME)
				hasPrefix := strings.HasPrefix(file.Name, prefix)
				if !hasPrefix {
					return nil, &util.CustomError{What: "Unknown directory found: '" + file.Name + "'" }
				}
			}
		} else {
			logger.Debug("file:", file.Name)
			logger.Debug("file:", file.FileInfo().Name())
			fullPath := filepath.Join(updateName, file.FileInfo().Name())
			logger.Debug("fullPath:", fullPath)
			switch file.FileInfo().Name() {
			case constant.UPDATE_DESCRIPTOR_FILE:
				//todo: read update descriptor and validate
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.UPDATE_DESCRIPTOR_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
				zippedFile, err := file.Open()
				if err != nil {
					return nil, err
				}
				data, err := ioutil.ReadAll(zippedFile)
				if err != nil {
					return nil, err
				}
				//fmt.Print(string(data))

				zippedFile.Close()

				err = yaml.Unmarshal(data, &updateDescriptor)
				if err != nil {
					return nil, err
				}
				err = util.ValidateUpdateDescriptor(&updateDescriptor)
				if err != nil {
					return nil, &util.CustomError{What: "'" + constant.UPDATE_DESCRIPTOR_FILE + "' is invalid. " + err.Error() }
				}
			case constant.LICENSE_FILE:
				//todo: read license and validate
				//This security update is licensed by WSO2 Inc. under Apache License 2.0.
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.LICENSE_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			case constant.INSTRUCTIONS_FILE:
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.INSTRUCTIONS_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			case constant.NOT_A_CONTRIBUTION_FILE:
				if file.Name != fullPath {
					parent := strings.TrimSuffix(file.Name, file.FileInfo().Name())
					return nil, &util.CustomError{What: "'" + constant.NOT_A_CONTRIBUTION_FILE + "' found at '" + parent + "'. It should be in the '" + updateName + "' directory" }
				}
			default:
				prefix := filepath.Join(updateName, constant.CARBON_HOME)
				hasPrefix := strings.HasPrefix(file.Name, prefix)
				if !hasPrefix {
					return nil, &util.CustomError{What: "Unknown file found: '" + file.Name + "'" }
				}
				relativePath := strings.TrimPrefix(file.Name, prefix)
				fileMap[relativePath] = false
			}
		}
	}
	return fileMap, nil
}

func readDistributionZip(filename string) (map[string]bool, error) {
	fileMap := make(map[string]bool)
	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(filename)
	if err != nil {
		return nil, err
	}
	defer zipReader.Close()

	productName := viper.GetString(constant.PRODUCT_NAME)
	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		relativePath := strings.TrimPrefix(file.Name, productName)
		if !file.FileInfo().IsDir() {
			fileMap[relativePath] = false
		}
	}
	return fileMap, nil
}<|endoftext|>


<commit_before>// Package jsonseq provides methods for reading and writing JSON text sequences
// (`application/json-seq`) as defined in RFC 7464 (https://tools.ietf.org/html/rfc7464).
package jsonseq

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
)

// ContentType is the MIME media type for JSON text sequences.
// See: https://tools.ietf.org/html/rfc7464#section-4
const ContentType = `application/json-seq`

const (
	rs = 0x1E
	lf = 0x0A
	sp = 0x20
	tb = 0x09
	cr = 0x0D
)

// whitespace characters defined in https://tools.ietf.org/html/rfc7159#section-2.
var wsSet = string([]byte{sp, tb, lf, cr})

const digitSet = "1234567980"

func ws(b byte) bool {
	return b == sp || b == tb || b == lf || b == cr
}

// WriteRecord writes a JSON text sequence record with beginning
// (RS) and end (LF) marker bytes.
func WriteRecord(w io.Writer, json []byte) error {
	_, err := w.Write([]byte{rs})
	if err != nil {
		return err
	}
	_, err = w.Write(json)
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{lf})
	return err
}

// A RecordWriter delimits the start of written records with a record separator.
//
// The standard library's json.Encoder calls Write just once for each value and
// always with a trailing line feed, so it can be adapted very simply to emit a
// JSON
//
// 	encoder := json.NewEncoder(&jsonseq.RecordWriter{writer})
type RecordWriter struct {
	io.Writer
}

// Write prefixes every written record with an ASCII record separator. The caller
// is responsible for including a trailing line feed when necessary. Calls the
// underlying Writer exactly once.
func (w *RecordWriter) Write(record []byte) (int, error) {
	_, err := w.Writer.Write([]byte{rs})
	if err != nil {
		return -1, err
	}
	n, err := w.Writer.Write(record)
	return n + 1, err
}

// A Decoder reads and decodes JSON text sequence records from an input stream.
type Decoder struct {
	s *bufio.Scanner
}

// NewDecoder creates a Decoder.
func NewDecoder(r io.Reader) *Decoder {
	s := bufio.NewScanner(r)
	s.Split(ScanRecord)
	return &Decoder{s: s}
}

// Decode scans the next record, or returns an error. Extra trailing data is discarded.
// The Decoder remains valid until io.EOF is returned.
func (d *Decoder) Decode(v interface{}) error {
	if !d.s.Scan() {
		if err := d.s.Err(); err != nil {
			return err
		}
		return io.EOF
	}
	b := d.s.Bytes()

	b, ok := RecordValue(b)
	if !ok {
		return fmt.Errorf("invalid record: %q", string(b))
	}
	// Decode the first value, and discard any remaining data.
	return json.NewDecoder(bytes.NewReader(b)).Decode(v)
}

// RecordValue returns a slice containing the value from a JSON sequence record
// and true if it can be decoded or false if the record was truncated or is
// otherwise invalid. This is *NOT* a validation of any contained JSON, and some
// records contain data after the first value, which is always invalid since it
// was not preceded by a RS.
//
// See section 2.4: Top-Level Values: numbers, true, false, and null.
// https://tools.ietf.org/html/rfc7464#section-2.4
func RecordValue(b []byte) ([]byte, bool) {
	if len(b) < 2 {
		return b, false
	}
	if b[0] != rs {
		return b, false
	}
	// Drop rs and leading whitespace.
	b = bytes.TrimLeft(b[1:], wsSet)

	// A number, true, false, or null value could be truncated if not
	// followed by whitespace.
	switch b[0] {
	case 'n':
		if bytes.HasPrefix(b, []byte("null")) {
			if ws(b[4]) {
				return b, true
			}
			return b, false
		}
	case 't':
		if bytes.HasPrefix(b, []byte("true")) {
			if ws(b[4]) {
				return b, true
			}
			return b, false
		}
	case 'f':
		if bytes.HasPrefix(b, []byte("false")) {
			if ws(b[5]) {
				return b, true
			}
			return b, false
		}
	case '-':
		if '0' <= b[1] && b[1] <= '9' {
			t := bytes.TrimLeft(b, digitSet)
			if len(t) > 0 && ws(t[0]) {
				return b, true
			}
			return b, false
		}
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		t := bytes.TrimLeft(b, digitSet)
		if len(t) > 0 && ws(t[0]) {
			return b, true
		}
		return b, false
	}

	return b, true
}

// ScanRecord is a bufio.SplitFunc which splits JSON text sequence records.
// Scanned bytes must be validated with the RecordValue function.
func ScanRecord(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	// Find record start.
	switch i := bytes.IndexByte(data, rs); {
	case i < 0:
		if atEOF {
			// Partial record.
			return len(data), data, nil
		}
		// Request more data.
		return 0, nil, nil
	case i > 0:
		// Partial record.
		return i, data[0 : i-1], nil
	}
	// else i == 0

	// Drop consecutive leading rs's
	for len(data) > 1 && data[1] == rs {
		data = data[1:]
	}

	// Find end or next record.
	i := bytes.IndexByte(data[1:], rs)
	if i < 0 {
		if atEOF {
			return len(data), data, nil
		}
		// Request more data.
		return 0, nil, nil
	}
	return 1 + i, data[:1+i], nil
}
<commit_msg>doc tweaks<commit_after>// Package jsonseq provides methods for reading and writing JSON text sequences
// (`application/json-seq`) as defined in RFC 7464 (https://tools.ietf.org/html/rfc7464).
package jsonseq

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
)

// ContentType is the MIME media type for JSON text sequences.
// See: https://tools.ietf.org/html/rfc7464#section-4
const ContentType = `application/json-seq`

const (
	rs = 0x1E
	lf = 0x0A
	sp = 0x20
	tb = 0x09
	cr = 0x0D
)

// whitespace characters defined in https://tools.ietf.org/html/rfc7159#section-2.
var wsSet = string([]byte{sp, tb, lf, cr})

const digitSet = "1234567980"

func ws(b byte) bool {
	return b == sp || b == tb || b == lf || b == cr
}

// WriteRecord writes a JSON text sequence record with beginning
// (RS) and end (LF) marker bytes.
func WriteRecord(w io.Writer, json []byte) error {
	_, err := w.Write([]byte{rs})
	if err != nil {
		return err
	}
	_, err = w.Write(json)
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{lf})
	return err
}

// A RecordWriter delimits the start of written records with a record separator.
//
// The standard library's json.Encoder calls Write just once for each value and
// always with a trailing line feed, so it can be adapted very simply to emit a
// JSON text sequence.
//
// 	encoder := json.NewEncoder(&jsonseq.RecordWriter{writer})
type RecordWriter struct {
	io.Writer
}

// Write prefixes every written record with an ASCII record separator. The caller
// is responsible for including a trailing line feed when necessary. Calls the
// underlying Writer exactly once.
func (w *RecordWriter) Write(record []byte) (int, error) {
	_, err := w.Writer.Write([]byte{rs})
	if err != nil {
		return -1, err
	}
	n, err := w.Writer.Write(record)
	return n + 1, err
}

// A Decoder reads and decodes JSON text sequence records from an input stream.
type Decoder struct {
	s *bufio.Scanner
}

// NewDecoder creates a Decoder.
func NewDecoder(r io.Reader) *Decoder {
	s := bufio.NewScanner(r)
	s.Split(ScanRecord)
	return &Decoder{s: s}
}

// Decode scans the next record, or returns an error. Extra trailing data is discarded.
// The Decoder remains valid until io.EOF is returned.
func (d *Decoder) Decode(v interface{}) error {
	if !d.s.Scan() {
		if err := d.s.Err(); err != nil {
			return err
		}
		return io.EOF
	}
	b := d.s.Bytes()

	b, ok := RecordValue(b)
	if !ok {
		return fmt.Errorf("invalid record: %q", string(b))
	}
	// Decode the first value, and discard any remaining data.
	return json.NewDecoder(bytes.NewReader(b)).Decode(v)
}

// RecordValue returns a slice containing the value from a JSON text sequence
// record and true if it can be decoded or false if the record was truncated or is
// otherwise invalid. This is *NOT* a validation of any contained JSON, and some
// records contain data after the first value, which is always invalid since it
// was not preceded by a RS.
//
// See section 2.4: Top-Level Values: numbers, true, false, and null.
// https://tools.ietf.org/html/rfc7464#section-2.4
func RecordValue(b []byte) ([]byte, bool) {
	if len(b) < 2 {
		return b, false
	}
	if b[0] != rs {
		return b, false
	}
	// Drop rs and leading whitespace.
	b = bytes.TrimLeft(b[1:], wsSet)

	// A number, true, false, or null value could be truncated if not
	// followed by whitespace.
	switch b[0] {
	case 'n':
		if bytes.HasPrefix(b, []byte("null")) {
			if ws(b[4]) {
				return b, true
			}
			return b, false
		}
	case 't':
		if bytes.HasPrefix(b, []byte("true")) {
			if ws(b[4]) {
				return b, true
			}
			return b, false
		}
	case 'f':
		if bytes.HasPrefix(b, []byte("false")) {
			if ws(b[5]) {
				return b, true
			}
			return b, false
		}
	case '-':
		if '0' <= b[1] && b[1] <= '9' {
			t := bytes.TrimLeft(b, digitSet)
			if len(t) > 0 && ws(t[0]) {
				return b, true
			}
			return b, false
		}
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		t := bytes.TrimLeft(b, digitSet)
		if len(t) > 0 && ws(t[0]) {
			return b, true
		}
		return b, false
	}

	return b, true
}

// ScanRecord is a bufio.SplitFunc which splits JSON text sequence records.
// Scanned bytes must be validated with the RecordValue function.
func ScanRecord(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	// Find record start.
	switch i := bytes.IndexByte(data, rs); {
	case i < 0:
		if atEOF {
			// Partial record.
			return len(data), data, nil
		}
		// Request more data.
		return 0, nil, nil
	case i > 0:
		// Partial record.
		return i, data[0 : i-1], nil
	}
	// else i == 0

	// Drop consecutive leading rs's
	for len(data) > 1 && data[1] == rs {
		data = data[1:]
	}

	// Find end or next record.
	i := bytes.IndexByte(data[1:], rs)
	if i < 0 {
		if atEOF {
			return len(data), data, nil
		}
		// Request more data.
		return 0, nil, nil
	}
	return 1 + i, data[:1+i], nil
}
<|endoftext|>


<commit_before>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

/*type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}*/

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

/*
//Functions for isAlive checking realization
func periodicTask(d time.Duration, f func(time.Time)) {
	for t := range time.Tick(d) {
		f(t)
	}
}

func checkAliveNodes(t time.Tick) {
	resp, err := http.Get("http://goappnode1.appspot.com/isalive")
	if err != nil {
		panic(err)
	}

}
*/

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

/*
func checkAliveStart(w http.ResponseWriter, r *http.Request) {

}

func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>added simple alive checker<commit_after>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"time"

	"appengine"
	"appengine/urlfetch"
)

//predefined parameters
var maxNodes int = 10
var isAliveCheckPeriod int = 500 //in millisecs

//changeable parameters
var statusContent string = "Default status"
var statusLog string = ""

//nodesStates := make(map[int]map[string]string)
/*
example for this map
var nodesStates map[int]map[string]string{
	1: map[string]string{
		"alive":"1",
		"hasTask":"true",
		"taskStatus":"completed",
		"taskResult":"some_result_for_node"
	},
}
*/

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	NodeCount        int    `json:"nodeCount"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"` //running-copleted-loaded
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type echoMessage struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

//types for periodical functions
type pFunc func()

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		//fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		/*inputMsg := echoMessage{}
		err2 := json.NewDecoder(r.Body).Decode(&inputMsg)
		if err2 != nil {
			panic(err2)
		}*/

		thisNodeStats := nodeStats{
			1,
			2,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr //+ "Input message object content: " + inputMsg.Title + inputMsg.Content
	}
}

//Functions for isAlive checking realization
func checkIsAlive(nodeId int) {
	nodeUrl := "http://goappnode" + nodeId + "0.appspot.com/"
	resp, err := http.Get(nodeUrl)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	if resp.Status == 200 {
		statusLog += "Node #" + nodeId + " - online"
	} else {
		statusLog += "Node #" + nodeId + " - offline"
	}
}

func periodicTask(period time.Duration, task pFunc) {
	for {
		task()
		time.Sleep(period * time.Millisecond)
	}
}

/*
func checkAliveNodes(t time.Tick) {
	resp, err := http.Get("http://goappnode1.appspot.com/isalive")
	if err != nil {
		panic(err)
	}

}
*/

func isAliveServer(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, 1)
}

func checkAliveStart(w http.ResponseWriter, r *http.Request) {
	go periodicTask(30000, checkAlive(1))
}

/*
func checkAliveStop(w http.ResponseWriter, r *http.Request) {

}
*/

func testEcho(w http.ResponseWriter, r *http.Request) {
	msg := echoMessage{
		"Message is",
		"",
	}

	r.ParseForm()
	c := appengine.NewContext(r)
	msg.Content = r.FormValue("echoContent")

	jsonMessage, err2 := json.Marshal(msg)
	if err2 != nil {
		panic(err2)
	}

	//jsonStr := []byte(`{"message":"` + r.FormValue("echoContent") + `"}`)
	jsonStr := []byte(jsonMessage)
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	//view pages
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	//service pages
	http.HandleFunc("/echo", testEcho)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/isalive", isAliveServer)
	http.HandleFunc("/startcheck", checkAliveStart)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"

	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/lunixbochs/vtclean"
	"github.com/heroku/heroku-cli/Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal"
)

// Stdout is used to mock stdout for testing
var Stdout io.Writer

// Stderr is to mock stderr for testing
var Stderr io.Writer

// ErrLogPath is the location of the error log
var ErrLogPath = filepath.Join(AppDir(), "error.log")
var errLogger = newLogger(ErrLogPath)
var exitFn = os.Exit
var debugging = isDebugging()
var debuggingHeaders = isDebuggingHeaders()

func init() {
	Stdout = os.Stdout
	Stderr = os.Stderr
}

func newLogger(path string) *log.Logger {
	err := os.MkdirAll(filepath.Dir(path), 0777)
	if err != nil {
		panic(err)
	}
	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	return log.New(file, "", log.LstdFlags)
}

// Exit just calls os.Exit, but can be mocked out for testing
func Exit(code int) {
	exitFn(code)
}

// Err just calls `fmt.Fprint(Stderr, a...)` but can be mocked out for testing.
func Err(a ...interface{}) {
	fmt.Fprint(Stderr, a...)
	Log(a...)
}

// Errf just calls `fmt.Fprintf(Stderr, a...)` but can be mocked out for testing.
func Errf(format string, a ...interface{}) {
	fmt.Fprintf(Stderr, format, a...)
	Logf(format, a...)
}

// Errln just calls `fmt.Fprintln(Stderr, a...)` but can be mocked out for testing.
func Errln(a ...interface{}) {
	fmt.Fprintln(Stderr, a...)
	Logln(a...)
}

// Print is used to replace `fmt.Print()` but can be mocked out for testing.
func Print(a ...interface{}) {
	fmt.Fprint(Stdout, a...)
}

// Printf is used to replace `fmt.Printf()` but can be mocked out for testing.
func Printf(format string, a ...interface{}) {
	fmt.Fprintf(Stdout, format, a...)
}

// Println is used to replace `fmt.Println()` but can be mocked out for testing.
func Println(a ...interface{}) {
	fmt.Fprintln(Stdout, a...)
}

// Log is used to print debugging information
// It will be added to the logfile in ~/.heroku or printed out if HEROKU_DEBUG is set.
func Log(a ...interface{}) {
	errLogger.Print(vtclean.Clean(fmt.Sprint(a...), false))
}

// Logln is used to print debugging information
// It will be added to the logfile in ~/.heroku
func Logln(a ...interface{}) {
	Log(fmt.Sprintln(a...))
}

// Logf is used to print debugging information
// It will be added to the logfile in ~/.heroku
func Logf(format string, a ...interface{}) {
	Log(fmt.Sprintf(format, a...))
}

// Debugln is used to print debugging information
// It will be added to the logfile in ~/.heroku and stderr if HEROKU_DEBUG is set.
func Debugln(a ...interface{}) {
	Logln(a...)
	if debugging {
		fmt.Fprintln(Stderr, a...)
	}
}

// PrintError is a helper that prints out formatted error messages in red text
func PrintError(e error) {
	if e == nil {
		return
	}
	Error("\n" + e.Error())
	if debugging {
		debug.PrintStack()
	}
}

// Warn shows a message with excalamation points prepended to stderr
func Warn(msg string) {
	bang := yellow(" ▸    ")
	msg = strings.TrimSpace(msg)
	msg = strings.Join(strings.Split(msg, "\n"), "\n"+bang)
	Errln(bang + msg)
}

// Error shows a message with excalamation points prepended to stderr
func Error(msg string) {
	bang := red(" " + errorArrow() + "    ")
	msg = strings.TrimSpace(msg)
	msg = strings.Join(strings.Split(msg, "\n"), "\n"+bang)
	Errln(bang + msg)
}

func errorArrow() string {
	if windows() {
		return "!"
	}
	return "▸"
}

// ExitIfError calls PrintError and exits if e is not null
func ExitIfError(e error) {
	if e != nil {
		PrintError(e)
		os.Exit(1)
	}
}

// LogIfError logs out an error if one arises
func LogIfError(e error) {
	if e != nil {
		Logln(e.Error())
	}
}

func isDebugging() bool {
	debug := strings.ToUpper(os.Getenv("HEROKU_DEBUG"))
	if debug == "TRUE" || debug == "1" {
		return true
	}
	return false
}

func isDebuggingHeaders() bool {
	debug := strings.ToUpper(os.Getenv("HEROKU_DEBUG_HEADERS"))
	if debug == "TRUE" || debug == "1" {
		return true
	}
	return false
}

func yellow(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[33m" + s + "\x1b[39m"
	}
	return s
}

func red(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[31m" + s + "\x1b[39m"
	}
	return s
}

func cyan(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[36m" + s + "\x1b[39m"
	}
	return s
}

func windows() bool {
	return runtime.GOOS == "windows"
}

func istty() bool {
	return terminal.IsTerminal(int(os.Stdout.Fd()))
}

func supportsColor() bool {
	if !istty() {
		return false
	}
	for _, arg := range os.Args {
		if arg == "--no-color" {
			return false
		}
	}
	return os.Getenv("COLOR") != "false"
}

func plural(word string, count int) string {
	if count == 1 {
		return word
	}
	return word + "s"
}
<commit_msg>fix newline before errors<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"

	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/lunixbochs/vtclean"
	"github.com/heroku/heroku-cli/Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal"
)

// Stdout is used to mock stdout for testing
var Stdout io.Writer

// Stderr is to mock stderr for testing
var Stderr io.Writer

// ErrLogPath is the location of the error log
var ErrLogPath = filepath.Join(AppDir(), "error.log")
var errLogger = newLogger(ErrLogPath)
var exitFn = os.Exit
var debugging = isDebugging()
var debuggingHeaders = isDebuggingHeaders()

func init() {
	Stdout = os.Stdout
	Stderr = os.Stderr
}

func newLogger(path string) *log.Logger {
	err := os.MkdirAll(filepath.Dir(path), 0777)
	if err != nil {
		panic(err)
	}
	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	return log.New(file, "", log.LstdFlags)
}

// Exit just calls os.Exit, but can be mocked out for testing
func Exit(code int) {
	exitFn(code)
}

// Err just calls `fmt.Fprint(Stderr, a...)` but can be mocked out for testing.
func Err(a ...interface{}) {
	fmt.Fprint(Stderr, a...)
	Log(a...)
}

// Errf just calls `fmt.Fprintf(Stderr, a...)` but can be mocked out for testing.
func Errf(format string, a ...interface{}) {
	fmt.Fprintf(Stderr, format, a...)
	Logf(format, a...)
}

// Errln just calls `fmt.Fprintln(Stderr, a...)` but can be mocked out for testing.
func Errln(a ...interface{}) {
	fmt.Fprintln(Stderr, a...)
	Logln(a...)
}

// Print is used to replace `fmt.Print()` but can be mocked out for testing.
func Print(a ...interface{}) {
	fmt.Fprint(Stdout, a...)
}

// Printf is used to replace `fmt.Printf()` but can be mocked out for testing.
func Printf(format string, a ...interface{}) {
	fmt.Fprintf(Stdout, format, a...)
}

// Println is used to replace `fmt.Println()` but can be mocked out for testing.
func Println(a ...interface{}) {
	fmt.Fprintln(Stdout, a...)
}

// Log is used to print debugging information
// It will be added to the logfile in ~/.heroku or printed out if HEROKU_DEBUG is set.
func Log(a ...interface{}) {
	errLogger.Print(vtclean.Clean(fmt.Sprint(a...), false))
}

// Logln is used to print debugging information
// It will be added to the logfile in ~/.heroku
func Logln(a ...interface{}) {
	Log(fmt.Sprintln(a...))
}

// Logf is used to print debugging information
// It will be added to the logfile in ~/.heroku
func Logf(format string, a ...interface{}) {
	Log(fmt.Sprintf(format, a...))
}

// Debugln is used to print debugging information
// It will be added to the logfile in ~/.heroku and stderr if HEROKU_DEBUG is set.
func Debugln(a ...interface{}) {
	Logln(a...)
	if debugging {
		fmt.Fprintln(Stderr, a...)
	}
}

// PrintError is a helper that prints out formatted error messages in red text
func PrintError(e error) {
	if e == nil {
		return
	}
	Errln()
	Error(e.Error())
	if debugging {
		debug.PrintStack()
	}
}

// Warn shows a message with excalamation points prepended to stderr
func Warn(msg string) {
	bang := yellow(" ▸    ")
	msg = strings.TrimSpace(msg)
	msg = strings.Join(strings.Split(msg, "\n"), "\n"+bang)
	Errln(bang + msg)
}

// Error shows a message with excalamation points prepended to stderr
func Error(msg string) {
	bang := red(" " + errorArrow() + "    ")
	msg = strings.TrimSpace(msg)
	msg = strings.Join(strings.Split(msg, "\n"), "\n"+bang)
	Errln(bang + msg)
}

func errorArrow() string {
	if windows() {
		return "!"
	}
	return "▸"
}

// ExitIfError calls PrintError and exits if e is not null
func ExitIfError(e error) {
	if e != nil {
		PrintError(e)
		os.Exit(1)
	}
}

// LogIfError logs out an error if one arises
func LogIfError(e error) {
	if e != nil {
		Logln(e.Error())
	}
}

func isDebugging() bool {
	debug := strings.ToUpper(os.Getenv("HEROKU_DEBUG"))
	if debug == "TRUE" || debug == "1" {
		return true
	}
	return false
}

func isDebuggingHeaders() bool {
	debug := strings.ToUpper(os.Getenv("HEROKU_DEBUG_HEADERS"))
	if debug == "TRUE" || debug == "1" {
		return true
	}
	return false
}

func yellow(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[33m" + s + "\x1b[39m"
	}
	return s
}

func red(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[31m" + s + "\x1b[39m"
	}
	return s
}

func cyan(s string) string {
	if supportsColor() && !windows() {
		return "\x1b[36m" + s + "\x1b[39m"
	}
	return s
}

func windows() bool {
	return runtime.GOOS == "windows"
}

func istty() bool {
	return terminal.IsTerminal(int(os.Stdout.Fd()))
}

func supportsColor() bool {
	if !istty() {
		return false
	}
	for _, arg := range os.Args {
		if arg == "--no-color" {
			return false
		}
	}
	return os.Getenv("COLOR") != "false"
}

func plural(word string, count int) string {
	if count == 1 {
		return word
	}
	return word + "s"
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/Toorop/govh"
	"github.com/Toorop/govh/ip"
	//"os"
	"strconv"
	"strings"
	"time"
)

func ipHandler(cmd *Cmd) (resp string, err error) {
	// New govh client
	client := govh.NewClient(OVH_APP_KEY, OVH_APP_SECRET, ck)
	// New ip ressource
	ipr, err := ip.New(client)

	switch cmd.Action {
	// List
	case "list":
		ipType := "all"
		if len(cmd.Args) > 2 {
			ipType = cmd.Args[2]
		}
		ips, err := ipr.List(ipType)
		if err != nil {
			dieError(err)
		}
		for _, i := range ips {
			resp = fmt.Sprintf("%s%s\r\n", resp, i.IP)
		}
		if len(resp) > 2 {
			resp = resp[0 : len(resp)-2]
		}
		break
	case "lb":
		if len(cmd.Args) < 3 {
			dieError("\"ip lb\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
		}
		var t []byte
		t, err = ipr.LbList()
		resp = string(t)
		break

	case "fw":
		// ip fw ipBlock.IP list
		// ip fw x.x.x.x/y
		// Return IP V4 list of this block which is under firewall
		if len(cmd.Args) == 4 && cmd.Args[3] == "list" {
			block := ip.IpBlock{cmd.Args[2], ""}
			ips, err := ipr.FwListIpOfBlock(block)
			if err != nil {
				dieError(err)
			}
			for _, i := range ips {
				resp = fmt.Sprintf("%s%s\r\n", resp, i)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			break
		}

		// Add IP to firewall
		// cmd : ip fw ibBlock.IP ipV4 add
		if len(cmd.Args) == 5 && cmd.Args[4] == "add" {
			block := ip.IpBlock{cmd.Args[2], ""}
			if err = ipr.FwAddIp(block, cmd.Args[3]); err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("%s added to firewall", cmd.Args[3]))
		}

		// Get properties of a firewalled IP
		// ip fw ipBlock.IP ipV4 prop
		if len(cmd.Args) == 5 && cmd.Args[4] == "prop" {
			block := ip.IpBlock{cmd.Args[2], ""}
			i, err := ipr.FwGetIpProperties(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("ipOnFirewall: %s%sEnabled: %t%sState: %s", i.IpOnFirewall, NL, i.Enabled, NL, i.State))
			break
		}

		// Enable firewalll for IP ipv4
		// ip fw ipVlock ipV4 enable
		if len(cmd.Args) == 5 && cmd.Args[4] == "enable" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.FwSetFirewallEnable(block, cmd.Args[3], true)
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
			break
		}

		// Disable firewalll for IP ipv4
		// ip fw ipVlock ipV4 disable
		if len(cmd.Args) == 5 && cmd.Args[4] == "disable" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.FwSetFirewallEnable(block, cmd.Args[3], false)
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
			break
		}

		// Remove IPv4 from firewall
		// cmd : ip fw ipBlock.IP ipV4 remove
		if len(cmd.Args) == 5 && cmd.Args[4] == "remove" {
			block := ip.IpBlock{cmd.Args[2], ""}
			if err = ipr.FwRemoveIp(block, cmd.Args[3]); err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("%s removed from firewall", cmd.Args[3]))
		}

		// Get rules sequences
		// ip fw ipBlock.IP ipV4 listRules all
		if len(cmd.Args) >= 5 && cmd.Args[4] == "listRules" {
			block := ip.IpBlock{cmd.Args[2], ""}
			state := ""
			if len(cmd.Args) == 6 {
				state = cmd.Args[6]
			}
			t, err := ipr.FwGetRulesSequences(block, cmd.Args[3], state)
			if err != nil {
				dieError(err)
			}
			var r string
			if len(t) > 0 {
				r = fmt.Sprintf("%d", t[0])
				for _, s := range t[1:] {
					r = fmt.Sprintf("%s%s%d", r, NL, s)
				}
			}
			dieOk(r)
		}

		// Add rule
		// ip fw ipBlock.IP ipV4 addRule rule (as Json)
		if len(cmd.Args) == 6 && cmd.Args[4] == "addRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			// Check json
			var rule ip.FirewallRule2Add
			err := json.Unmarshal([]byte(cmd.Args[5]), &rule)
			if err != nil {
				dieError("Rule error. See doc at : https://github.com/Toorop/ovh-cli", err)
			}
			err = ipr.FwAddRule(block, cmd.Args[3], rule)
			if err != nil {
				dieError(err)
			}
			dieOk("OK")
		}

		// Remove rule
		// ip fw ipBlock.IP ipV4 remRule ruleSequence
		if len(cmd.Args) == 6 && cmd.Args[4] == "remRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			sequence, err := strconv.Atoi(cmd.Args[5])
			if err != nil {
				dieError(err)
			}
			err = ipr.FwRemoveRule(block, cmd.Args[3], sequence)
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("Rule %d removed", sequence))

		}

		// Get rule
		// ip fw ipBlock.IP ipV4 getRule sequence
		if len(cmd.Args) == 6 && cmd.Args[4] == "getRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			sequence, err := strconv.Atoi(cmd.Args[5])
			if err != nil {
				dieError(err)
			}
			rule, err := ipr.FwGetRule(block, cmd.Args[3], sequence)
			if err != nil {
				dieError(err)
			}
			out := ""
			if len(rule.Protocol) > 0 {
				out = fmt.Sprintf("%sProtocol: %s%s", out, rule.Protocol, NL)
			}
			if len(rule.Source) > 0 {
				out = fmt.Sprintf("%sSource: %s%s", out, rule.Source, NL)
			}
			if len(rule.DestinationPort) > 0 {
				out = fmt.Sprintf("%sDestinationPort: %s%s", out, rule.DestinationPort, NL)
			}

			out = fmt.Sprintf("%sSequence: %d%s", out, rule.Sequence, NL)

			if len(rule.Options) > 0 {
				out = fmt.Sprintf("%sOptions: %s%s", out, strings.Join(rule.Options, " "), NL)
			}
			if len(rule.Destination) > 0 {
				out = fmt.Sprintf("%sDestination: %s%s", out, rule.Destination, NL)
			}
			if len(rule.Rule) > 0 {
				out = fmt.Sprintf("%sRule: %s%s", out, rule.Rule, NL)
			}
			if len(rule.SourcePort) > 0 {
				out = fmt.Sprintf("%sSourcePort: %s%s", out, rule.SourcePort, NL)
			}
			if len(rule.State) > 0 {
				out = fmt.Sprintf("%sState: %s%s", out, rule.State, NL)
			}
			if len(rule.CreationDate) > 0 {
				out = fmt.Sprintf("%sCreationDate: %s%s", out, rule.CreationDate, NL)
			}
			if len(rule.Action) > 0 {
				out = fmt.Sprintf("%sAction: %s%s", out, rule.Action, NL)
			}
			dieOk(out[0 : len(out)-2])

		}
		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		break

	case "spam":
		// List of spamming IP
		// ip spam ipBlock.IP listSpammingIp STATE
		if len(cmd.Args) >= 4 && cmd.Args[3] == "listSpammingIp" {
			block := ip.IpBlock{cmd.Args[2], ""}
			state := ""
			if len(cmd.Args) == 5 {
				state = cmd.Args[4]
			}
			ips, err := ipr.SpamGetSpammingIps(block, state)
			if err != nil {
				dieError(err)
			}
			for _, ip := range ips {
				fmt.Println(ip)
			}
			dieOk("")
		}

		// detailed info about a spamming IP
		// ip spam ipBlock.IP ipv4 details
		if len(cmd.Args) == 5 && cmd.Args[4] == "details" {
			block := ip.IpBlock{cmd.Args[2], ""}
			spamIp, err := ipr.SpamGetSpamIp(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("Time: %d%sDate: %s%sIpSpamming: %s%sState: %s", spamIp.Time, NL, spamIp.Date, NL, spamIp.IpSpamming, NL, spamIp.State))
		}

		// Stats about a spamming IP
		// ip spam ipBlock.IP ipv4 stats FROM TO
		if len(cmd.Args) == 7 && cmd.Args[4] == "stats" {
			block := ip.IpBlock{cmd.Args[2], ""}

			from, err := strconv.ParseInt(cmd.Args[5], 10, 64)
			if err != nil {
				dieError(err)
			}

			to, err := strconv.ParseInt(cmd.Args[6], 10, 64)
			if err != nil {
				dieError(err)
			}
			spamStats, err := ipr.SpamGetIpStats(block, cmd.Args[3], time.Unix(from, 0), time.Unix(to, 0))
			if err != nil {
				dieError(err)
			}
			if spamStats == nil {
				dieOk("No spam stats for this period")
			}
			fmt.Printf("Blocked for the last time: %s%s", time.Unix(spamStats.Timestamp, 0).Format(time.RFC822Z), NL)
			fmt.Printf("Number of emails sent: %d%s", spamStats.Total, NL)
			fmt.Printf("Number of spams sent: %d%s", spamStats.NumberOfSpams, NL)
			fmt.Printf("Average score: %d%s%s", spamStats.AverageSpamScore, NL, NL)
			if len(spamStats.DetectedSpams) > 0 {
				fmt.Println("Detected Spams : ", NL)
			}
			for _, ds := range spamStats.DetectedSpams {
				fmt.Println("")
				fmt.Printf("\tDate: %s%s", time.Unix(ds.Date, 0).Format(time.RFC822Z), NL)
				fmt.Printf("\tMessage ID: %s%s", ds.MessageId, NL)
				fmt.Printf("\tDestination IP: %s%s", ds.DestinationIp, NL)
				fmt.Printf("\tScore: %d%s", ds.Spamscore, NL)
			}
			dieOk("")
		}

		// Unblock
		// ip spam ipBlock.IP ipv4 unblock
		if len(cmd.Args) == 5 && cmd.Args[4] == "unblock" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.SpamUnblockSpamIp(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
		}

		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		break
	case "getBlockedForSpam":
		// On va chercher les blocks
		ips, err := ipr.GetBlockedForSpam()
		if err != nil {
			dieError(err)
		}
		if len(ips) == 0 {
			dieOk("")
		}
		for _, i := range ips {
			fmt.Println(i)
		}
		dieOk("")

		// On les tests
		break

	default:
		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
	}
	return

}
<commit_msg>refactor return<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/Toorop/govh"
	"github.com/Toorop/govh/ip"
	//"os"
	"strconv"
	"strings"
	"time"
)

func ipHandler(cmd *Cmd) (err error) {
	// New govh client
	client := govh.NewClient(OVH_APP_KEY, OVH_APP_SECRET, ck)
	// New ip ressource
	ipr, err := ip.New(client)
	if err != nil {
		return
	}

	var resp string

	switch cmd.Action {
	// List
	case "list":
		ipType := "all"
		if len(cmd.Args) > 2 {
			ipType = cmd.Args[2]
		}
		ips, err := ipr.List(ipType)
		if err != nil {
			dieError(err)
		}
		for _, i := range ips {
			resp = fmt.Sprintf("%s%s\r\n", resp, i.IP)
		}
		if len(resp) > 2 {
			resp = resp[0 : len(resp)-2]
		}
		dieOk(resp)
		break
	case "lb":
		if len(cmd.Args) < 3 {
			dieError("\"ip lb\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
		}
		var t []byte
		t, err = ipr.LbList()
		resp = string(t)
		dieOk(resp)
		break

	case "fw":
		// ip fw ipBlock.IP list
		// ip fw x.x.x.x/y
		// Return IP V4 list of this block which is under firewall
		if len(cmd.Args) == 4 && cmd.Args[3] == "list" {
			block := ip.IpBlock{cmd.Args[2], ""}
			ips, err := ipr.FwListIpOfBlock(block)
			if err != nil {
				dieError(err)
			}
			for _, i := range ips {
				resp = fmt.Sprintf("%s%s\r\n", resp, i)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			dieOk(resp)
			break
		}

		// Add IP to firewall
		// cmd : ip fw ibBlock.IP ipV4 add
		if len(cmd.Args) == 5 && cmd.Args[4] == "add" {
			block := ip.IpBlock{cmd.Args[2], ""}
			if err = ipr.FwAddIp(block, cmd.Args[3]); err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("%s added to firewall", cmd.Args[3]))
		}

		// Get properties of a firewalled IP
		// ip fw ipBlock.IP ipV4 prop
		if len(cmd.Args) == 5 && cmd.Args[4] == "prop" {
			block := ip.IpBlock{cmd.Args[2], ""}
			i, err := ipr.FwGetIpProperties(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("ipOnFirewall: %s%sEnabled: %t%sState: %s", i.IpOnFirewall, NL, i.Enabled, NL, i.State))
			break
		}

		// Enable firewalll for IP ipv4
		// ip fw ipVlock ipV4 enable
		if len(cmd.Args) == 5 && cmd.Args[4] == "enable" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.FwSetFirewallEnable(block, cmd.Args[3], true)
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
			break
		}

		// Disable firewalll for IP ipv4
		// ip fw ipVlock ipV4 disable
		if len(cmd.Args) == 5 && cmd.Args[4] == "disable" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.FwSetFirewallEnable(block, cmd.Args[3], false)
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
			break
		}

		// Remove IPv4 from firewall
		// cmd : ip fw ipBlock.IP ipV4 remove
		if len(cmd.Args) == 5 && cmd.Args[4] == "remove" {
			block := ip.IpBlock{cmd.Args[2], ""}
			if err = ipr.FwRemoveIp(block, cmd.Args[3]); err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("%s removed from firewall", cmd.Args[3]))
		}

		// Get rules sequences
		// ip fw ipBlock.IP ipV4 listRules all
		if len(cmd.Args) >= 5 && cmd.Args[4] == "listRules" {
			block := ip.IpBlock{cmd.Args[2], ""}
			state := ""
			if len(cmd.Args) == 6 {
				state = cmd.Args[6]
			}
			t, err := ipr.FwGetRulesSequences(block, cmd.Args[3], state)
			if err != nil {
				dieError(err)
			}
			var r string
			if len(t) > 0 {
				r = fmt.Sprintf("%d", t[0])
				for _, s := range t[1:] {
					r = fmt.Sprintf("%s%s%d", r, NL, s)
				}
			}
			dieOk(r)
		}

		// Add rule
		// ip fw ipBlock.IP ipV4 addRule rule (as Json)
		if len(cmd.Args) == 6 && cmd.Args[4] == "addRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			// Check json
			var rule ip.FirewallRule2Add
			err := json.Unmarshal([]byte(cmd.Args[5]), &rule)
			if err != nil {
				dieError("Rule error. See doc at : https://github.com/Toorop/ovh-cli", err)
			}
			err = ipr.FwAddRule(block, cmd.Args[3], rule)
			if err != nil {
				dieError(err)
			}
			dieOk("OK")
		}

		// Remove rule
		// ip fw ipBlock.IP ipV4 remRule ruleSequence
		if len(cmd.Args) == 6 && cmd.Args[4] == "remRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			sequence, err := strconv.Atoi(cmd.Args[5])
			if err != nil {
				dieError(err)
			}
			err = ipr.FwRemoveRule(block, cmd.Args[3], sequence)
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("Rule %d removed", sequence))

		}

		// Get rule
		// ip fw ipBlock.IP ipV4 getRule sequence
		if len(cmd.Args) == 6 && cmd.Args[4] == "getRule" {
			block := ip.IpBlock{cmd.Args[2], ""}
			sequence, err := strconv.Atoi(cmd.Args[5])
			if err != nil {
				dieError(err)
			}
			rule, err := ipr.FwGetRule(block, cmd.Args[3], sequence)
			if err != nil {
				dieError(err)
			}
			out := ""
			if len(rule.Protocol) > 0 {
				out = fmt.Sprintf("%sProtocol: %s%s", out, rule.Protocol, NL)
			}
			if len(rule.Source) > 0 {
				out = fmt.Sprintf("%sSource: %s%s", out, rule.Source, NL)
			}
			if len(rule.DestinationPort) > 0 {
				out = fmt.Sprintf("%sDestinationPort: %s%s", out, rule.DestinationPort, NL)
			}

			out = fmt.Sprintf("%sSequence: %d%s", out, rule.Sequence, NL)

			if len(rule.Options) > 0 {
				out = fmt.Sprintf("%sOptions: %s%s", out, strings.Join(rule.Options, " "), NL)
			}
			if len(rule.Destination) > 0 {
				out = fmt.Sprintf("%sDestination: %s%s", out, rule.Destination, NL)
			}
			if len(rule.Rule) > 0 {
				out = fmt.Sprintf("%sRule: %s%s", out, rule.Rule, NL)
			}
			if len(rule.SourcePort) > 0 {
				out = fmt.Sprintf("%sSourcePort: %s%s", out, rule.SourcePort, NL)
			}
			if len(rule.State) > 0 {
				out = fmt.Sprintf("%sState: %s%s", out, rule.State, NL)
			}
			if len(rule.CreationDate) > 0 {
				out = fmt.Sprintf("%sCreationDate: %s%s", out, rule.CreationDate, NL)
			}
			if len(rule.Action) > 0 {
				out = fmt.Sprintf("%sAction: %s%s", out, rule.Action, NL)
			}
			dieOk(out[0 : len(out)-2])

		}
		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		break

	case "spam":
		// List of spamming IP
		// ip spam ipBlock.IP listSpammingIp STATE
		if len(cmd.Args) >= 4 && cmd.Args[3] == "listSpammingIp" {
			block := ip.IpBlock{cmd.Args[2], ""}
			state := ""
			if len(cmd.Args) == 5 {
				state = cmd.Args[4]
			}
			ips, err := ipr.SpamGetSpammingIps(block, state)
			if err != nil {
				dieError(err)
			}
			for _, ip := range ips {
				fmt.Println(ip)
			}
			dieOk("")
		}

		// detailed info about a spamming IP
		// ip spam ipBlock.IP ipv4 details
		if len(cmd.Args) == 5 && cmd.Args[4] == "details" {
			block := ip.IpBlock{cmd.Args[2], ""}
			spamIp, err := ipr.SpamGetSpamIp(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk(fmt.Sprintf("Time: %d%sDate: %s%sIpSpamming: %s%sState: %s", spamIp.Time, NL, spamIp.Date, NL, spamIp.IpSpamming, NL, spamIp.State))
		}

		// Stats about a spamming IP
		// ip spam ipBlock.IP ipv4 stats FROM TO
		if len(cmd.Args) == 7 && cmd.Args[4] == "stats" {
			block := ip.IpBlock{cmd.Args[2], ""}

			from, err := strconv.ParseInt(cmd.Args[5], 10, 64)
			if err != nil {
				dieError(err)
			}

			to, err := strconv.ParseInt(cmd.Args[6], 10, 64)
			if err != nil {
				dieError(err)
			}
			spamStats, err := ipr.SpamGetIpStats(block, cmd.Args[3], time.Unix(from, 0), time.Unix(to, 0))
			if err != nil {
				dieError(err)
			}
			if spamStats == nil {
				dieOk("No spam stats for this period")
			}
			fmt.Printf("Blocked for the last time: %s%s", time.Unix(spamStats.Timestamp, 0).Format(time.RFC822Z), NL)
			fmt.Printf("Number of emails sent: %d%s", spamStats.Total, NL)
			fmt.Printf("Number of spams sent: %d%s", spamStats.NumberOfSpams, NL)
			fmt.Printf("Average score: %d%s%s", spamStats.AverageSpamScore, NL, NL)
			if len(spamStats.DetectedSpams) > 0 {
				fmt.Println("Detected Spams : ", NL)
			}
			for _, ds := range spamStats.DetectedSpams {
				fmt.Println("")
				fmt.Printf("\tDate: %s%s", time.Unix(ds.Date, 0).Format(time.RFC822Z), NL)
				fmt.Printf("\tMessage ID: %s%s", ds.MessageId, NL)
				fmt.Printf("\tDestination IP: %s%s", ds.DestinationIp, NL)
				fmt.Printf("\tScore: %d%s", ds.Spamscore, NL)
			}
			dieOk("")
		}

		// Unblock
		// ip spam ipBlock.IP ipv4 unblock
		if len(cmd.Args) == 5 && cmd.Args[4] == "unblock" {
			block := ip.IpBlock{cmd.Args[2], ""}
			err := ipr.SpamUnblockSpamIp(block, cmd.Args[3])
			if err != nil {
				dieError(err)
			}
			dieOk("ok")
		}

		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		break
	case "getBlockedForSpam":
		// On va chercher les blocks
		ips, err := ipr.GetBlockedForSpam()
		if err != nil {
			dieError(err)
		}
		if len(ips) == 0 {
			dieOk("")
		}
		for _, i := range ips {
			fmt.Println(i)
		}
		dieOk("")

		// On les tests
		break

	default:
		err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
	}
	return

}
<|endoftext|>