<commit_before>package commands

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"code.google.com/p/gopass"
	"github.com/spf13/cobra"
	api "github.com/yungsang/dockerclient"
	"github.com/yungsang/tablewriter"
	"github.com/yungsang/talk2docker/client"
)

var cmdHost = &cobra.Command{
	Use:   "host [command]",
	Short: "Manage hosts",
	Long:  appName + " host - Manage hosts",
	Run: func(ctx *cobra.Command, args []string) {
		ctx.Usage()
	},
}

var cmdListHosts = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List hosts",
	Long:    appName + " host list - List hosts",
	Run:     listHosts,
}

var cmdSwitchHost = &cobra.Command{
	Use:     "switch <NAME>",
	Aliases: []string{"sw"},
	Short:   "Switch the default host",
	Long:    appName + " host switch - Switch the default host",
	Run:     switchHost,
}

var cmdGetHostInfo = &cobra.Command{
	Use:   "info",
	Short: "Get the host information",
	Long:  appName + " host info - Get the host information",
	Run:   getHostInfo,
}

var cmdLogin = &cobra.Command{
	Use:   "login",
	Short: "Log in to a Docker registry server through the host",
	Long:  appName + " host login - Log in to a Docker registry server through the host",
	Run:   login,
}

var cmdAddHost = &cobra.Command{
	Use:   "add <NAME> <URL> [DESCRIPTION]",
	Short: "Add a new host into the config file",
	Long:  appName + " host add - Add a new host into the config",
	Run:   addHost,
}

var cmdRmHost = &cobra.Command{
	Use:   "rm <NAME>",
	Short: "Rmove a host from the config file",
	Long:  appName + " host rm - Rmove a host from the config file",
	Run:   rmHost,
}

var cmdHosts = &cobra.Command{
	Use:   "hosts",
	Short: "Shortcut to list hosts",
	Long:  appName + " hosts - List hosts",
	Run:   listHosts,
}

func init() {
	cmdListHosts.Flags().BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	cmdListHosts.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdHosts.Flags().BoolVarP(&boolQuiet, "quiet", "q", false, "Only display host names")
	cmdHosts.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdGetHostInfo.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdHost.AddCommand(cmdListHosts)
	cmdHost.AddCommand(cmdSwitchHost)
	cmdHost.AddCommand(cmdGetHostInfo)
	cmdHost.AddCommand(cmdLogin)
	cmdHost.AddCommand(cmdAddHost)
	cmdHost.AddCommand(cmdRmHost)
}

func listHosts(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	if boolQuiet {
		for _, host := range config.Hosts {
			fmt.Println(host.Name)
		}
		return
	}

	var items [][]string
	for _, host := range config.Hosts {
		out := []string{
			FormatBool(host.Name == config.Default, "*", ""),
			host.Name,
			host.URL,
			FormatNonBreakingString(host.Description),
			FormatBool(host.TLS, "YES", ""),
		}
		items = append(items, out)
	}

	var header = []string{
		"",
		"Name",
		"URL",
		"Description",
		"TLS",
	}

	table := tablewriter.NewWriter(os.Stdout)
	if !boolNoHeader {
		table.SetHeader(header)
	} else {
		table.SetBorder(false)
	}
	table.AppendBulk(items)
	table.Render()
}

func switchHost(ctx *cobra.Command, args []string) {
	if len(args) == 0 {
		fmt.Println("Needs an argument <NAME> to switch")
		ctx.Usage()
		return
	}

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	var name = args[0]

	host, err := config.GetHost(name)
	if err != nil {
		log.Fatal(err)
	}

	config.Default = host.Name

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}

func getHostInfo(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	docker, err := client.GetDockerClient(configPath, host.Name)
	if err != nil {
		log.Fatal(err)
	}

	info, err := docker.Info()
	if err != nil {
		log.Fatal(err)
	}

	var items [][]string

	items = append(items, []string{
		"Host", host.Name,
	})
	items = append(items, []string{
		"URL", host.URL,
	})
	items = append(items, []string{
		"Description", FormatNonBreakingString(host.Description),
	})
	items = append(items, []string{
		"TLS", FormatBool(host.TLS, "Supported", "No"),
	})
	if host.TLS {
		items = append(items, []string{
			FormatNonBreakingString("  CA Certificate file"), FormatNonBreakingString(host.TLSCaCert),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Certificate file"), FormatNonBreakingString(host.TLSCert),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Key file"), FormatNonBreakingString(host.TLSKey),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Verify"), FormatBool(host.TLSVerify, "Required", "No"),
		})
	}

	items = append(items, []string{
		"Containers", strconv.Itoa(info.Containers),
	})
	items = append(items, []string{
		"Images", strconv.Itoa(info.Images),
	})
	items = append(items, []string{
		"Storage Driver", info.Driver,
	})
	for _, pair := range info.DriverStatus {
		items = append(items, []string{
			FormatNonBreakingString(fmt.Sprintf("  %s", pair[0])), FormatNonBreakingString(fmt.Sprintf("%s", pair[1])),
		})
	}
	items = append(items, []string{
		"Execution Driver", info.ExecutionDriver,
	})
	items = append(items, []string{
		"Kernel Version", info.KernelVersion,
	})
	items = append(items, []string{
		"Operating System", FormatNonBreakingString(info.OperatingSystem),
	})
	items = append(items, []string{
		"CPUs", strconv.Itoa(info.NCPU),
	})
	items = append(items, []string{
		"Total Memory", strconv.FormatInt(info.MemTotal, 10),
	})

	items = append(items, []string{
		"Index Server Address", info.IndexServerAddress,
	})

	items = append(items, []string{
		"Memory Limit", FormatBool(info.MemoryLimit != 0, "Supported", "No"),
	})
	items = append(items, []string{
		"Swap Limit", FormatBool(info.SwapLimit != 0, "Supported", "No"),
	})
	items = append(items, []string{
		"IPv4 Forwarding", FormatBool(info.IPv4Forwarding != 0, "Enabled", "Disabled"),
	})

	items = append(items, []string{
		"ID", info.ID,
	})
	items = append(items, []string{
		"Name", info.Name,
	})
	var labels []string
	for _, pair := range info.Labels {
		labels = append(labels, fmt.Sprintf("%s: %s\n", pair[0], pair[1]))
	}
	items = append(items, []string{
		"Labels", FormatNonBreakingString(strings.Join(labels, ", ")),
	})

	items = append(items, []string{
		"Debug Mode", FormatBool(info.Debug != 0, "Yes", "No"),
	})
	if info.Debug != 0 {
		items = append(items, []string{
			FormatNonBreakingString("  Events Listeners"), strconv.Itoa(info.NEventsListener),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Fds"), strconv.Itoa(info.NFd),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Goroutines"), strconv.Itoa(info.NGoroutines),
		})

		items = append(items, []string{
			FormatNonBreakingString("  Init Path"), info.InitPath,
		})
		items = append(items, []string{
			FormatNonBreakingString("  Init SHA1"), info.InitSha1,
		})
		items = append(items, []string{
			FormatNonBreakingString("  Docker Root Dir"), info.DockerRootDir,
		})
	}

	table := tablewriter.NewWriter(os.Stdout)
	if boolNoHeader {
		table.SetBorder(false)
	}
	table.AppendBulk(items)
	table.Render()
}

func login(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	docker, err := client.GetDockerClient(configPath, host.Name)
	if err != nil {
		log.Fatal(err)
	}

	info, err := docker.Info()
	if err != nil {
		log.Fatal(err)
	}

	indexServerAddress := info.IndexServerAddress

	server, notFound := config.GetIndexServer(indexServerAddress)

	var authConfig api.AuthConfig
	authConfig.ServerAddress = server.URL

	promptDefault := func(prompt string, configDefault string) {
		if configDefault == "" {
			fmt.Printf("%s: ", prompt)
		} else {
			fmt.Printf("%s (%s): ", prompt, configDefault)
		}
	}

	readInput := func() string {
		reader := bufio.NewReader(os.Stdin)
		line, _, err := reader.ReadLine()
		if err != nil {
			log.Fatal(err)
		}
		return string(line)
	}

	promptDefault("Username", server.Username)
	authConfig.Username = readInput()
	if authConfig.Username == "" {
		authConfig.Username = server.Username
	}

	authConfig.Password, err = gopass.GetPass("Password: ")

	promptDefault("Email", server.Email)
	authConfig.Email = readInput()
	if authConfig.Email == "" {
		authConfig.Email = server.Email
	}

	err = docker.Auth(&authConfig)
	if err != nil {
		log.Fatal(err)
	}

	server.Username = authConfig.Username
	server.Email = authConfig.Email
	server.Auth = server.Encode(authConfig.Username, authConfig.Password)

	if notFound != nil {
		config.IndexServers = append(config.IndexServers, *server)
	}

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Login Succeeded!")
}

func addHost(ctx *cobra.Command, args []string) {
	if len(args) < 2 {
		fmt.Println("Needs two arguments <NAME> and <URL> at least")
		ctx.Usage()
		return
	}

	name := args[0]
	url := args[1]
	desc := ""
	if len(args) > 2 {
		desc = strings.Join(args[2:], " ")
	}

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	_, err = config.GetHost(name)
	if err == nil {
		log.Fatal(fmt.Sprintf("\"%s\" already exists", name))
	}

	newHost := client.Host{
		Name:        name,
		URL:         url,
		Description: desc,
	}

	config.Default = newHost.Name
	config.Hosts = append(config.Hosts, newHost)

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}

func rmHost(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		fmt.Println("Needs an argument <NAME> to remove")
		ctx.Usage()
		return
	}

	name := args[0]

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	if config.Default == name {
		log.Fatal("You can't remove the default host.")
	}

	_, err = config.GetHost(name)
	if err != nil {
		log.Fatal(err)
	}

	hosts := []client.Host{}

	for _, host := range config.Hosts {
		if host.Name != name {
			hosts = append(hosts, host)
		}
	}

	config.Hosts = hosts

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}
<commit_msg>Add aliases for host remove command<commit_after>package commands

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"code.google.com/p/gopass"
	"github.com/spf13/cobra"
	api "github.com/yungsang/dockerclient"
	"github.com/yungsang/tablewriter"
	"github.com/yungsang/talk2docker/client"
)

var cmdHost = &cobra.Command{
	Use:   "host [command]",
	Short: "Manage hosts",
	Long:  appName + " host - Manage hosts",
	Run: func(ctx *cobra.Command, args []string) {
		ctx.Usage()
	},
}

var cmdListHosts = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List hosts",
	Long:    appName + " host list - List hosts",
	Run:     listHosts,
}

var cmdSwitchHost = &cobra.Command{
	Use:     "switch <NAME>",
	Aliases: []string{"sw"},
	Short:   "Switch the default host",
	Long:    appName + " host switch - Switch the default host",
	Run:     switchHost,
}

var cmdGetHostInfo = &cobra.Command{
	Use:   "info",
	Short: "Get the host information",
	Long:  appName + " host info - Get the host information",
	Run:   getHostInfo,
}

var cmdLogin = &cobra.Command{
	Use:   "login",
	Short: "Log in to a Docker registry server through the host",
	Long:  appName + " host login - Log in to a Docker registry server through the host",
	Run:   login,
}

var cmdAddHost = &cobra.Command{
	Use:   "add <NAME> <URL> [DESCRIPTION]",
	Short: "Add a new host into the config file",
	Long:  appName + " host add - Add a new host into the config",
	Run:   addHost,
}

var cmdRmHost = &cobra.Command{
	Use:   "remove <NAME>",
	Aliases: []string{"rm", "delete", "del"},
	Short: "Rmove a host from the config file",
	Long:  appName + " host remove - Rmove a host from the config file",
	Run:   rmHost,
}

var cmdHosts = &cobra.Command{
	Use:   "hosts",
	Short: "Shortcut to list hosts",
	Long:  appName + " hosts - List hosts",
	Run:   listHosts,
}

func init() {
	cmdListHosts.Flags().BoolVarP(&boolQuiet, "quiet", "q", false, "Only display numeric IDs")
	cmdListHosts.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdHosts.Flags().BoolVarP(&boolQuiet, "quiet", "q", false, "Only display host names")
	cmdHosts.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdGetHostInfo.Flags().BoolVarP(&boolNoHeader, "no-header", "n", false, "Omit the header")

	cmdHost.AddCommand(cmdListHosts)
	cmdHost.AddCommand(cmdSwitchHost)
	cmdHost.AddCommand(cmdGetHostInfo)
	cmdHost.AddCommand(cmdLogin)
	cmdHost.AddCommand(cmdAddHost)
	cmdHost.AddCommand(cmdRmHost)
}

func listHosts(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	if boolQuiet {
		for _, host := range config.Hosts {
			fmt.Println(host.Name)
		}
		return
	}

	var items [][]string
	for _, host := range config.Hosts {
		out := []string{
			FormatBool(host.Name == config.Default, "*", ""),
			host.Name,
			host.URL,
			FormatNonBreakingString(host.Description),
			FormatBool(host.TLS, "YES", ""),
		}
		items = append(items, out)
	}

	var header = []string{
		"",
		"Name",
		"URL",
		"Description",
		"TLS",
	}

	table := tablewriter.NewWriter(os.Stdout)
	if !boolNoHeader {
		table.SetHeader(header)
	} else {
		table.SetBorder(false)
	}
	table.AppendBulk(items)
	table.Render()
}

func switchHost(ctx *cobra.Command, args []string) {
	if len(args) == 0 {
		fmt.Println("Needs an argument <NAME> to switch")
		ctx.Usage()
		return
	}

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	var name = args[0]

	host, err := config.GetHost(name)
	if err != nil {
		log.Fatal(err)
	}

	config.Default = host.Name

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}

func getHostInfo(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	docker, err := client.GetDockerClient(configPath, host.Name)
	if err != nil {
		log.Fatal(err)
	}

	info, err := docker.Info()
	if err != nil {
		log.Fatal(err)
	}

	var items [][]string

	items = append(items, []string{
		"Host", host.Name,
	})
	items = append(items, []string{
		"URL", host.URL,
	})
	items = append(items, []string{
		"Description", FormatNonBreakingString(host.Description),
	})
	items = append(items, []string{
		"TLS", FormatBool(host.TLS, "Supported", "No"),
	})
	if host.TLS {
		items = append(items, []string{
			FormatNonBreakingString("  CA Certificate file"), FormatNonBreakingString(host.TLSCaCert),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Certificate file"), FormatNonBreakingString(host.TLSCert),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Key file"), FormatNonBreakingString(host.TLSKey),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Verify"), FormatBool(host.TLSVerify, "Required", "No"),
		})
	}

	items = append(items, []string{
		"Containers", strconv.Itoa(info.Containers),
	})
	items = append(items, []string{
		"Images", strconv.Itoa(info.Images),
	})
	items = append(items, []string{
		"Storage Driver", info.Driver,
	})
	for _, pair := range info.DriverStatus {
		items = append(items, []string{
			FormatNonBreakingString(fmt.Sprintf("  %s", pair[0])), FormatNonBreakingString(fmt.Sprintf("%s", pair[1])),
		})
	}
	items = append(items, []string{
		"Execution Driver", info.ExecutionDriver,
	})
	items = append(items, []string{
		"Kernel Version", info.KernelVersion,
	})
	items = append(items, []string{
		"Operating System", FormatNonBreakingString(info.OperatingSystem),
	})
	items = append(items, []string{
		"CPUs", strconv.Itoa(info.NCPU),
	})
	items = append(items, []string{
		"Total Memory", strconv.FormatInt(info.MemTotal, 10),
	})

	items = append(items, []string{
		"Index Server Address", info.IndexServerAddress,
	})

	items = append(items, []string{
		"Memory Limit", FormatBool(info.MemoryLimit != 0, "Supported", "No"),
	})
	items = append(items, []string{
		"Swap Limit", FormatBool(info.SwapLimit != 0, "Supported", "No"),
	})
	items = append(items, []string{
		"IPv4 Forwarding", FormatBool(info.IPv4Forwarding != 0, "Enabled", "Disabled"),
	})

	items = append(items, []string{
		"ID", info.ID,
	})
	items = append(items, []string{
		"Name", info.Name,
	})
	var labels []string
	for _, pair := range info.Labels {
		labels = append(labels, fmt.Sprintf("%s: %s\n", pair[0], pair[1]))
	}
	items = append(items, []string{
		"Labels", FormatNonBreakingString(strings.Join(labels, ", ")),
	})

	items = append(items, []string{
		"Debug Mode", FormatBool(info.Debug != 0, "Yes", "No"),
	})
	if info.Debug != 0 {
		items = append(items, []string{
			FormatNonBreakingString("  Events Listeners"), strconv.Itoa(info.NEventsListener),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Fds"), strconv.Itoa(info.NFd),
		})
		items = append(items, []string{
			FormatNonBreakingString("  Goroutines"), strconv.Itoa(info.NGoroutines),
		})

		items = append(items, []string{
			FormatNonBreakingString("  Init Path"), info.InitPath,
		})
		items = append(items, []string{
			FormatNonBreakingString("  Init SHA1"), info.InitSha1,
		})
		items = append(items, []string{
			FormatNonBreakingString("  Docker Root Dir"), info.DockerRootDir,
		})
	}

	table := tablewriter.NewWriter(os.Stdout)
	if boolNoHeader {
		table.SetBorder(false)
	}
	table.AppendBulk(items)
	table.Render()
}

func login(ctx *cobra.Command, args []string) {
	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	docker, err := client.GetDockerClient(configPath, host.Name)
	if err != nil {
		log.Fatal(err)
	}

	info, err := docker.Info()
	if err != nil {
		log.Fatal(err)
	}

	indexServerAddress := info.IndexServerAddress

	server, notFound := config.GetIndexServer(indexServerAddress)

	var authConfig api.AuthConfig
	authConfig.ServerAddress = server.URL

	promptDefault := func(prompt string, configDefault string) {
		if configDefault == "" {
			fmt.Printf("%s: ", prompt)
		} else {
			fmt.Printf("%s (%s): ", prompt, configDefault)
		}
	}

	readInput := func() string {
		reader := bufio.NewReader(os.Stdin)
		line, _, err := reader.ReadLine()
		if err != nil {
			log.Fatal(err)
		}
		return string(line)
	}

	promptDefault("Username", server.Username)
	authConfig.Username = readInput()
	if authConfig.Username == "" {
		authConfig.Username = server.Username
	}

	authConfig.Password, err = gopass.GetPass("Password: ")

	promptDefault("Email", server.Email)
	authConfig.Email = readInput()
	if authConfig.Email == "" {
		authConfig.Email = server.Email
	}

	err = docker.Auth(&authConfig)
	if err != nil {
		log.Fatal(err)
	}

	server.Username = authConfig.Username
	server.Email = authConfig.Email
	server.Auth = server.Encode(authConfig.Username, authConfig.Password)

	if notFound != nil {
		config.IndexServers = append(config.IndexServers, *server)
	}

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Login Succeeded!")
}

func addHost(ctx *cobra.Command, args []string) {
	if len(args) < 2 {
		fmt.Println("Needs two arguments <NAME> and <URL> at least")
		ctx.Usage()
		return
	}

	name := args[0]
	url := args[1]
	desc := ""
	if len(args) > 2 {
		desc = strings.Join(args[2:], " ")
	}

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	_, err = config.GetHost(name)
	if err == nil {
		log.Fatal(fmt.Sprintf("\"%s\" already exists", name))
	}

	newHost := client.Host{
		Name:        name,
		URL:         url,
		Description: desc,
	}

	config.Default = newHost.Name
	config.Hosts = append(config.Hosts, newHost)

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}

func rmHost(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		fmt.Println("Needs an argument <NAME> to remove")
		ctx.Usage()
		return
	}

	name := args[0]

	path := os.ExpandEnv(configPath)

	config, err := client.LoadConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	if config.Default == name {
		log.Fatal("You can't remove the default host.")
	}

	_, err = config.GetHost(name)
	if err != nil {
		log.Fatal(err)
	}

	hosts := []client.Host{}

	for _, host := range config.Hosts {
		if host.Name != name {
			hosts = append(hosts, host)
		}
	}

	config.Hosts = hosts

	err = config.SaveConfig(path)
	if err != nil {
		log.Fatal(err)
	}

	listHosts(ctx, args)
}
<|endoftext|>


<commit_before>package filesys

import (
	"context"
	"os"
	"path"
	"path/filepath"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type Dir struct {
	Path       string
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

var _ = fs.Node(&Dir{})
var _ = fs.NodeCreater(&Dir{})
var _ = fs.NodeMkdirer(&Dir{})
var _ = fs.NodeRequestLookuper(&Dir{})
var _ = fs.HandleReadDirAller(&Dir{})
var _ = fs.NodeRemover(&Dir{})
var _ = fs.NodeRenamer(&Dir{})
var _ = fs.NodeSetattrer(&Dir{})

func (dir *Dir) Attr(context context.Context, attr *fuse.Attr) error {

	// https://github.com/bazil/fuse/issues/196
	attr.Valid = time.Second

	if dir.Path == "/" {
		attr.Mode = os.ModeDir | 0777
		return nil
	}

	item := dir.wfs.listDirectoryEntriesCache.Get(dir.Path)
	if item != nil && !item.Expired() {
		entry := item.Value().(*filer_pb.Entry)

		attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		attr.Mode = os.FileMode(entry.Attributes.FileMode)
		attr.Gid = entry.Attributes.Gid
		attr.Uid = entry.Attributes.Uid

		return nil
	}

	parent, name := filepath.Split(dir.Path)

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.LookupDirectoryEntryRequest{
			Directory: parent,
			Name:      name,
		}

		glog.V(1).Infof("read dir %s attr: %v", dir.Path, request)
		resp, err := client.LookupDirectoryEntry(context, request)
		if err != nil {
			glog.V(0).Infof("read dir %s attr %v: %v", dir.Path, request, err)
			return err
		}

		if resp.Entry != nil {
			dir.attributes = resp.Entry.Attributes
		}

		dir.wfs.listDirectoryEntriesCache.Set(dir.Path, resp.Entry, dir.wfs.option.EntryCacheTtl)

		return nil
	})

	if err != nil {
		return err
	}

	// glog.V(1).Infof("dir %s: %v", dir.Path, attributes)
	// glog.V(1).Infof("dir %s permission: %v", dir.Path, os.FileMode(attributes.FileMode))

	attr.Mode = os.FileMode(dir.attributes.FileMode) | os.ModeDir

	attr.Mtime = time.Unix(dir.attributes.Mtime, 0)
	attr.Ctime = time.Unix(dir.attributes.Crtime, 0)
	attr.Gid = dir.attributes.Gid
	attr.Uid = dir.attributes.Uid

	return nil
}

func (dir *Dir) newFile(name string, entry *filer_pb.Entry) *File {
	return &File{
		Name:  name,
		dir:   dir,
		wfs:   dir.wfs,
		entry: entry,
	}
}

func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {

	request := &filer_pb.CreateEntryRequest{
		Directory: dir.Path,
		Entry: &filer_pb.Entry{
			Name:        req.Name,
			IsDirectory: req.Mode&os.ModeDir > 0,
			Attributes: &filer_pb.FuseAttributes{
				Mtime:       time.Now().Unix(),
				Crtime:      time.Now().Unix(),
				FileMode:    uint32(req.Mode),
				Uid:         req.Uid,
				Gid:         req.Gid,
				Collection:  dir.wfs.option.Collection,
				Replication: dir.wfs.option.Replication,
				TtlSec:      dir.wfs.option.TtlSec,
			},
		},
	}
	glog.V(1).Infof("create: %v", request)

	if request.Entry.IsDirectory {
		if err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
			if _, err := client.CreateEntry(ctx, request); err != nil {
				glog.V(0).Infof("create %s/%s: %v", dir.Path, req.Name, err)
				return fuse.EIO
			}
			return nil
		}); err != nil {
			return nil, nil, err
		}
	}

	file := dir.newFile(req.Name, request.Entry)
	if !request.Entry.IsDirectory {
		file.isOpen = true
	}
	fh := dir.wfs.AcquireHandle(file, req.Uid, req.Gid)
	fh.dirtyMetadata = true
	return file, fh, nil

}

func (dir *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.CreateEntryRequest{
			Directory: dir.Path,
			Entry: &filer_pb.Entry{
				Name:        req.Name,
				IsDirectory: true,
				Attributes: &filer_pb.FuseAttributes{
					Mtime:    time.Now().Unix(),
					Crtime:   time.Now().Unix(),
					FileMode: uint32(req.Mode),
					Uid:      req.Uid,
					Gid:      req.Gid,
				},
			},
		}

		glog.V(1).Infof("mkdir: %v", request)
		if _, err := client.CreateEntry(ctx, request); err != nil {
			glog.V(0).Infof("mkdir %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		return nil
	})

	if err == nil {
		node := &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs}
		return node, nil
	}

	return nil, err
}

func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {

	var entry *filer_pb.Entry

	item := dir.wfs.listDirectoryEntriesCache.Get(path.Join(dir.Path, req.Name))
	if item != nil && !item.Expired() {
		entry = item.Value().(*filer_pb.Entry)
	}

	if entry == nil {
		err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

			request := &filer_pb.LookupDirectoryEntryRequest{
				Directory: dir.Path,
				Name:      req.Name,
			}

			glog.V(4).Infof("lookup directory entry: %v", request)
			resp, err := client.LookupDirectoryEntry(ctx, request)
			if err != nil {
				// glog.V(0).Infof("lookup %s/%s: %v", dir.Path, name, err)
				return fuse.ENOENT
			}

			entry = resp.Entry

			dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, dir.wfs.option.EntryCacheTtl)

			return nil
		})
	}

	if entry != nil {
		if entry.IsDirectory {
			node = &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs, attributes: entry.Attributes}
		} else {
			node = dir.newFile(req.Name, entry)
		}

		resp.EntryValid = time.Duration(0)
		resp.Attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		resp.Attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		resp.Attr.Mode = os.FileMode(entry.Attributes.FileMode)
		resp.Attr.Gid = entry.Attributes.Gid
		resp.Attr.Uid = entry.Attributes.Uid

		return node, nil
	}

	return nil, fuse.ENOENT
}

func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) {

	err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		paginationLimit := 1024
		remaining := dir.wfs.option.DirListingLimit

		lastEntryName := ""

		for remaining >= 0 {

			request := &filer_pb.ListEntriesRequest{
				Directory:         dir.Path,
				StartFromFileName: lastEntryName,
				Limit:             uint32(paginationLimit),
			}

			glog.V(4).Infof("read directory: %v", request)
			resp, err := client.ListEntries(ctx, request)
			if err != nil {
				glog.V(0).Infof("list %s: %v", dir.Path, err)
				return fuse.EIO
			}

			cacheTtl := estimatedCacheTtl(len(resp.Entries))

			for _, entry := range resp.Entries {
				if entry.IsDirectory {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_Dir}
					ret = append(ret, dirent)
				} else {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_File}
					ret = append(ret, dirent)
				}
				dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, cacheTtl)
				lastEntryName = entry.Name
			}

			remaining -= len(resp.Entries)

			if len(resp.Entries) < paginationLimit {
				break
			}

		}

		return nil
	})

	return ret, err
}

func (dir *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {

	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:    dir.Path,
			Name:         req.Name,
			IsDeleteData: true,
		}

		glog.V(1).Infof("remove directory entry: %v", request)
		_, err := client.DeleteEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("remove %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))

		return nil
	})

}

func (dir *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {

	glog.V(3).Infof("%v dir setattr %+v, fh=%d", dir.Path, req, req.Handle)
	if req.Valid.Mode() {
		dir.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		dir.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		dir.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		dir.attributes.Mtime = req.Mtime.Unix()
	}

	parentDir, name := filer2.FullPath(dir.Path).DirAndName()
	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.UpdateEntryRequest{
			Directory: parentDir,
			Entry: &filer_pb.Entry{
				Name:       name,
				Attributes: dir.attributes,
			},
		}

		glog.V(1).Infof("set attr directory entry: %v", request)
		_, err := client.UpdateEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("UpdateEntry %s: %v", dir.Path, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(dir.Path)

		return nil
	})

}

func estimatedCacheTtl(numEntries int) time.Duration {
	if numEntries < 100 {
		// 30 ms per entry
		return 3 * time.Second
	}
	if numEntries < 1000 {
		// 10 ms per entry
		return 10 * time.Second
	}
	if numEntries < 10000 {
		// 10 ms per entry
		return 100 * time.Second
	}

	// 2 ms per entry
	return time.Duration(numEntries*2) * time.Millisecond
}
<commit_msg>avoid error log if not found<commit_after>package filesys

import (
	"context"
	"os"
	"path"
	"path/filepath"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type Dir struct {
	Path       string
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

var _ = fs.Node(&Dir{})
var _ = fs.NodeCreater(&Dir{})
var _ = fs.NodeMkdirer(&Dir{})
var _ = fs.NodeRequestLookuper(&Dir{})
var _ = fs.HandleReadDirAller(&Dir{})
var _ = fs.NodeRemover(&Dir{})
var _ = fs.NodeRenamer(&Dir{})
var _ = fs.NodeSetattrer(&Dir{})

func (dir *Dir) Attr(context context.Context, attr *fuse.Attr) error {

	// https://github.com/bazil/fuse/issues/196
	attr.Valid = time.Second

	if dir.Path == "/" {
		attr.Mode = os.ModeDir | 0777
		return nil
	}

	item := dir.wfs.listDirectoryEntriesCache.Get(dir.Path)
	if item != nil && !item.Expired() {
		entry := item.Value().(*filer_pb.Entry)

		attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		attr.Mode = os.FileMode(entry.Attributes.FileMode)
		attr.Gid = entry.Attributes.Gid
		attr.Uid = entry.Attributes.Uid

		return nil
	}

	parent, name := filepath.Split(dir.Path)

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.LookupDirectoryEntryRequest{
			Directory: parent,
			Name:      name,
		}

		glog.V(1).Infof("read dir %s attr: %v", dir.Path, request)
		resp, err := client.LookupDirectoryEntry(context, request)
		if err != nil {
			if err == filer2.ErrNotFound {
				return nil
			}
			glog.V(0).Infof("read dir %s attr %v: %v", dir.Path, request, err)
			return err
		}

		if resp.Entry != nil {
			dir.attributes = resp.Entry.Attributes
		}

		dir.wfs.listDirectoryEntriesCache.Set(dir.Path, resp.Entry, dir.wfs.option.EntryCacheTtl)

		return nil
	})

	if err != nil {
		return err
	}

	// glog.V(1).Infof("dir %s: %v", dir.Path, attributes)
	// glog.V(1).Infof("dir %s permission: %v", dir.Path, os.FileMode(attributes.FileMode))

	attr.Mode = os.FileMode(dir.attributes.FileMode) | os.ModeDir

	attr.Mtime = time.Unix(dir.attributes.Mtime, 0)
	attr.Ctime = time.Unix(dir.attributes.Crtime, 0)
	attr.Gid = dir.attributes.Gid
	attr.Uid = dir.attributes.Uid

	return nil
}

func (dir *Dir) newFile(name string, entry *filer_pb.Entry) *File {
	return &File{
		Name:  name,
		dir:   dir,
		wfs:   dir.wfs,
		entry: entry,
	}
}

func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {

	request := &filer_pb.CreateEntryRequest{
		Directory: dir.Path,
		Entry: &filer_pb.Entry{
			Name:        req.Name,
			IsDirectory: req.Mode&os.ModeDir > 0,
			Attributes: &filer_pb.FuseAttributes{
				Mtime:       time.Now().Unix(),
				Crtime:      time.Now().Unix(),
				FileMode:    uint32(req.Mode),
				Uid:         req.Uid,
				Gid:         req.Gid,
				Collection:  dir.wfs.option.Collection,
				Replication: dir.wfs.option.Replication,
				TtlSec:      dir.wfs.option.TtlSec,
			},
		},
	}
	glog.V(1).Infof("create: %v", request)

	if request.Entry.IsDirectory {
		if err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
			if _, err := client.CreateEntry(ctx, request); err != nil {
				glog.V(0).Infof("create %s/%s: %v", dir.Path, req.Name, err)
				return fuse.EIO
			}
			return nil
		}); err != nil {
			return nil, nil, err
		}
	}

	file := dir.newFile(req.Name, request.Entry)
	if !request.Entry.IsDirectory {
		file.isOpen = true
	}
	fh := dir.wfs.AcquireHandle(file, req.Uid, req.Gid)
	fh.dirtyMetadata = true
	return file, fh, nil

}

func (dir *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.CreateEntryRequest{
			Directory: dir.Path,
			Entry: &filer_pb.Entry{
				Name:        req.Name,
				IsDirectory: true,
				Attributes: &filer_pb.FuseAttributes{
					Mtime:    time.Now().Unix(),
					Crtime:   time.Now().Unix(),
					FileMode: uint32(req.Mode),
					Uid:      req.Uid,
					Gid:      req.Gid,
				},
			},
		}

		glog.V(1).Infof("mkdir: %v", request)
		if _, err := client.CreateEntry(ctx, request); err != nil {
			glog.V(0).Infof("mkdir %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		return nil
	})

	if err == nil {
		node := &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs}
		return node, nil
	}

	return nil, err
}

func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {

	var entry *filer_pb.Entry

	item := dir.wfs.listDirectoryEntriesCache.Get(path.Join(dir.Path, req.Name))
	if item != nil && !item.Expired() {
		entry = item.Value().(*filer_pb.Entry)
	}

	if entry == nil {
		err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

			request := &filer_pb.LookupDirectoryEntryRequest{
				Directory: dir.Path,
				Name:      req.Name,
			}

			glog.V(4).Infof("lookup directory entry: %v", request)
			resp, err := client.LookupDirectoryEntry(ctx, request)
			if err != nil {
				// glog.V(0).Infof("lookup %s/%s: %v", dir.Path, name, err)
				return fuse.ENOENT
			}

			entry = resp.Entry

			dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, dir.wfs.option.EntryCacheTtl)

			return nil
		})
	}

	if entry != nil {
		if entry.IsDirectory {
			node = &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs, attributes: entry.Attributes}
		} else {
			node = dir.newFile(req.Name, entry)
		}

		resp.EntryValid = time.Duration(0)
		resp.Attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		resp.Attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		resp.Attr.Mode = os.FileMode(entry.Attributes.FileMode)
		resp.Attr.Gid = entry.Attributes.Gid
		resp.Attr.Uid = entry.Attributes.Uid

		return node, nil
	}

	return nil, fuse.ENOENT
}

func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) {

	err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		paginationLimit := 1024
		remaining := dir.wfs.option.DirListingLimit

		lastEntryName := ""

		for remaining >= 0 {

			request := &filer_pb.ListEntriesRequest{
				Directory:         dir.Path,
				StartFromFileName: lastEntryName,
				Limit:             uint32(paginationLimit),
			}

			glog.V(4).Infof("read directory: %v", request)
			resp, err := client.ListEntries(ctx, request)
			if err != nil {
				glog.V(0).Infof("list %s: %v", dir.Path, err)
				return fuse.EIO
			}

			cacheTtl := estimatedCacheTtl(len(resp.Entries))

			for _, entry := range resp.Entries {
				if entry.IsDirectory {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_Dir}
					ret = append(ret, dirent)
				} else {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_File}
					ret = append(ret, dirent)
				}
				dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, cacheTtl)
				lastEntryName = entry.Name
			}

			remaining -= len(resp.Entries)

			if len(resp.Entries) < paginationLimit {
				break
			}

		}

		return nil
	})

	return ret, err
}

func (dir *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {

	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:    dir.Path,
			Name:         req.Name,
			IsDeleteData: true,
		}

		glog.V(1).Infof("remove directory entry: %v", request)
		_, err := client.DeleteEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("remove %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))

		return nil
	})

}

func (dir *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {

	glog.V(3).Infof("%v dir setattr %+v, fh=%d", dir.Path, req, req.Handle)
	if req.Valid.Mode() {
		dir.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		dir.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		dir.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		dir.attributes.Mtime = req.Mtime.Unix()
	}

	parentDir, name := filer2.FullPath(dir.Path).DirAndName()
	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.UpdateEntryRequest{
			Directory: parentDir,
			Entry: &filer_pb.Entry{
				Name:       name,
				Attributes: dir.attributes,
			},
		}

		glog.V(1).Infof("set attr directory entry: %v", request)
		_, err := client.UpdateEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("UpdateEntry %s: %v", dir.Path, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(dir.Path)

		return nil
	})

}

func estimatedCacheTtl(numEntries int) time.Duration {
	if numEntries < 100 {
		// 30 ms per entry
		return 3 * time.Second
	}
	if numEntries < 1000 {
		// 10 ms per entry
		return 10 * time.Second
	}
	if numEntries < 10000 {
		// 10 ms per entry
		return 100 * time.Second
	}

	// 2 ms per entry
	return time.Duration(numEntries*2) * time.Millisecond
}
<|endoftext|>


<commit_before>package commands

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"code.cloudfoundry.org/cli/cf/appfiles"
	"code.cloudfoundry.org/cli/plugin"
	"code.cloudfoundry.org/gofileutils/fileutils"
	. "github.com/cloudfoundry/v3-cli-plugin/models"
	. "github.com/cloudfoundry/v3-cli-plugin/util"
	"github.com/simonleung8/flags"
)

func Push(cliConnection plugin.CliConnection, args []string) {
	appDir := "."
	buildpack := "null"
	dockerImage := ""

	fc := flags.New()
	fc.NewStringFlag("filepath", "p", "path to app dir or zip to upload")
	fc.NewStringFlag("buildpack", "b", "the buildpack to use")
	fc.NewStringFlag("docker-image", "di", "the docker image to use")
	fc.Parse(args...)
	if fc.IsSet("p") {
		appDir = fc.String("p")
	}
	if fc.IsSet("b") {
		buildpack = fmt.Sprintf(`"%s"`, fc.String("b"))
	}
	if fc.IsSet("di") {
		dockerImage = fmt.Sprintf(`"%s"`, fc.String("di"))
	}

	mySpace, _ := cliConnection.GetCurrentSpace()

	lifecycle := ""
	if dockerImage != "" {
		lifecycle = `"lifecycle": { "type": "docker", "data": {} }`
	} else {
		lifecycle = fmt.Sprintf(`"lifecycle": { "type": "buildpack", "data": { "buildpacks": [%s] } }`, buildpack)
	}

	//create the app
	rawOutput, err := cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/apps", "-X", "POST", "-d",
		fmt.Sprintf(`{"name":"%s", "relationships": { "space": { "data": {"guid":"%s"}}}, %s}`, fc.Args()[1], mySpace.Guid, lifecycle))
	FreakOut(err)
	output := strings.Join(rawOutput, "")
	app := V3AppModel{}
	err = json.Unmarshal([]byte(output), &app)
	FreakOut(err)
	if app.Error_Code != "" {
		FreakOut(errors.New("Error creating v3 app: " + app.Error_Code))
	}
	time.Sleep(2 * time.Second) // wait for app to settle before kicking off the log streamer
	go Logs(cliConnection, args)
	time.Sleep(2 * time.Second) // b/c sharing the cliConnection makes things break

	//create package
	pack := V3PackageModel{}
	if dockerImage != "" {
		request := fmt.Sprintf(`{"type": "docker", "data": {"image": %s}, "relationships": {"app":{"guid":"%s"}}}`, dockerImage, app.Guid)
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/packages", "-X", "POST", "-d", request)
		FreakOut(err)
		output = strings.Join(rawOutput, "")

		err = json.Unmarshal([]byte(output), &pack)
		if err != nil {
			FreakOut(errors.New("Error creating v3 app package: " + app.Error_Code))
		}
	} else {
		//create the empty package to upload the app bits to
		request := fmt.Sprintf(`{"type": "bits", "relationships": {"app":{"guid":"%s"}}}`, app.Guid)
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/packages", "-X", "POST", "-d", request)
		FreakOut(err)
		output = strings.Join(rawOutput, "")

		err = json.Unmarshal([]byte(output), &pack)
		if err != nil {
			FreakOut(errors.New("Error creating v3 app package: " + app.Error_Code))
		}

		token, err := cliConnection.AccessToken()
		FreakOut(err)
		api, apiErr := cliConnection.ApiEndpoint()
		FreakOut(apiErr)
		apiString := fmt.Sprintf("%s", api)
		if strings.Index(apiString, "s") == 4 {
			apiString = apiString[:4] + apiString[5:]
		}

		//gather files
		zipper := appfiles.ApplicationZipper{}
		fileutils.TempFile("uploads", func(zipFile *os.File, err error) {
			zipper.Zip(appDir, zipFile)
			_, upload := exec.Command("curl", fmt.Sprintf("%s/v3/packages/%s/upload", apiString, pack.Guid), "-F", fmt.Sprintf("bits=@%s", zipFile.Name()), "-H", fmt.Sprintf("Authorization: %s", token), "-H", "Expect:").Output()
			FreakOut(upload)
		})

		//waiting for cc to pour bits into blobstore
		Poll(cliConnection, fmt.Sprintf("/v3/packages/%s", pack.Guid), "READY", 5*time.Minute, "Package failed to upload")
	}

	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/packages/%s/droplets", pack.Guid), "-X", "POST", "-d", "{}")
	FreakOut(err)
	output = strings.Join(rawOutput, "")
	droplet := V3DropletModel{}
	err = json.Unmarshal([]byte(output), &droplet)
	if err != nil {
		FreakOut(errors.New("error marshaling the v3 droplet: " + err.Error()))
	}
	//wait for the droplet to be ready
	Poll(cliConnection, fmt.Sprintf("/v3/droplets/%s", droplet.Guid), "STAGED", 10*time.Minute, "Droplet failed to stage")

	//assign droplet to the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/apps/%s/droplets/current", app.Guid), "-X", "PUT", "-d", fmt.Sprintf("{\"droplet_guid\":\"%s\"}", droplet.Guid))
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	//pick the first available shared domain, get the guid
	space, _ := cliConnection.GetCurrentSpace()
	nextUrl := "/v2/shared_domains"
	allDomains := DomainsModel{}
	for nextUrl != "" {
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", nextUrl)
		FreakOut(err)
		output = strings.Join(rawOutput, "")
		tmp := DomainsModel{}
		err = json.Unmarshal([]byte(output), &tmp)
		FreakOut(err)
		allDomains.Resources = append(allDomains.Resources, tmp.Resources...)

		if tmp.NextUrl != "" {
			nextUrl = tmp.NextUrl
		} else {
			nextUrl = ""
		}
	}
	domainGuid := allDomains.Resources[0].Metadata.Guid
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "v2/routes", "-X", "POST", "-d", fmt.Sprintf(`{"host":"%s","domain_guid":"%s","space_guid":"%s"}`, fc.Args()[1], domainGuid, space.Guid))
	output = strings.Join(rawOutput, "")

	var routeGuid string
	if strings.Contains(output, "CF-RouteHostTaken") {
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("v2/routes?q=host:%s;domain_guid:%s", fc.Args()[1], domainGuid))
		output = strings.Join(rawOutput, "")
		routes := RoutesModel{}
		err = json.Unmarshal([]byte(output), &routes)
		routeGuid = routes.Routes[0].Metadata.Guid
	} else {
		route := RouteModel{}
		err = json.Unmarshal([]byte(output), &route)
		if err != nil {
			FreakOut(errors.New("error unmarshaling the route: " + err.Error()))
		}
		routeGuid = route.Metadata.Guid
	}

	FreakOut(err)
	route := RouteModel{}
	err = json.Unmarshal([]byte(output), &route)
	if err != nil {
		FreakOut(errors.New("error unmarshaling the route: " + err.Error()))
	}

	//map the route to the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/route_mappings", "-X", "POST", "-d", fmt.Sprintf(`{"relationships": { "route": { "guid": "%s" }, "app": { "guid": "%s" } }`, routeGuid, app.Guid))
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	//start the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/apps/%s/start", app.Guid), "-X", "PUT")
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	fmt.Println("Done pushing! Checkout your processes using 'cf apps'")
}
<commit_msg>Update v3-push with new current droplet structure<commit_after>package commands

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"code.cloudfoundry.org/cli/cf/appfiles"
	"code.cloudfoundry.org/cli/plugin"
	"code.cloudfoundry.org/gofileutils/fileutils"
	. "github.com/cloudfoundry/v3-cli-plugin/models"
	. "github.com/cloudfoundry/v3-cli-plugin/util"
	"github.com/simonleung8/flags"
)

func Push(cliConnection plugin.CliConnection, args []string) {
	appDir := "."
	buildpack := "null"
	dockerImage := ""

	fc := flags.New()
	fc.NewStringFlag("filepath", "p", "path to app dir or zip to upload")
	fc.NewStringFlag("buildpack", "b", "the buildpack to use")
	fc.NewStringFlag("docker-image", "di", "the docker image to use")
	fc.Parse(args...)
	if fc.IsSet("p") {
		appDir = fc.String("p")
	}
	if fc.IsSet("b") {
		buildpack = fmt.Sprintf(`"%s"`, fc.String("b"))
	}
	if fc.IsSet("di") {
		dockerImage = fmt.Sprintf(`"%s"`, fc.String("di"))
	}

	mySpace, _ := cliConnection.GetCurrentSpace()

	lifecycle := ""
	if dockerImage != "" {
		lifecycle = `"lifecycle": { "type": "docker", "data": {} }`
	} else {
		lifecycle = fmt.Sprintf(`"lifecycle": { "type": "buildpack", "data": { "buildpacks": [%s] } }`, buildpack)
	}

	//create the app
	rawOutput, err := cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/apps", "-X", "POST", "-d",
		fmt.Sprintf(`{"name":"%s", "relationships": { "space": { "data": {"guid":"%s"}}}, %s}`, fc.Args()[1], mySpace.Guid, lifecycle))
	FreakOut(err)
	output := strings.Join(rawOutput, "")
	app := V3AppModel{}
	err = json.Unmarshal([]byte(output), &app)
	FreakOut(err)
	if app.Error_Code != "" {
		FreakOut(errors.New("Error creating v3 app: " + app.Error_Code))
	}
	time.Sleep(2 * time.Second) // wait for app to settle before kicking off the log streamer
	go Logs(cliConnection, args)
	time.Sleep(2 * time.Second) // b/c sharing the cliConnection makes things break

	//create package
	pack := V3PackageModel{}
	if dockerImage != "" {
		request := fmt.Sprintf(`{"type": "docker", "data": {"image": %s}, "relationships": {"app": {"data": {"guid": "%s"}}}}`, dockerImage, app.Guid)
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/packages", "-X", "POST", "-d", request)
		FreakOut(err)
		output = strings.Join(rawOutput, "")

		err = json.Unmarshal([]byte(output), &pack)
		if err != nil {
			FreakOut(errors.New("Error creating v3 app package: " + app.Error_Code))
		}
	} else {
		//create the empty package to upload the app bits to
		request := fmt.Sprintf(`{"type": "bits", "relationships": {"app": {"data": {"guid": "%s"}}}}`, app.Guid)
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/packages", "-X", "POST", "-d", request)
		FreakOut(err)
		output = strings.Join(rawOutput, "")

		err = json.Unmarshal([]byte(output), &pack)
		if err != nil {
			FreakOut(errors.New("Error creating v3 app package: " + app.Error_Code))
		}

		token, err := cliConnection.AccessToken()
		FreakOut(err)
		api, apiErr := cliConnection.ApiEndpoint()
		FreakOut(apiErr)
		apiString := fmt.Sprintf("%s", api)
		if strings.Index(apiString, "s") == 4 {
			apiString = apiString[:4] + apiString[5:]
		}

		//gather files
		zipper := appfiles.ApplicationZipper{}
		fileutils.TempFile("uploads", func(zipFile *os.File, err error) {
			zipper.Zip(appDir, zipFile)
			_, upload := exec.Command("curl", fmt.Sprintf("%s/v3/packages/%s/upload", apiString, pack.Guid), "-F", fmt.Sprintf("bits=@%s", zipFile.Name()), "-H", fmt.Sprintf("Authorization: %s", token), "-H", "Expect:").Output()
			FreakOut(upload)
		})

		//waiting for cc to pour bits into blobstore
		Poll(cliConnection, fmt.Sprintf("/v3/packages/%s", pack.Guid), "READY", 5*time.Minute, "Package failed to upload")
	}

	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/packages/%s/droplets", pack.Guid), "-X", "POST", "-d", "{}")
	FreakOut(err)
	output = strings.Join(rawOutput, "")
	droplet := V3DropletModel{}
	err = json.Unmarshal([]byte(output), &droplet)
	if err != nil {
		FreakOut(errors.New("error marshaling the v3 droplet: " + err.Error()))
	}
	//wait for the droplet to be ready
	Poll(cliConnection, fmt.Sprintf("/v3/droplets/%s", droplet.Guid), "STAGED", 10*time.Minute, "Droplet failed to stage")

	//assign droplet to the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/apps/%s/relationships/current_droplet", app.Guid), "-X", "PATCH", "-d", fmt.Sprintf("{\"data\": {\"guid\":\"%s\"}}", droplet.Guid))
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	//pick the first available shared domain, get the guid
	space, _ := cliConnection.GetCurrentSpace()
	nextUrl := "/v2/shared_domains"
	allDomains := DomainsModel{}
	for nextUrl != "" {
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", nextUrl)
		FreakOut(err)
		output = strings.Join(rawOutput, "")
		tmp := DomainsModel{}
		err = json.Unmarshal([]byte(output), &tmp)
		FreakOut(err)
		allDomains.Resources = append(allDomains.Resources, tmp.Resources...)

		if tmp.NextUrl != "" {
			nextUrl = tmp.NextUrl
		} else {
			nextUrl = ""
		}
	}
	domainGuid := allDomains.Resources[0].Metadata.Guid
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "v2/routes", "-X", "POST", "-d", fmt.Sprintf(`{"host":"%s","domain_guid":"%s","space_guid":"%s"}`, fc.Args()[1], domainGuid, space.Guid))
	output = strings.Join(rawOutput, "")

	var routeGuid string
	if strings.Contains(output, "CF-RouteHostTaken") {
		rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("v2/routes?q=host:%s;domain_guid:%s", fc.Args()[1], domainGuid))
		output = strings.Join(rawOutput, "")
		routes := RoutesModel{}
		err = json.Unmarshal([]byte(output), &routes)
		routeGuid = routes.Routes[0].Metadata.Guid
	} else {
		route := RouteModel{}
		err = json.Unmarshal([]byte(output), &route)
		if err != nil {
			FreakOut(errors.New("error unmarshaling the route: " + err.Error()))
		}
		routeGuid = route.Metadata.Guid
	}

	FreakOut(err)
	route := RouteModel{}
	err = json.Unmarshal([]byte(output), &route)
	if err != nil {
		FreakOut(errors.New("error unmarshaling the route: " + err.Error()))
	}

	//map the route to the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", "/v3/route_mappings", "-X", "POST", "-d", fmt.Sprintf(`{"relationships": { "route": { "guid": "%s" }, "app": { "guid": "%s" } }`, routeGuid, app.Guid))
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	//start the app
	rawOutput, err = cliConnection.CliCommandWithoutTerminalOutput("curl", fmt.Sprintf("/v3/apps/%s/start", app.Guid), "-X", "PUT")
	FreakOut(err)
	output = strings.Join(rawOutput, "")

	fmt.Println("Done pushing! Checkout your processes using 'cf apps'")
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"net/url"

	"github.com/codegangsta/cli"

	"github.com/yuuki1/droot/aws"
	"github.com/yuuki1/droot/docker"
	"github.com/yuuki1/droot/log"
	"github.com/yuuki1/droot/osutil"
)

var CommandArgPush = "--to S3_ENDPOINT DOCKER_REPOSITORY[:TAG]"
var CommandPush = cli.Command{
	Name:  "push",
	Usage: "Push an extracted docker image into s3",
	Action: fatalOnError(doPush),
	Flags: []cli.Flag{
		cli.StringFlag{Name: "to, t", Usage: "Amazon S3 endpoint (ex. s3://example.com/containers/app.tar.gz)"},
	},
}

func doPush(c *cli.Context) error {
	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, "push")
		return errors.New("docker repository required")
	}
	repository := c.Args().Get(0)
	to := c.String("to")
	if to == "" || repository == "" {
		cli.ShowCommandHelp(c, "push")
		return errors.New("docker repository required")
	}

	s3Url, err := url.Parse(to)
	if err != nil {
		return err
	}
	if s3Url.Scheme != "s3" {
		return fmt.Errorf("Not s3 scheme %s", to)
	}

	log.Info("export", repository)
	imageReader, err := docker.ExportImage(repository)
	if err != nil {
		return err
	}

	gzipReader := osutil.Compress(imageReader)

	log.Info("s3 uploading to", to)
	location, err := aws.NewS3Client().Upload(s3Url, gzipReader)
	if err != nil {
		return err
	}
	log.Info("uploaded", location)

	return nil
}
<commit_msg>Add pipe close<commit_after>package commands

import (
	"errors"
	"fmt"
	"net/url"

	"github.com/codegangsta/cli"

	"github.com/yuuki1/droot/aws"
	"github.com/yuuki1/droot/docker"
	"github.com/yuuki1/droot/log"
	"github.com/yuuki1/droot/osutil"
)

var CommandArgPush = "--to S3_ENDPOINT DOCKER_REPOSITORY[:TAG]"
var CommandPush = cli.Command{
	Name:  "push",
	Usage: "Push an extracted docker image into s3",
	Action: fatalOnError(doPush),
	Flags: []cli.Flag{
		cli.StringFlag{Name: "to, t", Usage: "Amazon S3 endpoint (ex. s3://example.com/containers/app.tar.gz)"},
	},
}

func doPush(c *cli.Context) error {
	if len(c.Args()) < 1 {
		cli.ShowCommandHelp(c, "push")
		return errors.New("docker repository required")
	}
	repository := c.Args().Get(0)
	to := c.String("to")
	if to == "" || repository == "" {
		cli.ShowCommandHelp(c, "push")
		return errors.New("docker repository required")
	}

	s3Url, err := url.Parse(to)
	if err != nil {
		return err
	}
	if s3Url.Scheme != "s3" {
		return fmt.Errorf("Not s3 scheme %s", to)
	}

	log.Info("export", repository)
	imageReader, err := docker.ExportImage(repository)
	if err != nil {
		return err
	}
	defer imageReader.Close()

	gzipReader := osutil.Compress(imageReader)
	defer gzipReader.Close()

	log.Info("s3 uploading to", to)
	location, err := aws.NewS3Client().Upload(s3Url, gzipReader)
	if err != nil {
		return err
	}
	log.Info("uploaded", location)

	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"runtime"
	"strconv"

	"gopkg.in/cheggaaa/pb.v1"

	"github.com/inconshreveable/go-update"

	"github.com/concourse/fly/commands/internal/displayhelpers"
	"github.com/concourse/fly/rc"
)

type SyncCommand struct{}

func (command *SyncCommand) Execute(args []string) error {
	client, err := rc.TargetClient(Fly.Target)
	if err != nil {
		return err
	}

	body, headers, err := client.GetCLIReader(runtime.GOARCH, runtime.GOOS)
	if err != nil {
		return err
	}

	fmt.Printf("downloading fly from %s... \n", client.URL())

	filesSize, _ := strconv.ParseInt(headers.Get("Content-Length"), 10, 64)
	progressBar := pb.New64(filesSize).SetUnits(pb.U_BYTES)
	progressBar.SetWidth(100)
	progressBar.Start()
	r := body
	reader := progressBar.NewProxyReader(r)

	err = update.Apply(reader, update.Options{})
	if err != nil {
		displayhelpers.Failf("update failed: %s", err)
	}

	progressBar.FinishPrint("update successful!")
	return nil
}
<commit_msg>set progress bar the size of the window<commit_after>package commands

import (
	"fmt"
	"runtime"
	"strconv"

	"gopkg.in/cheggaaa/pb.v1"

	"github.com/inconshreveable/go-update"

	"github.com/concourse/fly/commands/internal/displayhelpers"
	"github.com/concourse/fly/rc"
)

type SyncCommand struct{}

func (command *SyncCommand) Execute(args []string) error {
	client, err := rc.TargetClient(Fly.Target)
	if err != nil {
		return err
	}

	body, headers, err := client.GetCLIReader(runtime.GOARCH, runtime.GOOS)
	if err != nil {
		return err
	}

	fmt.Printf("downloading fly from %s... \n", client.URL())

	filesSize, _ := strconv.ParseInt(headers.Get("Content-Length"), 10, 64)
	progressBar := pb.New64(filesSize).SetUnits(pb.U_BYTES)
	progressBar.Start()
	defer progressBar.FinishPrint("update successful!")
	r := body
	reader := progressBar.NewProxyReader(r)

	err = update.Apply(reader, update.Options{})
	if err != nil {
		displayhelpers.Failf("update failed: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEncryptDecryptCommand(t *testing.T) {
	input := bytes.NewBufferString("secret")
	var encrypted, output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")
	encryption := newKeyEncryptionStrategy(masterPublicKey, configPrivateKey)

	encryptCommand(input, &encrypted, encryption, false)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptStream(&encrypted, &output, crypto)

	assert.Equal(t, "secret", output.String())
}

func TestEncryptDecryptCommandSubstrings(t *testing.T) {
	input := bytes.NewBufferString("secret")
	input2 := bytes.NewBufferString("secret2")
	var encrypted, output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")
	encryption := newKeyEncryptionStrategy(masterPublicKey, configPrivateKey)

	encryptCommand(input, &encrypted, encryption, false)
	encrypted.Write([]byte("somepadding"))
	encryptCommand(input2, &encrypted, encryption, false)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptStream(&encrypted, &output, crypto)

	assert.Equal(t, "secretsomepaddingsecret2", output.String())
}

func TestDecryptEnvironmentCommand(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	{
		input := []string{"a=b", fmt.Sprintf("b=%s", encrypted), "c=d", fmt.Sprintf("e=%s", encrypted2), "e.f=d"}

		crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
		ok, err := decryptEnvironment(input, &output, crypto)

		assert.True(t, ok)
		assert.Nil(t, err)
		assert.Equal(t, "export b='secret'\nexport e='secret2'\n", output.String())
	}

	{
		input := []string{"a.b=b", fmt.Sprintf("b.c=%s", encrypted)}

		crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
		ok, err := decryptEnvironment(input, &output, crypto)

		assert.False(t, ok)
		assert.Equal(t, "the env var 'b.c' is not a valid shell script identifier. Only alphanumeric characters and underscores are supported, starting with an alphabetic or underscore character", err.Error())
	}
}

func TestDecryptEnvironmentCommandSubstrings(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	input := []string{"a=b", fmt.Sprintf("b=blabla%sblabla%s", encrypted, encrypted2), "c=d"}

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptEnvironment(input, &output, crypto)

	assert.Equal(t, "export b='blablasecretblablasecret2'\n", output.String())
}

func TestDecryptEnvironmentCommandSubstringsSpaces(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	input := []string{"a=b", fmt.Sprintf("b=blabla %sb la bla %s", encrypted, encrypted2), "c=d"}

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptEnvironment(input, &output, crypto)

	assert.Equal(t, "export b='blabla secretb la bla secret2'\n", output.String())
}

func TestCreateExecArgs(t *testing.T) {
	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("Mellon"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("hunter2"))
	assert.Nil(t, err)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)

	cmd, argv, environ, err := createExecArgs(
		[]string{"/bin/echo", encrypted},
		[]string{"SECRET=" + encrypted2},
		crypto,
	)
	assert.Nil(t, err)
	assert.Equal(t, cmd, "/bin/echo")
	assert.Equal(t, []string{"echo", "Mellon"}, argv)
	assert.Equal(t, []string{"SECRET=hunter2"}, environ)
}
<commit_msg>Add more testcases for exec command<commit_after>package main

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEncryptDecryptCommand(t *testing.T) {
	input := bytes.NewBufferString("secret")
	var encrypted, output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")
	encryption := newKeyEncryptionStrategy(masterPublicKey, configPrivateKey)

	encryptCommand(input, &encrypted, encryption, false)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptStream(&encrypted, &output, crypto)

	assert.Equal(t, "secret", output.String())
}

func TestEncryptDecryptCommandSubstrings(t *testing.T) {
	input := bytes.NewBufferString("secret")
	input2 := bytes.NewBufferString("secret2")
	var encrypted, output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")
	encryption := newKeyEncryptionStrategy(masterPublicKey, configPrivateKey)

	encryptCommand(input, &encrypted, encryption, false)
	encrypted.Write([]byte("somepadding"))
	encryptCommand(input2, &encrypted, encryption, false)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptStream(&encrypted, &output, crypto)

	assert.Equal(t, "secretsomepaddingsecret2", output.String())
}

func TestDecryptEnvironmentCommand(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	{
		input := []string{"a=b", fmt.Sprintf("b=%s", encrypted), "c=d", fmt.Sprintf("e=%s", encrypted2), "e.f=d"}

		crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
		ok, err := decryptEnvironment(input, &output, crypto)

		assert.True(t, ok)
		assert.Nil(t, err)
		assert.Equal(t, "export b='secret'\nexport e='secret2'\n", output.String())
	}

	{
		input := []string{"a.b=b", fmt.Sprintf("b.c=%s", encrypted)}

		crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
		ok, err := decryptEnvironment(input, &output, crypto)

		assert.False(t, ok)
		assert.Equal(t, "the env var 'b.c' is not a valid shell script identifier. Only alphanumeric characters and underscores are supported, starting with an alphabetic or underscore character", err.Error())
	}
}

func TestDecryptEnvironmentCommandSubstrings(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	input := []string{"a=b", fmt.Sprintf("b=blabla%sblabla%s", encrypted, encrypted2), "c=d"}

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptEnvironment(input, &output, crypto)

	assert.Equal(t, "export b='blablasecretblablasecret2'\n", output.String())
}

func TestDecryptEnvironmentCommandSubstringsSpaces(t *testing.T) {
	var output bytes.Buffer

	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("secret2"))
	assert.Nil(t, err)

	input := []string{"a=b", fmt.Sprintf("b=blabla %sb la bla %s", encrypted, encrypted2), "c=d"}

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)
	decryptEnvironment(input, &output, crypto)

	assert.Equal(t, "export b='blabla secretb la bla secret2'\n", output.String())
}

func TestCreateExecArgs(t *testing.T) {
	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("Mellon"))
	assert.Nil(t, err)

	encrypted2, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("hunter2"))
	assert.Nil(t, err)

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)

	cmd, argv, environ, err := createExecArgs(
		[]string{"/bin/echo", encrypted},
		[]string{"SECRET=" + encrypted2},
		crypto,
	)
	assert.Nil(t, err)
	assert.Equal(t, cmd, "/bin/echo")
	assert.Equal(t, []string{"echo", "Mellon"}, argv)
	assert.Equal(t, []string{"SECRET=hunter2"}, environ)
}

func TestCreateExecArgsInvalidEnvelope(t *testing.T) {
	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	//	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	//	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)

	_, _, _, err := createExecArgs(
		[]string{"/bin/echo", "ENC[NACL,invalidenvelope]"},
		[]string{},
		crypto,
	)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "Error while decrypting argument")
}

func TestCreateExecArgsInvalidEnvelopeInEnvironment(t *testing.T) {
	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	//	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	//	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	crypto := newKeyDecryptionStrategy(configPublicKey, masterPrivateKey)

	_, _, _, err := createExecArgs(
		[]string{"/bin/echo"},
		[]string{"ENC[NACL,invalidenvelope]"},
		crypto,
	)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "Error while decrypting environment")
}

func TestCreateExecArgsInvalidDecryptionKey(t *testing.T) {
	configPublicKey := pemRead("./resources/test/keys/config-public-key.pem")
	configPrivateKey := pemRead("./resources/test/keys/config-private-key.pem")
	masterPublicKey := pemRead("./resources/test/keys/master-public-key.pem")
	//masterPrivateKey := pemRead("./resources/test/keys/master-private-key.pem")

	encrypted, err := encryptEnvelope(masterPublicKey, configPrivateKey, []byte("Mellon"))
	assert.Nil(t, err)

	// NB: Erroneously using configPrivateKey here
	crypto := newKeyDecryptionStrategy(configPublicKey, configPrivateKey)

	_, _, _, err = createExecArgs(
		[]string{"/bin/echo", encrypted},
		[]string{},
		crypto,
	)

	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "Error while decrypting argument")
	assert.Contains(t, err.Error(), "incorrect keys?")
}
<|endoftext|>


<commit_before>// ***************************************************************************
//
//  Copyright 2017 David (Dizzy) Smith, dizzyd@dizzyd.com
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
// ***************************************************************************

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"archive/zip"

	"bytes"

	"strings"

	"encoding/binary"

	"github.com/Jeffail/gabs"
	"github.com/xi2/xz"
)

func forgeIDStr(minecraftVsn, forgeVsn string) string {
	return minecraftVsn + "-forge-" + forgeVsn
}

func isForgeInstalled(minecraftVsn, forgeVsn string) bool {
	forgeDir := filepath.Join(env().MinecraftDir, "versions", forgeIDStr(minecraftVsn, forgeVsn))
	return dirExists(forgeDir)
}

func installServerForge(minecraftVsn, forgeVsn, targetDir string) (string, error) {
	// Construct the download URL
	forgeURL := fmt.Sprintf("http://files.minecraftforge.net/maven/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar",
		minecraftVsn, forgeVsn, minecraftVsn, forgeVsn)

	fmt.Printf("Downloading Forge %s\n", forgeVsn)

	// Download the Forge installer (into memory)
	resp, err := HttpGet(forgeURL)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}
	defer resp.Body.Close()

	installerBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}

	// Extract the install_profile.json from ZIP containing forge
	installProfile, forgeJar, err := extractInstaller(installerBytes)
	if err != nil {
		return "", err
	}

	// Fix up the versionInfo.id in the profile to use the correct ID
	// (Forge uses a weird repeating version by default)
	forgeID := forgeIDStr(minecraftVsn, forgeVsn)
	installProfile.SetP(forgeID, "versionInfo.id")

	// Write the forge JAR into a file in the directory
	jarFilename := filepath.Join(targetDir, fmt.Sprintf("forge-%s-%s.jar", minecraftVsn, forgeVsn))
	err = writeStream(jarFilename, forgeJar)
	if err != nil {
		return "", fmt.Errorf("failed to write %s: %+v", jarFilename, err)
	}

	// Install libraries
	err = installForgeLibraries(installProfile.Path("versionInfo"), targetDir)
	if err != nil {
		fmt.Printf("Installing forge libraries failed %s: %+v\n", forgeID, err)
		return "", err
	}

	return forgeID, nil
}

func installClientForge(minecraftVsn, forgeVsn string) (string, error) {
	// If this version of forge is already installed, exit early
	if isForgeInstalled(minecraftVsn, forgeVsn) {
		fmt.Printf("Forge %s already available.\n", forgeVsn)
		return forgeIDStr(minecraftVsn, forgeVsn), nil
	}

	// Construct the download URL
	forgeURL := fmt.Sprintf("http://files.minecraftforge.net/maven/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar",
		minecraftVsn, forgeVsn, minecraftVsn, forgeVsn)

	fmt.Printf("Downloading Forge %s\n", forgeVsn)

	// Download the Forge installer (into memory)
	resp, err := HttpGet(forgeURL)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}
	defer resp.Body.Close()

	installerBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}

	// Extract the install_profile.json from ZIP containing forge
	installProfile, forgeJar, err := extractInstaller(installerBytes)
	if err != nil {
		return "", err
	}

	// Fix up the versionInfo.id in the profile to use the correct ID
	// (Forge uses a weird repeating version by default)
	forgeID := forgeIDStr(minecraftVsn, forgeVsn)
	installProfile.SetP(forgeID, "versionInfo.id")

	// Create the versions/ registry directory
	forgeDir := filepath.Join(env().MinecraftDir, "versions", forgeID)
	err = os.MkdirAll(forgeDir, 0700)
	if err != nil {
		return "", fmt.Errorf("failed to create dir %s: %+v", forgeDir, err)
	}

	// Extract the versionInfo section and write it to disk
	versionInfoBytes := []byte(installProfile.Path("versionInfo").StringIndent("", "  "))
	err = ioutil.WriteFile(filepath.Join(forgeDir, forgeID+".json"), versionInfoBytes, 0644)
	if err != nil {
		return "", fmt.Errorf("failed to write %s/%s.json: %+v", forgeDir, forgeID, err)
	}

	// Create the directory in which to install the forgeJar
	forgeJarID := fmt.Sprintf("%s-%s", minecraftVsn, forgeVsn)
	forgeJarDir := filepath.Join(env().MinecraftDir, "libraries", "net", "minecraftforge", "forge", forgeJarID)
	err = os.MkdirAll(forgeJarDir, 0700)
	if err != nil {
		return "", fmt.Errorf("failed to create dir %s: %+v", forgeJarDir, err)
	}

	// Write the forge JAR into a file in the directory
	jarFilename := filepath.Join(forgeJarDir, fmt.Sprintf("forge-%s.jar", forgeJarID))
	err = writeStream(jarFilename, forgeJar)
	if err != nil {
		return "", fmt.Errorf("failed to write %s: %+v", jarFilename, err)
	}

	// Install libraries
	err = installForgeLibraries(installProfile.Path("versionInfo"), env().MinecraftDir)
	if err != nil {
		fmt.Printf("Installing forge libraries failed %s: %+v\n", forgeID, err)
		return "", err
	}

	return forgeID, nil
}

func extractInstaller(forgeInstallerBytes []byte) (*gabs.Container, io.Reader, error) {
	// Open the installer byte stream as a ZIP file
	installerReader := bytes.NewReader(forgeInstallerBytes)
	installerReaderSz := int64(len(forgeInstallerBytes))
	file, err := zip.NewReader(installerReader, installerReaderSz)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to read Forge installer: %+v", err)
	}

	var installProfile *gabs.Container
	var forgeJar io.ReadCloser

	// Look for the necessary files
	for _, f := range file.File {
		if f.Name == "install_profile.json" {
			// Parse the JSON for install_profile.json
			installProfile, err = zipEntryToJSON("install_profile.json", f)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to parse install_profile.json: %+v", err)
			}
		} else if strings.HasSuffix(f.Name, "-universal.jar") {
			forgeJar, err = f.Open()
			if err != nil {
				return nil, nil, fmt.Errorf("failed to read %s: %+v", f.Name, err)
			}
		}
	}

	if installProfile == nil || forgeJar == nil {
		return nil, nil, fmt.Errorf("failed to find install_profile.json or *-universal.jar")
	}

	return installProfile, forgeJar, nil
}

func installForgeLibraries(versionInfo *gabs.Container, targetDir string) error {
	libs, _ := versionInfo.Path("libraries").Children()
	for _, lib := range libs {
		err := installForgeLibrary(lib, targetDir)
		if err != nil {
			return fmt.Errorf("%s: %+v", lib, err)
		}
	}

	return nil
}

func getFlag(obj *gabs.Container, flag string) bool {
	fdata := obj.S(flag).Data()
	fval, ok := fdata.(bool)
	if !ok {
		return false
	}
	return fval
}

func downloadXzPack(url, filename string) error {
	dir := filepath.Dir(filename)
	filename = filepath.Base(filename)

	// Construct the URL to download
	finalURL := fmt.Sprintf("%s.pack.xz", url)
	resp, err := HttpGet(finalURL)
	if err != nil {
		return fmt.Errorf("failed to download %s: %+v", finalURL, err)
	}
	defer resp.Body.Close()

	// If we got anything other than 200, bail
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to download %s: unexpected HTTP response %d", finalURL, resp.StatusCode)
	}

	// Open a XZ decompressor
	xzResponse, err := xz.NewReader(resp.Body, 0)
	if err != nil {
		return fmt.Errorf("failed to download %s: unexpected xz error: %+v", finalURL, err)
	}

	// Stream the whole decompressed response into memory; we need to strip off the oddball
	// signatures before we invoke unpack200 to convert it into a JAR file
	var packDataBuf bytes.Buffer
	packSz, err := packDataBuf.ReadFrom(xzResponse)
	if err != nil {
		return fmt.Errorf("failed to decompress %s: %+v", finalURL, err)
	}

	// Grab the raw bytes to the data for munging purposes
	packData := packDataBuf.Bytes()

	// Get the signature length
	sigLen, err := signatureLen(packData)
	if err != nil {
		return fmt.Errorf("failed to strip signatures: %+v", err)
	}

	// Create the directory
	err = os.MkdirAll(dir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create lib directory %s: %+v", dir, err)
	}

	// Write the packData (minus the signature) to disk
	err = writeStream(filepath.Join(dir, "tmp.pack"), bytes.NewReader(packData[0:packSz-sigLen]))
	if err != nil {
		fmt.Printf("failed to write %s: %+v", dir, err)
	}

	// Invoke unpack200 on tmp.pack and output to the appropriate JAR name
	err = invokeUnpack200(dir, filename)
	if err != nil {
		return err
	}
	return nil
}

func downloadJar(url, filename string) error {
	dir := filepath.Dir(filename)
	filename = filepath.Base(filename)

	// Construct the URL to download
	resp, err := HttpGet(url)
	if err != nil {
		return fmt.Errorf("failed to download %s: %+v", url, err)
	}
	defer resp.Body.Close()

	// If we got anything other than 200, bail
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to download %s: unexpected HTTP response %d", url, resp.StatusCode)
	}

	// Create the directory
	err = os.MkdirAll(dir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create lib directory %s: %+v", dir, err)
	}

	// Save the stream to disk
	err = writeStream(filepath.Join(dir, filename), resp.Body)
	if err != nil {
		fmt.Printf("failed to write %s: %+v", dir, err)
	}
	return nil
}

func installForgeLibrary(library *gabs.Container, targetDir string) error {
	// TODO: Add support for handling server-side libraries
	var isClientLib = getFlag(library, "clientreq")
	var isServerLib = getFlag(library, "serverreq")

	// Extract key parts of library name
	name := library.Path("name").Data().(string)
	url, ok := library.Path("url").Data().(string)
	if !ok {
		url = "https://libraries.minecraft.net"
	}

	if !isClientLib && !isServerLib {
		fmt.Printf("Skipping %s - not client or server req!\n", name)
		return nil
	}

	fmt.Printf("Installing %s: %s\n", name, url)

	// Unpack the name into maven standard: groupId, artifactId and version
	parts := strings.SplitN(name, ":", 3)
	groupID := parts[0]
	artifactID := parts[1]
	vsn := parts[2]

	// Replace all periods in groupId with path delimiters
	groupID = strings.Replace(groupID, ".", "/", -1)

	// Construct the libDir and libName; if the file already exists, bail
	libName := fmt.Sprintf("%s-%s.jar", artifactID, vsn)
	libDir := filepath.Join(targetDir, "libraries", groupID, artifactID, vsn)
	if fileExists(filepath.Join(libDir, libName)) {
		return nil
	}

	// Construct the URL to download
	finalURL := fmt.Sprintf("%s/%s/%s/%s/%s", url, groupID, artifactID, vsn, libName)
	err := downloadXzPack(finalURL, filepath.Join(libDir, libName))
	if err != nil {
		err = downloadJar(finalURL, filepath.Join(libDir, libName))
		if err != nil {
			return err
		}
	}

	return nil
}

func signatureLen(data []byte) (int64, error) {
	dataSz := len(data)
	if string(data[dataSz-4:dataSz]) != "SIGN" {
		return 0, fmt.Errorf("invalid signature bytes")
	}

	var sigLen uint32
	err := binary.Read(bytes.NewReader(data[dataSz-8:dataSz-4]), binary.LittleEndian, &sigLen)
	if err != nil {
		return 0, fmt.Errorf("invalid signature len: %+v", err)
	}

	return int64(sigLen + 8), nil
}

func invokeUnpack200(libDir, libName string) error {
	err := exec.Command(unpack200Cmd(), "-r",
		filepath.Join(libDir, "tmp.pack"),
		filepath.Join(libDir, libName)).Run()
	if err != nil {
		return fmt.Errorf("failed to run unpack200 on %s: %+v", libName, err)
	}
	return nil
}
<commit_msg>Add special logic for bjorked forge versions; improve HTTP error handling<commit_after>// ***************************************************************************
//
//  Copyright 2017 David (Dizzy) Smith, dizzyd@dizzyd.com
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
// ***************************************************************************

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"archive/zip"

	"bytes"

	"strings"

	"encoding/binary"

	"github.com/Jeffail/gabs"
	"github.com/xi2/xz"
)

func forgeIDStr(minecraftVsn, forgeVsn string) string {
	return minecraftVsn + "-forge-" + forgeVsn
}

func isForgeInstalled(minecraftVsn, forgeVsn string) bool {
	forgeDir := filepath.Join(env().MinecraftDir, "versions", forgeIDStr(minecraftVsn, forgeVsn))
	return dirExists(forgeDir)
}

func installServerForge(minecraftVsn, forgeVsn, targetDir string) (string, error) {
	// Construct the download URL
	forgeURL := fmt.Sprintf("http://files.minecraftforge.net/maven/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar",
		minecraftVsn, forgeVsn, minecraftVsn, forgeVsn)

	fmt.Printf("Downloading Forge %s\n", forgeVsn)

	// Download the Forge installer (into memory)
	resp, err := HttpGet(forgeURL)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}
	defer resp.Body.Close()

	installerBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}

	// Extract the install_profile.json from ZIP containing forge
	installProfile, forgeJar, err := extractInstaller(installerBytes)
	if err != nil {
		return "", err
	}

	// Fix up the versionInfo.id in the profile to use the correct ID
	// (Forge uses a weird repeating version by default)
	forgeID := forgeIDStr(minecraftVsn, forgeVsn)
	installProfile.SetP(forgeID, "versionInfo.id")

	// Write the forge JAR into a file in the directory
	jarFilename := filepath.Join(targetDir, fmt.Sprintf("forge-%s-%s.jar", minecraftVsn, forgeVsn))
	err = writeStream(jarFilename, forgeJar)
	if err != nil {
		return "", fmt.Errorf("failed to write %s: %+v", jarFilename, err)
	}

	// Install libraries
	err = installForgeLibraries(installProfile.Path("versionInfo"), targetDir)
	if err != nil {
		fmt.Printf("Installing forge libraries failed %s: %+v\n", forgeID, err)
		return "", err
	}

	return forgeID, nil
}

func installClientForge(minecraftVsn, forgeVsn string) (string, error) {
	// If this version of forge is already installed, exit early
	if isForgeInstalled(minecraftVsn, forgeVsn) {
		fmt.Printf("Forge %s already available.\n", forgeVsn)
		return forgeIDStr(minecraftVsn, forgeVsn), nil
	}

	// Choose the right format for the download URL; some older versions
	// of Forge are a tad inconsistent
	var forgeURL string
	switch minecraftVsn {
	case "1.7.10":
		forgeURL = fmt.Sprintf("http://files.minecraftforge.net/maven/net/minecraftforge/forge/%s-%s-%s/forge-%s-%s-%s-installer.jar",
			minecraftVsn, forgeVsn, minecraftVsn, minecraftVsn, forgeVsn, minecraftVsn)
	default:
		forgeURL = fmt.Sprintf("http://files.minecraftforge.net/maven/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar",
			minecraftVsn, forgeVsn, minecraftVsn, forgeVsn)
	}

	// Construct the download URL
	fmt.Printf("Downloading Forge %s\n", forgeVsn)

	// Download the Forge installer (into memory)
	resp, err := HttpGet(forgeURL)
	if err != nil {
		return "", fmt.Errorf("download failed: %+v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("HTTP error %d", resp.StatusCode)
	}

	installerBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to download Forge %s: %+v", forgeVsn, err)
	}

	// Extract the install_profile.json from ZIP containing forge
	installProfile, forgeJar, err := extractInstaller(installerBytes)
	if err != nil {
		return "", err
	}

	// Fix up the versionInfo.id in the profile to use the correct ID
	// (Forge uses a weird repeating version by default)
	forgeID := forgeIDStr(minecraftVsn, forgeVsn)
	installProfile.SetP(forgeID, "versionInfo.id")

	// Create the versions/ registry directory
	forgeDir := filepath.Join(env().MinecraftDir, "versions", forgeID)
	err = os.MkdirAll(forgeDir, 0700)
	if err != nil {
		return "", fmt.Errorf("failed to create dir %s: %+v", forgeDir, err)
	}

	// Extract the versionInfo section and write it to disk
	versionInfoBytes := []byte(installProfile.Path("versionInfo").StringIndent("", "  "))
	err = ioutil.WriteFile(filepath.Join(forgeDir, forgeID+".json"), versionInfoBytes, 0644)
	if err != nil {
		return "", fmt.Errorf("failed to write %s/%s.json: %+v", forgeDir, forgeID, err)
	}

	// Create the directory in which to install the forgeJar
	forgeJarID := fmt.Sprintf("%s-%s", minecraftVsn, forgeVsn)
	forgeJarDir := filepath.Join(env().MinecraftDir, "libraries", "net", "minecraftforge", "forge", forgeJarID)
	err = os.MkdirAll(forgeJarDir, 0700)
	if err != nil {
		return "", fmt.Errorf("failed to create dir %s: %+v", forgeJarDir, err)
	}

	// Write the forge JAR into a file in the directory
	jarFilename := filepath.Join(forgeJarDir, fmt.Sprintf("forge-%s.jar", forgeJarID))
	err = writeStream(jarFilename, forgeJar)
	if err != nil {
		return "", fmt.Errorf("failed to write %s: %+v", jarFilename, err)
	}

	// Install libraries
	err = installForgeLibraries(installProfile.Path("versionInfo"), env().MinecraftDir)
	if err != nil {
		fmt.Printf("Installing forge libraries failed %s: %+v\n", forgeID, err)
		return "", err
	}

	return forgeID, nil
}

func extractInstaller(forgeInstallerBytes []byte) (*gabs.Container, io.Reader, error) {
	// Open the installer byte stream as a ZIP file
	installerReader := bytes.NewReader(forgeInstallerBytes)
	installerReaderSz := int64(len(forgeInstallerBytes))
	file, err := zip.NewReader(installerReader, installerReaderSz)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to read Forge installer: %+v", err)
	}

	var installProfile *gabs.Container
	var forgeJar io.ReadCloser

	// Look for the necessary files
	for _, f := range file.File {
		if f.Name == "install_profile.json" {
			// Parse the JSON for install_profile.json
			installProfile, err = zipEntryToJSON("install_profile.json", f)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to parse install_profile.json: %+v", err)
			}
		} else if strings.HasSuffix(f.Name, "-universal.jar") {
			forgeJar, err = f.Open()
			if err != nil {
				return nil, nil, fmt.Errorf("failed to read %s: %+v", f.Name, err)
			}
		}
	}

	if installProfile == nil || forgeJar == nil {
		return nil, nil, fmt.Errorf("failed to find install_profile.json or *-universal.jar")
	}

	return installProfile, forgeJar, nil
}

func installForgeLibraries(versionInfo *gabs.Container, targetDir string) error {
	libs, _ := versionInfo.Path("libraries").Children()
	for _, lib := range libs {
		err := installForgeLibrary(lib, targetDir)
		if err != nil {
			return fmt.Errorf("%s: %+v", lib, err)
		}
	}

	return nil
}

func getFlag(obj *gabs.Container, flag string) bool {
	fdata := obj.S(flag).Data()
	fval, ok := fdata.(bool)
	if !ok {
		return false
	}
	return fval
}

func downloadXzPack(url, filename string) error {
	dir := filepath.Dir(filename)
	filename = filepath.Base(filename)

	// Construct the URL to download
	finalURL := fmt.Sprintf("%s.pack.xz", url)
	resp, err := HttpGet(finalURL)
	if err != nil {
		return fmt.Errorf("failed to download %s: %+v", finalURL, err)
	}
	defer resp.Body.Close()

	// If we got anything other than 200, bail
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to download %s: unexpected HTTP response %d", finalURL, resp.StatusCode)
	}

	// Open a XZ decompressor
	xzResponse, err := xz.NewReader(resp.Body, 0)
	if err != nil {
		return fmt.Errorf("failed to download %s: unexpected xz error: %+v", finalURL, err)
	}

	// Stream the whole decompressed response into memory; we need to strip off the oddball
	// signatures before we invoke unpack200 to convert it into a JAR file
	var packDataBuf bytes.Buffer
	packSz, err := packDataBuf.ReadFrom(xzResponse)
	if err != nil {
		return fmt.Errorf("failed to decompress %s: %+v", finalURL, err)
	}

	// Grab the raw bytes to the data for munging purposes
	packData := packDataBuf.Bytes()

	// Get the signature length
	sigLen, err := signatureLen(packData)
	if err != nil {
		return fmt.Errorf("failed to strip signatures: %+v", err)
	}

	// Create the directory
	err = os.MkdirAll(dir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create lib directory %s: %+v", dir, err)
	}

	// Write the packData (minus the signature) to disk
	err = writeStream(filepath.Join(dir, "tmp.pack"), bytes.NewReader(packData[0:packSz-sigLen]))
	if err != nil {
		fmt.Printf("failed to write %s: %+v", dir, err)
	}

	// Invoke unpack200 on tmp.pack and output to the appropriate JAR name
	err = invokeUnpack200(dir, filename)
	if err != nil {
		return err
	}
	return nil
}

func downloadJar(url, filename string) error {
	dir := filepath.Dir(filename)
	filename = filepath.Base(filename)

	// Construct the URL to download
	resp, err := HttpGet(url)
	if err != nil {
		return fmt.Errorf("failed to download %s: %+v", url, err)
	}
	defer resp.Body.Close()

	// If we got anything other than 200, bail
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to download %s: unexpected HTTP response %d", url, resp.StatusCode)
	}

	// Create the directory
	err = os.MkdirAll(dir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create lib directory %s: %+v", dir, err)
	}

	// Save the stream to disk
	err = writeStream(filepath.Join(dir, filename), resp.Body)
	if err != nil {
		fmt.Printf("failed to write %s: %+v", dir, err)
	}
	return nil
}

func installForgeLibrary(library *gabs.Container, targetDir string) error {
	// TODO: Add support for handling server-side libraries
	var isClientLib = getFlag(library, "clientreq")
	var isServerLib = getFlag(library, "serverreq")

	// Extract key parts of library name
	name := library.Path("name").Data().(string)
	url, ok := library.Path("url").Data().(string)
	if !ok {
		url = "https://libraries.minecraft.net"
	}

	if !isClientLib && !isServerLib {
		fmt.Printf("Skipping %s - not client or server req!\n", name)
		return nil
	}

	fmt.Printf("Installing %s: %s\n", name, url)

	// Unpack the name into maven standard: groupId, artifactId and version
	parts := strings.SplitN(name, ":", 3)
	groupID := parts[0]
	artifactID := parts[1]
	vsn := parts[2]

	// Replace all periods in groupId with path delimiters
	groupID = strings.Replace(groupID, ".", "/", -1)

	// Construct the libDir and libName; if the file already exists, bail
	libName := fmt.Sprintf("%s-%s.jar", artifactID, vsn)
	libDir := filepath.Join(targetDir, "libraries", groupID, artifactID, vsn)
	if fileExists(filepath.Join(libDir, libName)) {
		return nil
	}

	// Construct the URL to download
	finalURL := fmt.Sprintf("%s/%s/%s/%s/%s", url, groupID, artifactID, vsn, libName)
	err := downloadXzPack(finalURL, filepath.Join(libDir, libName))
	if err != nil {
		err = downloadJar(finalURL, filepath.Join(libDir, libName))
		if err != nil {
			return err
		}
	}

	return nil
}

func signatureLen(data []byte) (int64, error) {
	dataSz := len(data)
	if string(data[dataSz-4:dataSz]) != "SIGN" {
		return 0, fmt.Errorf("invalid signature bytes")
	}

	var sigLen uint32
	err := binary.Read(bytes.NewReader(data[dataSz-8:dataSz-4]), binary.LittleEndian, &sigLen)
	if err != nil {
		return 0, fmt.Errorf("invalid signature len: %+v", err)
	}

	return int64(sigLen + 8), nil
}

func invokeUnpack200(libDir, libName string) error {
	err := exec.Command(unpack200Cmd(), "-r",
		filepath.Join(libDir, "tmp.pack"),
		filepath.Join(libDir, libName)).Run()
	if err != nil {
		return fmt.Errorf("failed to run unpack200 on %s: %+v", libName, err)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/rand"
	"time"

	"code.google.com/p/go.net/websocket"
	"menteslibres.net/gosexy/redis"
)

func main() {
	pageSample()
	//mapsSample()
	//    pageUpdates()
}

func random(min, max int) int {
	//rand.Seed(time.Now().Nanosecond())
	return rand.Intn(max-min) + min
}

func mapsSample() {
	client := redis.New()
	err := client.Connect("localhost", 6379)

	if err != nil {
		log.Fatalf("Connect failed: %s\n", err.Error())
		return
	}

	var message string
	for {
		message = fmt.Sprintf("{\"Event\":\"MessageAll\",\"Body\":{\"Event\": \"tweet\", \"Message\": {\"coordinates\": [%v, %v]}},\"Time\":12312}", (random(0, 123) * -1), random(0, 64))
		client.Publish("Message", message)

		time.Sleep(20 * time.Millisecond)

	}
}

var pages = []string{"image", "new", "popular", "realtime", "memes", "gifs", "cats", "dogs", "seals", "headphones"}

func pageSample() {
	shut := make(chan bool)
	store := []*websocket.Conn{}

	for i := 0; i < 26000; i++ {

		ws, err := websocket.Dial("ws://localhost:4000/socket", "", "http://localhost/")
		if err != nil {
			log.Printf("initSocketListener Test failed, Could not connect")
		}

		Auth := fmt.Sprintf("{\"command\":{\"command\":\"authenticate\",\"user\":\"%v\"},\"message\":{\"time\":1378493866}}", i)
		if err := websocket.Message.Send(ws, Auth); err != nil {
			log.Fatal("Authenticate Test failed, Could not send Auth message")
		}

		page := fmt.Sprintf("{\"command\":{\"command\":\"setpage\",\"page\":\"%v\"},\"message\":{\"time\":1378493866}}", pages[random(0, len(pages))])
		if err := websocket.Message.Send(ws, page); err != nil {
			log.Fatal("Authenticate Test failed, Could not send Auth message")
		}

		store = append(store, ws)
		//      time.Sleep(20 * time.Millisecond)
	}

	<-shut
}

func pageUpdates() {
	client := redis.New()
	err := client.Connect("localhost", 6379)

	if err != nil {
		log.Fatalf("Connect failed: %s\n", err.Error())
		return
	}

	var message string
	for {
		page := pages[random(0, len(pages))]
		message = fmt.Sprintf("{\"Event\":\"MessagePage\",\"Body\":{\"Event\": \"update\", \"Page\": \"%s\", \"Message\": {\"page\": \"%s\"}}, \"Time\":12312}", page, page)
		client.Publish("Message", message)
		time.Sleep(20 * time.Millisecond)

	}
}
<commit_msg>Delete testApp.go<commit_after><|endoftext|>