<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"os/signal"
	"syscall"
	"strings"
	"github.com/ethragur/i3ipc-go"
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"flag"
	"github.com/nightlyone/lockfile"
)

func main() {
	usr, err := user.Current()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not get User " + err.Error())
		return
	}

	lock, err := lockfile.New(usr.HomeDir + "/.cache/i3autoname")
	if err != nil {
		fmt.Fprintln(os.Stderr, "Locked: " + err.Error())
		return
	}
	err =  lock.TryLock()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Locked: " + err.Error())
		return
	}
	defer lock.Unlock()

	confDir, err := createConfigDir()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not get User Config Dir" + err.Error())
		return
	}

	db, err := sql.Open("sqlite3", confDir + "gorename.db")
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not create database: " + err.Error())
		return
	}
	defer db.Close()

	err = createWindowDB(db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not create table: " + err.Error())
		return
	}

	listPtr   := flag.Bool("l", false, "Print the DB")
	insertPtr := flag.Bool("i", false, "Insert Icon Class Into DB")
	keyPtr	  := flag.String("class", "", "The Class of the Window")
	iconPtr   := flag.String("icon", "", "The Icon of the Window")
	typePtr   := flag.String("type", "", "Application type")

	flag.Parse()

	if *listPtr {
		err = printIconList(db)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error while Printing List: " + err.Error())
		}
		return
	}
	if *insertPtr {
		if *iconPtr != "" && *typePtr != "" && *keyPtr == "" {
			//insert Type/Icon Pair
			err = insertTypeIconPair(db, *typePtr, *iconPtr)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error inserting Type/Icon Pair: " + err.Error())
			}
		} else if *keyPtr != "" && *typePtr != "" && *iconPtr == "" {
			//insert Class/Type Pair
			err = insertClassTypePair(db, *keyPtr, *typePtr)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error inserting Class/Type Pair: " + err.Error())
			}
		} else {
			fmt.Fprintln(os.Stderr, "Please enter Class and Icon with -class=... & -icon=...")
		}
		return
	}

	window_icons, err := getWindowIcons(db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not read table: " + err.Error())
		return
	}

	// reload config on SIGUSR1
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGUSR1)
	go func(){
		window_icons, err = getWindowIcons(db)

		if err != nil {
			fmt.Fprintln(os.Stderr, "Error could not read table: " + err.Error())
			os.Exit(1)

		}
	}()

	i3ipc.StartEventListener()
	windowEvents, err   := i3ipc.Subscribe(i3ipc.I3WindowEvent)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not subscribe to i3ipc: " + err.Error())
		return
	}

	ipcSocket, err := i3ipc.GetIPCSocket()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Cannot get i3ipc Socket: ", err)
		return
	}

	go shutdownOnRestart()
	for {
		event := <-windowEvents

		if event.Change == "close" || event.Change == "new" || event.Change == "move" {
			tree, err := ipcSocket.GetTree()

			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
			//workspace, err := ipcSocket.GetWorkspaces();
			workspaces := GetWorkspaces(tree.Nodes...)
			for _, workspace := range workspaces {
				newWsName := fmt.Sprintf("%d", workspace.Num)

				for i , window := range GetWindows(workspace) {
					if i == 0 {
						newWsName += ": "
					}
					icon := window_icons[strings.ToLower(window.Window_Properties.Class)]
					if icon == "" {
						icon = ""
					}
					newWsName += icon + "    "

				}
				ipcSocket.Command(fmt.Sprintf("rename workspace \"%s\" to \"%s\"", workspace.Name, newWsName))
			}
		}
	}
}

func GetWorkspaces(Nodes ...i3ipc.I3Node) (workspaces []i3ipc.I3Node) {
	if len(Nodes) == 0 {
		return
	}
	for _, Node := range Nodes {
		//get All of type workspace execpt the internal __i3_scratch space
		if Node.Type == "workspace" && Node.Num != -1 {
			workspaces = append(workspaces, Node)
		} else {
			workspaces = append(workspaces, GetWorkspaces(Node.Nodes...)...)
		}
	}
	return
}

func GetWindows(Nodes ...i3ipc.I3Node) (windows []i3ipc.I3Node) {
	if len(Nodes) == 0 {
		return
	}
	for _, Node := range Nodes {
		//get All of type workspace execpt the internal __i3_scratch space
		if (Node.Type == "con" || Node.Type == "floating_con") && Node.Window > 0 {
			windows = append(windows, Node)
		} else {
			windows = append(windows, GetWindows(Node.Nodes...)...)
		}
	}
	return
}

func createWindowDB(db *sql.DB) (err error) {
	sqlStmt := "CREATE TABLE IF NOT EXISTS type_class(window_class TEXT, window_type TEXT);"
	_, err = db.Exec(sqlStmt)
	sqlStmt = "CREATE TABLE IF NOT EXISTS type_icon(window_type TEXT, window_icon TEXT);"
	_, err = db.Exec(sqlStmt)
	return err
}

func insertTypeIconPair(db *sql.DB, wType string, icon string) (err error) {
	sqlStmt := fmt.Sprintf("INSERT INTO type_icon(window_type, window_icon) VALUES(\"%s\", \"%s\");", wType, icon)
	_, err = db.Exec(sqlStmt)
	return err
}

func insertClassTypePair(db *sql.DB, class string, wType string) (err error) {
	sqlStmt := fmt.Sprintf("INSERT INTO type_class(window_class, window_type) VALUES(\"%s\", \"%s\");", class, wType)
	_, err = db.Exec(sqlStmt)
	return err
}

func getWindowIcons(db *sql.DB) (window_infos map[string]string, err error) {
	window_infos = make(map[string]string)
	sqlStmt := "SELECT window_class, window_icon FROM type_class INNER JOIN type_icon ON type_class.window_type = type_icon.window_type;"
	rows, err := db.Query(sqlStmt)
	if err != nil {
		return window_infos, err
	}
	defer rows.Close()

	for rows.Next() {
		var winClass string
		var winIcon string
		err = rows.Scan(&winClass, &winIcon)
		if err != nil {
			return window_infos, err
		}
		window_infos[strings.ToLower(winClass)] = winIcon
	}
	return window_infos, rows.Err()
}

func createConfigDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	db_dir := usr.HomeDir + "/.config/i3-autorename/"
	os.MkdirAll(db_dir, 0700)
	return db_dir, nil
}

func printIconList(db *sql.DB) (err error) {
	sqlStmt := "SELECT window_class, type_class.window_type, window_icon FROM type_class INNER JOIN type_icon ON type_class.window_type = type_icon.window_type;"
	rows, err := db.Query(sqlStmt)
	if err != nil {
		return err
	}
	defer rows.Close()

	fmt.Println(" Class | Type | Icon ")
	for rows.Next() {
		var winClass string
		var winType  string
		var winIcon  string
		err = rows.Scan(&winClass, &winType, &winIcon)
		if err != nil {
			return err
		}
		fmt.Println(winClass + " | " + winType + " | " + winIcon)
	}
	return nil
}

func shutdownOnRestart() {
	shutdownEvents, err := i3ipc.Subscribe(i3ipc.I3ShutdownEvent)

	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not Subscribe to shutdown event: " + err.Error());
		os.Exit(1)
	}

	for {

		shutdownEvent := <-shutdownEvents

		if shutdownEvent.Change == "restart" {
			os.Exit(1)
		}
	}
}

<commit_msg>Better shutdown Event handler<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"os/signal"
	"syscall"
	"strings"
	"github.com/ethragur/i3ipc-go"
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"flag"
	"github.com/nightlyone/lockfile"
)

func main() {
	usr, err := user.Current()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not get User " + err.Error())
		return
	}

	confDir, err := createConfigDir()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not get User Config Dir" + err.Error())
		return
	}

	db, err := sql.Open("sqlite3", confDir + "gorename.db")
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not create database: " + err.Error())
		return
	}
	defer db.Close()

	err = createWindowDB(db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not create table: " + err.Error())
		return
	}

	listPtr   := flag.Bool("l", false, "Print the DB")
	insertPtr := flag.Bool("i", false, "Insert Icon Class Into DB")
	keyPtr	  := flag.String("class", "", "The Class of the Window")
	iconPtr   := flag.String("icon", "", "The Icon of the Window")
	typePtr   := flag.String("type", "", "Application type")

	flag.Parse()

	if *listPtr {
		err = printIconList(db)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error while Printing List: " + err.Error())
		}
		return
	}
	if *insertPtr {
		if *iconPtr != "" && *typePtr != "" && *keyPtr == "" {
			//insert Type/Icon Pair
			err = insertTypeIconPair(db, *typePtr, *iconPtr)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error inserting Type/Icon Pair: " + err.Error())
			}
		} else if *keyPtr != "" && *typePtr != "" && *iconPtr == "" {
			//insert Class/Type Pair
			err = insertClassTypePair(db, *keyPtr, *typePtr)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error inserting Class/Type Pair: " + err.Error())
			}
		} else {
			fmt.Fprintln(os.Stderr, "Please enter Class and Icon with -class=... & -icon=...")
		}
		return
	}

	window_icons, err := getWindowIcons(db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error could not read table: " + err.Error())
		return
	}

	lock, err := lockfile.New(usr.HomeDir + "/.cache/i3autoname")
	if err != nil {
		fmt.Fprintln(os.Stderr, "Locked: " + err.Error())
		return
	}
	err =  lock.TryLock()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Locked: " + err.Error())
		return
	}
	defer lock.Unlock()

	// reload config on SIGUSR1
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGUSR1)
	go func(){
		window_icons, err = getWindowIcons(db)

		if err != nil {
			fmt.Fprintln(os.Stderr, "Error could not read table: " + err.Error())
			os.Exit(1)

		}
	}()

	i3ipc.StartEventListener()
	windowEvents, err   := i3ipc.Subscribe(i3ipc.I3WindowEvent)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not subscribe to i3ipc: " + err.Error())
		return
	}

	ipcSocket, err := i3ipc.GetIPCSocket()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Cannot get i3ipc Socket: ", err)
		return
	}

	go shutdownOnRestart()
	for {
		event := <-windowEvents

		if event.Change == "close" || event.Change == "new" || event.Change == "move" {
			tree, err := ipcSocket.GetTree()

			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
			//workspace, err := ipcSocket.GetWorkspaces();
			workspaces := GetWorkspaces(tree.Nodes...)
			for _, workspace := range workspaces {
				newWsName := fmt.Sprintf("%d", workspace.Num)

				for i , window := range GetWindows(workspace) {
					if i == 0 {
						newWsName += ": "
					}
					icon := window_icons[strings.ToLower(window.Window_Properties.Class)]
					if icon == "" {
						icon = ""
					}
					newWsName += icon + "    "

				}
				ipcSocket.Command(fmt.Sprintf("rename workspace \"%s\" to \"%s\"", workspace.Name, newWsName))
			}
		}
	}
}

func GetWorkspaces(Nodes ...i3ipc.I3Node) (workspaces []i3ipc.I3Node) {
	if len(Nodes) == 0 {
		return
	}
	for _, Node := range Nodes {
		//get All of type workspace execpt the internal __i3_scratch space
		if Node.Type == "workspace" && Node.Num != -1 {
			workspaces = append(workspaces, Node)
		} else {
			workspaces = append(workspaces, GetWorkspaces(Node.Nodes...)...)
		}
	}
	return
}

func GetWindows(Nodes ...i3ipc.I3Node) (windows []i3ipc.I3Node) {
	if len(Nodes) == 0 {
		return
	}
	for _, Node := range Nodes {
		//get All of type workspace execpt the internal __i3_scratch space
		if (Node.Type == "con" || Node.Type == "floating_con") && Node.Window > 0 {
			windows = append(windows, Node)
		} else {
			windows = append(windows, GetWindows(Node.Nodes...)...)
		}
	}
	return
}

func createWindowDB(db *sql.DB) (err error) {
	sqlStmt := "CREATE TABLE IF NOT EXISTS type_class(window_class TEXT, window_type TEXT);"
	_, err = db.Exec(sqlStmt)
	sqlStmt = "CREATE TABLE IF NOT EXISTS type_icon(window_type TEXT, window_icon TEXT);"
	_, err = db.Exec(sqlStmt)
	return err
}

func insertTypeIconPair(db *sql.DB, wType string, icon string) (err error) {
	sqlStmt := fmt.Sprintf("INSERT INTO type_icon(window_type, window_icon) VALUES(\"%s\", \"%s\");", wType, icon)
	_, err = db.Exec(sqlStmt)
	return err
}

func insertClassTypePair(db *sql.DB, class string, wType string) (err error) {
	sqlStmt := fmt.Sprintf("INSERT INTO type_class(window_class, window_type) VALUES(\"%s\", \"%s\");", class, wType)
	_, err = db.Exec(sqlStmt)
	return err
}

func getWindowIcons(db *sql.DB) (window_infos map[string]string, err error) {
	window_infos = make(map[string]string)
	sqlStmt := "SELECT window_class, window_icon FROM type_class INNER JOIN type_icon ON type_class.window_type = type_icon.window_type;"
	rows, err := db.Query(sqlStmt)
	if err != nil {
		return window_infos, err
	}
	defer rows.Close()

	for rows.Next() {
		var winClass string
		var winIcon string
		err = rows.Scan(&winClass, &winIcon)
		if err != nil {
			return window_infos, err
		}
		window_infos[strings.ToLower(winClass)] = winIcon
	}
	return window_infos, rows.Err()
}

func createConfigDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	db_dir := usr.HomeDir + "/.config/i3-autorename/"
	os.MkdirAll(db_dir, 0700)
	return db_dir, nil
}

func printIconList(db *sql.DB) (err error) {
	sqlStmt := "SELECT window_class, type_class.window_type, window_icon FROM type_class INNER JOIN type_icon ON type_class.window_type = type_icon.window_type;"
	rows, err := db.Query(sqlStmt)
	if err != nil {
		return err
	}
	defer rows.Close()

	fmt.Println(" Class | Type | Icon ")
	for rows.Next() {
		var winClass string
		var winType  string
		var winIcon  string
		err = rows.Scan(&winClass, &winType, &winIcon)
		if err != nil {
			return err
		}
		fmt.Println(winClass + " | " + winType + " | " + winIcon)
	}
	return nil
}

func shutdownOnRestart() {
	shutdownEvents, err := i3ipc.Subscribe(i3ipc.I3ShutdownEvent)

	if err != nil {
		fmt.Fprintln(os.Stderr, "Could not Subscribe to shutdown event: " + err.Error());
		os.Exit(1)
	}

	for {

		shutdownEvent := <-shutdownEvents

		if shutdownEvent.Change == "restart"  || shutdownEvent.Change == "exit" {
			os.Exit(1)
		}
	}
}

<|endoftext|>


<commit_before>package file

import (
	"bytes"
	"fmt"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"github.com/realglobe-Inc/go-lib-rg/log"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"time"
)

// ファイル・ディレクトリに関するユーティリティ。

const (
	dirPerm  = 0755
	filePerm = 0644
)

func IsExist(path string) (bool, error) {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		} else {
			return false, erro.Wrap(err)
		}
	}
	return true, nil
}

func IsDir(path string) (bool, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, erro.Wrap(err)
	}
	return fi.IsDir(), nil
}

func Size(path string) (int64, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return -1, erro.Wrap(err)
	}
	return fi.Size(), nil
}

// /a/b/c から [ /a/b /a / ] をつくる
func DirList(path string) []string {
	list := []string{}
	for i := 0; i < 10; i++ {
		newPath := filepath.Dir(path)
		if newPath == path {
			break
		}
		path = newPath
		list = append(list, path)
	}
	return list
}

// ファイルの中身を丸ごと比較する。
func Compare(path1, path2 string) (int, error) {
	fi1, err := os.Stat(path1)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	fi2, err := os.Stat(path2)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	if fi1.Size() < fi2.Size() {
		return -1, nil
	} else if fi1.Size() > fi2.Size() {
		return 1, nil
	}

	// 読み込みつつ逐次比較するように改善できるが、めんどう。
	bytes1, err := ioutil.ReadFile(path1)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	bytes2, err := ioutil.ReadFile(path2)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	return bytes.Compare(bytes1, bytes2), nil
}

// ファイルまたはディレクトリの名前をてきとうに変更する。ディレクトリ間の移動はしない。
// 返り値は新しい名前。ディレクトリは含まない。
func Escape(path, suffix string) (newName string, err error) {
	name := filepath.Base(path)
	date := time.Now()
	tag := fmt.Sprintf("%04d%02d%02d%02d%02d%02d%09d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), date.Nanosecond())
	newName = name + "." + tag + suffix
	newPath := filepath.Join(filepath.Dir(path), newName)

	if e := os.Rename(path, newPath); e != nil {
		return "", erro.Wrap(e)
	}

	log.Debug("mv ", path, " ", newPath)

	return newName, nil
}

// 引数の順番は io.Copy にならった。
func Copy(to, from string) error {
	buff, err := ioutil.ReadFile(from)
	if err != nil {
		return erro.Wrap(err)
	}

	// パーミッションも取得。
	fi, err := os.Stat(from)
	if err != nil {
		return erro.Wrap(err)
	}

	if e := os.MkdirAll(filepath.Dir(to), dirPerm); e != nil {
		return erro.Wrap(e)
	}

	if e := ioutil.WriteFile(to, buff, fi.Mode()); e != nil {
		return erro.Wrap(e)
	}

	log.Debug("cp ", from, " ", to)

	return nil
}

// ファイルの末尾に付け足す。
func Append(path string, data []byte) error {
	if e := os.MkdirAll(filepath.Dir(path), dirPerm); e != nil {
		return erro.Wrap(e)
	}

	writer, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, filePerm)
	if err != nil {
		return erro.Wrap(err)
	}
	defer writer.Close()

	stat, err := writer.Stat()
	if err != nil {
		return erro.Wrap(err)
	}

	if _, e := writer.WriteAt(data, stat.Size()); e != nil {
		return erro.Wrap(e)
	}

	return erro.Wrap(err)
}

// path 以下を全部 owner のものにする。
func Chown(path, owner string) error {
	user, err := user.Lookup(owner)
	if err != nil {
		return erro.Wrap(err)
	}

	uid, err := strconv.ParseUint(user.Uid, 10, 32)
	if err != nil {
		return erro.Wrap(err)
	}

	if e := filepath.Walk(path, func(curPath string, info os.FileInfo, err error) error {
		if err != nil {
			return erro.Wrap(err)
		}

		if e := os.Chown(curPath, int(uid), int(uid)); e != nil {
			return erro.Wrap(e)
		}

		return nil
	}); e != nil {
		return erro.Wrap(err)
	}

	return nil
}
<commit_msg>リンクも検知するように変更<commit_after>package file

import (
	"bytes"
	"fmt"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"github.com/realglobe-Inc/go-lib-rg/log"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"time"
)

// ファイル・ディレクトリに関するユーティリティ。

const (
	dirPerm  = 0755
	filePerm = 0644
)

func IsExist(path string) (bool, error) {
	_, err := os.Lstat(path) // リンクも検知する。
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		} else {
			return false, erro.Wrap(err)
		}
	}
	return true, nil
}

func IsDir(path string) (bool, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, erro.Wrap(err)
	}
	return fi.IsDir(), nil
}

func Size(path string) (int64, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return -1, erro.Wrap(err)
	}
	return fi.Size(), nil
}

// /a/b/c から [ /a/b /a / ] をつくる
func DirList(path string) []string {
	list := []string{}
	for i := 0; i < 10; i++ {
		newPath := filepath.Dir(path)
		if newPath == path {
			break
		}
		path = newPath
		list = append(list, path)
	}
	return list
}

// ファイルの中身を丸ごと比較する。
func Compare(path1, path2 string) (int, error) {
	fi1, err := os.Stat(path1)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	fi2, err := os.Stat(path2)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	if fi1.Size() < fi2.Size() {
		return -1, nil
	} else if fi1.Size() > fi2.Size() {
		return 1, nil
	}

	// 読み込みつつ逐次比較するように改善できるが、めんどう。
	bytes1, err := ioutil.ReadFile(path1)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	bytes2, err := ioutil.ReadFile(path2)
	if err != nil {
		return 0, erro.Wrap(err)
	}
	return bytes.Compare(bytes1, bytes2), nil
}

// ファイルまたはディレクトリの名前をてきとうに変更する。ディレクトリ間の移動はしない。
// 返り値は新しい名前。ディレクトリは含まない。
func Escape(path, suffix string) (newName string, err error) {
	name := filepath.Base(path)
	date := time.Now()
	tag := fmt.Sprintf("%04d%02d%02d%02d%02d%02d%09d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), date.Nanosecond())
	newName = name + "." + tag + suffix
	newPath := filepath.Join(filepath.Dir(path), newName)

	if e := os.Rename(path, newPath); e != nil {
		return "", erro.Wrap(e)
	}

	log.Debug("mv ", path, " ", newPath)

	return newName, nil
}

// 引数の順番は io.Copy にならった。
func Copy(to, from string) error {
	buff, err := ioutil.ReadFile(from)
	if err != nil {
		return erro.Wrap(err)
	}

	// パーミッションも取得。
	fi, err := os.Stat(from)
	if err != nil {
		return erro.Wrap(err)
	}

	if e := os.MkdirAll(filepath.Dir(to), dirPerm); e != nil {
		return erro.Wrap(e)
	}

	if e := ioutil.WriteFile(to, buff, fi.Mode()); e != nil {
		return erro.Wrap(e)
	}

	log.Debug("cp ", from, " ", to)

	return nil
}

// ファイルの末尾に付け足す。
func Append(path string, data []byte) error {
	if e := os.MkdirAll(filepath.Dir(path), dirPerm); e != nil {
		return erro.Wrap(e)
	}

	writer, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, filePerm)
	if err != nil {
		return erro.Wrap(err)
	}
	defer writer.Close()

	stat, err := writer.Stat()
	if err != nil {
		return erro.Wrap(err)
	}

	if _, e := writer.WriteAt(data, stat.Size()); e != nil {
		return erro.Wrap(e)
	}

	return erro.Wrap(err)
}

// path 以下を全部 owner のものにする。
func Chown(path, owner string) error {
	user, err := user.Lookup(owner)
	if err != nil {
		return erro.Wrap(err)
	}

	uid, err := strconv.ParseUint(user.Uid, 10, 32)
	if err != nil {
		return erro.Wrap(err)
	}

	if e := filepath.Walk(path, func(curPath string, info os.FileInfo, err error) error {
		if err != nil {
			return erro.Wrap(err)
		}

		if e := os.Chown(curPath, int(uid), int(uid)); e != nil {
			return erro.Wrap(e)
		}

		return nil
	}); e != nil {
		return erro.Wrap(err)
	}

	return nil
}
<|endoftext|>


<commit_before>package inference

import (
	"testing"

	"github.com/cayleygraph/quad"
	"github.com/cayleygraph/quad/voc/rdf"
)

func TestStoreProcessQuad(t *testing.T) {
	store := NewStore()
	q := quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI(rdf.Type), Object: quad.IRI("Person"), Label: nil}
	store.ProcessQuad(q)
	createdClass := store.GetClass(quad.IRI("Person"))
	if createdClass == nil {
		t.Error("Class was not created")
	}
}
<commit_msg>Test creations<commit_after>package inference

import (
	"testing"

	"github.com/cayleygraph/quad"
	"github.com/cayleygraph/quad/voc/rdf"
	"github.com/cayleygraph/quad/voc/rdfs"
)

func TestReferencedType(t *testing.T) {
	store := NewStore()
	q := quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI(rdf.Type), Object: quad.IRI("Person"), Label: nil}
	store.ProcessQuad(q)
	createdClass := store.GetClass(quad.IRI("Person"))
	if createdClass == nil {
		t.Error("Class was not created")
	}
}

func TestNewClass(t *testing.T) {
	store := NewStore()
	q := quad.Quad{Subject: quad.IRI("Person"), Predicate: quad.IRI(rdf.Type), Object: quad.IRI(rdfs.Class), Label: nil}
	store.ProcessQuad(q)
	createdClass := store.GetClass(quad.IRI("Person"))
	if createdClass == nil {
		t.Error("Class was not created")
	}
}

func TestNewProperty(t *testing.T) {
	store := NewStore()
	q := quad.Quad{Subject: quad.IRI("name"), Predicate: quad.IRI(rdf.Type), Object: quad.IRI(rdf.Property), Label: nil}
	store.ProcessQuad(q)
	createdProperty := store.GetProperty(quad.IRI("name"))
	if createdProperty == nil {
		t.Error("Property was not created")
	}
}
<|endoftext|>


<commit_before>package integration

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	yaml "gopkg.in/yaml.v2"

	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Happy Path Installation Tests", func() {
	kisPath := CopyKismaticToTemp()

	BeforeSuite(func() {
		fmt.Println("Unpacking kismatic to", kisPath)
		c := exec.Command("tar", "-zxf", "../out/kismatic.tar.gz", "-C", kisPath)
		tarOut, tarErr := c.CombinedOutput()
		if tarErr != nil {
			log.Fatal("Error unpacking installer", string(tarOut), tarErr)
		}
		CopyDir("test-tls/", kisPath+"/test-tls")
		os.Chdir(kisPath)
	})

	AfterSuite(func() {
		if !leaveInstallDir() {
			os.RemoveAll(kisPath)
		}
	})

	Describe("Calling installer with no input", func() {
		It("should output help text", func() {
			c := exec.Command("./kismatic")
			helpbytes, helperr := c.Output()
			Expect(helperr).To(BeNil())
			helpText := string(helpbytes)
			Expect(helpText).To(ContainSubstring("Usage"))
		})
	})

	Describe("Calling installer with 'install plan'", func() {
		Context("and just hitting enter", func() {
			It("should result in the output of a well formed default plan file", func() {
				By("Outputing a file")
				c := exec.Command("./kismatic", "install", "plan")
				helpbytes, helperr := c.Output()
				Expect(helperr).To(BeNil())
				helpText := string(helpbytes)
				Expect(helpText).To(ContainSubstring("Generating installation plan file with 3 etcd nodes, 2 master nodes and 3 worker nodes"))
				Expect(FileExists("kismatic-cluster.yaml")).To(Equal(true))

				By("Outputing a file with valid YAML")
				yamlBytes, err := ioutil.ReadFile("kismatic-cluster.yaml")
				if err != nil {
					Fail("Could not read cluster file")
				}
				yamlBlob := string(yamlBytes)

				planFromYaml := ClusterPlan{}

				unmarshallErr := yaml.Unmarshal([]byte(yamlBlob), &planFromYaml)
				if unmarshallErr != nil {
					Fail("Could not unmarshall cluster yaml: %v")
				}
			})
		})
	})
	Describe("Calling installer with a plan targeting bad infrastructure", func() {
		Context("Using a 1/1/1 Ubuntu 16.04 layout pointing to bad ip addresses", func() {
			It("should bomb validate and apply", func() {
				if !completesInTime(installKismaticWithABadNode, 30*time.Second) {
					Fail("It shouldn't take 30 seconds for Kismatic to fail with bad nodes.")
				}
			})
		})
	})

	Describe("Installing with package installation enabled", func() {
		installOpts := installOptions{
			allowPackageInstallation: true,
		}
		Context("Targeting AWS infrastructure", func() {
			Context("using a 1/1/1 layout with Ubuntu 16.04 LTS", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, Ubuntu1604LTS, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("using a 1/1/1 layout with CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("using a 3/2/3 layout with CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{3, 2, 3}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing against a minikube layout", func() {
		Context("Targeting AWS infrastructure", func() {
			Context("Using CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("Using Ubuntu 16.04 LTS", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(Ubuntu1604LTS, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})

		Context("Targeting Packet Infrastructure", func() {
			Context("Using CentOS 7", func() {
				ItOnPacket("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing with package installation disabled", func() {
		installOpts := installOptions{
			allowPackageInstallation: false,
		}
		Context("Targeting AWS infrastructure", func() {
			Context("Using a 1/1/1 layout with Ubuntu 16.04 LTS", func() {
				ItOnAWS("Should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, Ubuntu1604LTS, provisioner, func(nodes provisionedNodes, sshKey string) {
						By("Installing the Kismatic RPMs")
						InstallKismaticRPMs(nodes, Ubuntu1604LTS, sshKey)
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			Context("Using a 1/1/1 CentOS 7 layout", func() {
				ItOnAWS("Should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						By("Installing the Kismatic RPMs")
						InstallKismaticRPMs(nodes, CentOS7, sshKey)
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing with private Docker registry", func() {
		Context("Using a 1/1/1 CentOS 7 layout", func() {
			nodeCount := NodeCount{Etcd: 1, Master: 1, Worker: 1}
			distro := CentOS7

			Context("Using the auto-configured docker registry", func() {
				ItOnAWS("should result in a working cluster", func(aws infrastructureProvisioner) {
					WithInfrastructure(nodeCount, distro, aws, func(nodes provisionedNodes, sshKey string) {
						installOpts := installOptions{
							allowPackageInstallation:    true,
							autoConfigureDockerRegistry: true,
						}
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			Context("Using a custom registry provided by the user", func() {
				ItOnAWS("should result in a working cluster", func(aws infrastructureProvisioner) {
					WithInfrastructure(nodeCount, distro, aws, func(nodes provisionedNodes, sshKey string) {
						By("Installing an external Docker registry on one of the etcd nodes")
						dockerRegistryPort := 443
						caFile, err := deployDockerRegistry(nodes.etcd[0], dockerRegistryPort, sshKey)
						Expect(err).ToNot(HaveOccurred())
						installOpts := installOptions{
							allowPackageInstallation: true,
							dockerRegistryCAPath:     caFile,
							dockerRegistryIP:         nodes.etcd[0].PrivateIP,
							dockerRegistryPort:       dockerRegistryPort,
						}
						err = installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})
})
<commit_msg>Fixed integration tests<commit_after>package integration

import (
	"io/ioutil"
	"time"

	yaml "gopkg.in/yaml.v2"

	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Happy Path Installation Tests", func() {
	Describe("Calling installer with no input", func() {
		It("should output help text", func() {
			c := exec.Command("./kismatic")
			helpbytes, helperr := c.Output()
			Expect(helperr).To(BeNil())
			helpText := string(helpbytes)
			Expect(helpText).To(ContainSubstring("Usage"))
		})
	})

	Describe("Calling installer with 'install plan'", func() {
		Context("and just hitting enter", func() {
			It("should result in the output of a well formed default plan file", func() {
				By("Outputing a file")
				c := exec.Command("./kismatic", "install", "plan")
				helpbytes, helperr := c.Output()
				Expect(helperr).To(BeNil())
				helpText := string(helpbytes)
				Expect(helpText).To(ContainSubstring("Generating installation plan file with 3 etcd nodes, 2 master nodes and 3 worker nodes"))
				Expect(FileExists("kismatic-cluster.yaml")).To(Equal(true))

				By("Outputing a file with valid YAML")
				yamlBytes, err := ioutil.ReadFile("kismatic-cluster.yaml")
				if err != nil {
					Fail("Could not read cluster file")
				}
				yamlBlob := string(yamlBytes)

				planFromYaml := ClusterPlan{}

				unmarshallErr := yaml.Unmarshal([]byte(yamlBlob), &planFromYaml)
				if unmarshallErr != nil {
					Fail("Could not unmarshall cluster yaml: %v")
				}
			})
		})
	})
	Describe("Calling installer with a plan targeting bad infrastructure", func() {
		Context("Using a 1/1/1 Ubuntu 16.04 layout pointing to bad ip addresses", func() {
			It("should bomb validate and apply", func() {
				if !completesInTime(installKismaticWithABadNode, 30*time.Second) {
					Fail("It shouldn't take 30 seconds for Kismatic to fail with bad nodes.")
				}
			})
		})
	})

	Describe("Installing with package installation enabled", func() {
		installOpts := installOptions{
			allowPackageInstallation: true,
		}
		Context("Targeting AWS infrastructure", func() {
			Context("using a 1/1/1 layout with Ubuntu 16.04 LTS", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, Ubuntu1604LTS, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("using a 1/1/1 layout with CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("using a 3/2/3 layout with CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{3, 2, 3}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing against a minikube layout", func() {
		Context("Targeting AWS infrastructure", func() {
			Context("Using CentOS 7", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
			Context("Using Ubuntu 16.04 LTS", func() {
				ItOnAWS("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(Ubuntu1604LTS, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})

		Context("Targeting Packet Infrastructure", func() {
			Context("Using CentOS 7", func() {
				ItOnPacket("should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, provisioner, func(node NodeDeets, sshKey string) {
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing with package installation disabled", func() {
		installOpts := installOptions{
			allowPackageInstallation: false,
		}
		Context("Targeting AWS infrastructure", func() {
			Context("Using a 1/1/1 layout with Ubuntu 16.04 LTS", func() {
				ItOnAWS("Should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, Ubuntu1604LTS, provisioner, func(nodes provisionedNodes, sshKey string) {
						By("Installing the Kismatic RPMs")
						InstallKismaticRPMs(nodes, Ubuntu1604LTS, sshKey)
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			Context("Using a 1/1/1 CentOS 7 layout", func() {
				ItOnAWS("Should result in a working cluster", func(provisioner infrastructureProvisioner) {
					WithInfrastructure(NodeCount{1, 1, 1}, CentOS7, provisioner, func(nodes provisionedNodes, sshKey string) {
						By("Installing the Kismatic RPMs")
						InstallKismaticRPMs(nodes, CentOS7, sshKey)
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})

	Describe("Installing with private Docker registry", func() {
		Context("Using a 1/1/1 CentOS 7 layout", func() {
			nodeCount := NodeCount{Etcd: 1, Master: 1, Worker: 1}
			distro := CentOS7

			Context("Using the auto-configured docker registry", func() {
				ItOnAWS("should result in a working cluster", func(aws infrastructureProvisioner) {
					WithInfrastructure(nodeCount, distro, aws, func(nodes provisionedNodes, sshKey string) {
						installOpts := installOptions{
							allowPackageInstallation:    true,
							autoConfigureDockerRegistry: true,
						}
						err := installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			Context("Using a custom registry provided by the user", func() {
				ItOnAWS("should result in a working cluster", func(aws infrastructureProvisioner) {
					WithInfrastructure(nodeCount, distro, aws, func(nodes provisionedNodes, sshKey string) {
						By("Installing an external Docker registry on one of the etcd nodes")
						dockerRegistryPort := 443
						caFile, err := deployDockerRegistry(nodes.etcd[0], dockerRegistryPort, sshKey)
						Expect(err).ToNot(HaveOccurred())
						installOpts := installOptions{
							allowPackageInstallation: true,
							dockerRegistryCAPath:     caFile,
							dockerRegistryIP:         nodes.etcd[0].PrivateIP,
							dockerRegistryPort:       dockerRegistryPort,
						}
						err = installKismatic(nodes, installOpts, sshKey)
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package batch

import (
	"bufio"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"
	"time"

	"github.com/8legd/RRP/processors"
)

func MultipartMixed(w http.ResponseWriter, r *http.Request) {
	var batch []*http.Request
	ct, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if ct != "multipart/mixed" {
		err = errors.New("unsupported content type, expected multipart/mixed")
		http.Error(w, err.Error(), http.StatusUnsupportedMediaType)
		return
	}
	// check for optional timeout header

	tm := r.Header.Get("x-batchproxy-timeout")
	var timeout time.Duration
	if tm != "" {
		timeout, err = time.ParseDuration(tm + "s")
		if err != nil {
			http.Error(w, "invalid value for x-batchproxy-timeout header, expected number of seconds", http.StatusBadRequest)
			return
		}
	} else {
		timeout = time.Duration(20) * time.Second // Default timeout is 20 seconds
	}
	log.Println(timeout)
	boundary, ok := params["boundary"]
	if !ok {
		err = errors.New("missing multipart boundary")
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	mr := multipart.NewReader(r.Body, boundary)
	for {
		p, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		pct, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		if pct != "application/http" {
			err = errors.New("unsupported content type for multipart/mixed content, expected each part to be application/http")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		r, err := http.ReadRequest(bufio.NewReader(p))
		// We need to get the protocol from a header in the part's request
		protocol := r.Header.Get("Forwarded")
		if protocol == "" || !strings.Contains(protocol, "proto=http") { // proto must be `http` or `https`
			err = errors.New("missing header in multipart/mixed content, expected each part to contain a Forwarded header with a valid proto value (proto=http or proto=https)")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		parts := strings.Split(protocol, "proto=")
		if len(parts) < 2 || (parts[1] != "http" && parts[1] != "https") {
			err = errors.New("invalid proto value in Forwarded header, expected proto=http or proto=https")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		protocol = parts[1]
		url := protocol + "://" + r.Host + r.RequestURI
		request, err := http.NewRequest(r.Method, url, r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		batch = append(batch, request)
	}
	responses, err := processors.ProcessBatch(batch, timeout)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	mw := multipart.NewWriter(w)
	defer mw.Close()
	w.Header().Set("Content-Type", "multipart/mixed; boundary="+mw.Boundary())

	var pw io.Writer
	var pb []byte

	for _, next := range responses {
		ph := make(textproto.MIMEHeader)
		ph.Set("Content-Type", "application/http")
		pw, err = mw.CreatePart(ph)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		io.WriteString(pw, next.Proto+" "+next.Status+"\n")
		if next.Header != nil {
			log.Println(next.Header)
			next.Header.Write(pw)
			io.WriteString(pw, "\n")
		}
		if next.Body != nil {
			pb, err = ioutil.ReadAll(next.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			pw.Write(pb)
			io.WriteString(pw, "\n")
		}
	}
}
<commit_msg>Continued work on initial release<commit_after>package batch

import (
	"bufio"
	"errors"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"
	"time"

	"github.com/8legd/RRP/processors"
)

func MultipartMixed(w http.ResponseWriter, r *http.Request) {
	var batch []*http.Request
	ct, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if ct != "multipart/mixed" {
		err = errors.New("unsupported content type, expected multipart/mixed")
		http.Error(w, err.Error(), http.StatusUnsupportedMediaType)
		return
	}
	// check for optional timeout header

	tm := r.Header.Get("x-batchproxy-timeout")
	var timeout time.Duration
	if tm != "" {
		timeout, err = time.ParseDuration(tm + "s")
		if err != nil {
			http.Error(w, "invalid value for x-batchproxy-timeout header, expected number of seconds", http.StatusBadRequest)
			return
		}
	} else {
		timeout = time.Duration(20) * time.Second // Default timeout is 20 seconds
	}
	boundary, ok := params["boundary"]
	if !ok {
		err = errors.New("missing multipart boundary")
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	mr := multipart.NewReader(r.Body, boundary)
	for {
		p, err := mr.NextPart()
		if err == io.EOF {
			if p == nil {
				err = errors.New("invalid multipart content")
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			break
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		pct, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if pct != "application/http" {
			err = errors.New("unsupported content type for multipart/mixed content, expected each part to be application/http")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		r, err := http.ReadRequest(bufio.NewReader(p))
		// We need to get the protocol from a header in the part's request
		protocol := r.Header.Get("Forwarded")
		if protocol == "" || !strings.Contains(protocol, "proto=http") { // proto must be `http` or `https`
			err = errors.New("missing header in multipart/mixed content, expected each part to contain a Forwarded header with a valid proto value (proto=http or proto=https)")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		parts := strings.Split(protocol, "proto=")
		if len(parts) < 2 || (parts[1] != "http" && parts[1] != "https") {
			err = errors.New("invalid proto value in Forwarded header, expected proto=http or proto=https")
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		protocol = parts[1]
		url := protocol + "://" + r.Host + r.RequestURI
		request, err := http.NewRequest(r.Method, url, r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		batch = append(batch, request)
	}
	responses, err := processors.ProcessBatch(batch, timeout)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	mw := multipart.NewWriter(w)
	defer mw.Close()
	w.Header().Set("Content-Type", "multipart/mixed; boundary="+mw.Boundary())

	var pw io.Writer
	var pb []byte

	for _, next := range responses {
		ph := make(textproto.MIMEHeader)
		ph.Set("Content-Type", "application/http")
		pw, err = mw.CreatePart(ph)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		io.WriteString(pw, next.Proto+" "+next.Status+"\n")
		if next.Header != nil {
			next.Header.Write(pw)
			io.WriteString(pw, "\n")
		}
		if next.Body != nil {
			pb, err = ioutil.ReadAll(next.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			pw.Write(pb)
			io.WriteString(pw, "\n")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/PuerkitoBio/goquery"
)

const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"

func main() {
	var (
		flagBitbar = flag.Bool("bitbar", false, "Enable bitbar compatible output")
	)
	flag.Parse()
	if flag.NArg() == 0 {
		funds, err := GetFunds()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Print(prettyPrint(*flagBitbar, funds...))
		return
	}

	code := flag.Arg(0)
	fund, err := GetFund(code)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(prettyPrint(*flagBitbar, fund))

}

func GetFund(code string) (Fund, error) {
	code = strings.ToUpper(code)

	funds, err := GetFunds()
	if err != nil {
		return Fund{}, err
	}

	for _, fund := range funds {
		if fund.Code == code {
			return fund, nil
		}
	}
	return Fund{}, fmt.Errorf("fund not found")
}

func GetFunds(codes ...string) ([]Fund, error) {
	const (
		baseurl    = "http://www.akportfoy.com.tr/ajax/getfundreturns?fundsubtypeId=%v&enddate=%v&lang=tr"
		timelayout = "02/01/2006"
	)

	c := &http.Client{Timeout: time.Minute}

	const fund = YabanciHisseSenedi
	today := time.Now().Format(timelayout)

	u := fmt.Sprintf(baseurl, fund, today)
	req, _ := http.NewRequest("POST", u, nil)
	req.Header.Set("User-Agent", userAgent)

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var response struct {
		Title string
		Table string
	}
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return nil, err
	}

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(response.Table))
	if err != nil {
		return nil, err
	}

	atof := func(s string) float64 {
		f, _ := strconv.ParseFloat(s, 64)
		return f
	}

	var funds []Fund
	doc.Find("tr").Each(func(i int, sel *goquery.Selection) {
		if i == 0 {
			return
		}

		code := sel.Find(".fundcode").Text()
		name := sel.Find("th").Text()
		name = strings.TrimPrefix(name, code)
		name = strings.TrimSpace(name)

		fund := Fund{
			Code: code,
			Name: name,
		}

		sel.Children().Each(func(n int, sel *goquery.Selection) {
			switch n {
			case 1:
				fund.Price = atof(sel.Text())
			case 2:
				fund.Daily = atof(sel.Text())
			case 3:
				fund.Weekly = atof(sel.Text())
			case 4:
				fund.Monthly = atof(sel.Text())
			case 5:
				fund.Annual = atof(sel.Text())
			}
		})

		funds = append(funds, fund)
	})

	return funds, nil
}

type Fund struct {
	Type    FundType
	Code    string
	Name    string
	Price   float64
	Daily   float64
	Weekly  float64
	Monthly float64
	Annual  float64
}

type FundType uint8

const (
	ParaPiyasasi            FundType = 5
	BorclanmaAraclari       FundType = 6
	Katilim                 FundType = 4
	Degisken                FundType = 2
	HisseSenedi             FundType = 1
	YabanciHisseSenedi      FundType = 7
	DegerliMaden            FundType = 3
	FonSepeti               FundType = 9
	GayrimenkulYatirim      FundType = 32
	GirisimSermayesiYatirim FundType = 30
	DovizSerbest            FundType = 71
)

func prettyPrint(bitbar bool, funds ...Fund) string {
	if bitbar {
		return printBitbar(funds...)
	}

	return printLong(funds...)
}

func printBitbar(funds ...Fund) string {
	color := func(f float64) string {
		if f < 0 {
			return "red"
		}
		return "green"
	}

	var buf bytes.Buffer

	format := "%v %v (%v%%) | color=%v href=https://www.akportfoy.com.tr/tr/fund/%v\n"

	for _, f := range funds {
		fmt.Fprintf(&buf, format, f.Code, f.Price, f.Daily, color(f.Daily), f.Code)
		fmt.Fprintf(&buf, "----\n")
		fmt.Fprintf(&buf, "-- weekly:  %v%% | color=%v\n", f.Weekly, color(f.Weekly))
		fmt.Fprintf(&buf, "-- monthly: %v%% | color=%v\n", f.Monthly, color(f.Monthly))
		fmt.Fprintf(&buf, "-- annual:  %v%% | color=%v\n", f.Annual, color(f.Annual))
	}
	return buf.String()
}

func printLong(funds ...Fund) string {
	var buf bytes.Buffer
	w := tabwriter.NewWriter(&buf, 0, 2, 2, ' ', 0)

	format := "%v\t%v\t%v\t%v\t%v\t%v\t%v\n"

	fmt.Fprintf(w, format, "code", "name", "price", "daily", "weekly", "monthly", "annual")
	for _, f := range funds {
		fmt.Fprintf(w, format, f.Code, f.Name, f.Price, f.Daily, f.Weekly, f.Monthly, f.Annual)
	}
	w.Flush()

	return buf.String()
}
<commit_msg>fund: show tefas link on click<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/PuerkitoBio/goquery"
)

const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"

func main() {
	var (
		flagBitbar = flag.Bool("bitbar", false, "Enable bitbar compatible output")
	)
	flag.Parse()
	if flag.NArg() == 0 {
		funds, err := GetFunds()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Print(prettyPrint(*flagBitbar, funds...))
		return
	}

	code := flag.Arg(0)
	fund, err := GetFund(code)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(prettyPrint(*flagBitbar, fund))

}

func GetFund(code string) (Fund, error) {
	code = strings.ToUpper(code)

	funds, err := GetFunds()
	if err != nil {
		return Fund{}, err
	}

	for _, fund := range funds {
		if fund.Code == code {
			return fund, nil
		}
	}
	return Fund{}, fmt.Errorf("fund not found")
}

func GetFunds(codes ...string) ([]Fund, error) {
	const (
		baseurl    = "http://www.akportfoy.com.tr/ajax/getfundreturns?fundsubtypeId=%v&enddate=%v&lang=tr"
		timelayout = "02/01/2006"
	)

	c := &http.Client{Timeout: time.Minute}

	const fund = YabanciHisseSenedi
	today := time.Now().Format(timelayout)

	u := fmt.Sprintf(baseurl, fund, today)
	req, _ := http.NewRequest("POST", u, nil)
	req.Header.Set("User-Agent", userAgent)

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var response struct {
		Title string
		Table string
	}
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return nil, err
	}

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(response.Table))
	if err != nil {
		return nil, err
	}

	atof := func(s string) float64 {
		f, _ := strconv.ParseFloat(s, 64)
		return f
	}

	var funds []Fund
	doc.Find("tr").Each(func(i int, sel *goquery.Selection) {
		if i == 0 {
			return
		}

		code := sel.Find(".fundcode").Text()
		name := sel.Find("th").Text()
		name = strings.TrimPrefix(name, code)
		name = strings.TrimSpace(name)

		fund := Fund{
			Code: code,
			Name: name,
		}

		sel.Children().Each(func(n int, sel *goquery.Selection) {
			switch n {
			case 1:
				fund.Price = atof(sel.Text())
			case 2:
				fund.Daily = atof(sel.Text())
			case 3:
				fund.Weekly = atof(sel.Text())
			case 4:
				fund.Monthly = atof(sel.Text())
			case 5:
				fund.Annual = atof(sel.Text())
			}
		})

		funds = append(funds, fund)
	})

	return funds, nil
}

type Fund struct {
	Type    FundType
	Code    string
	Name    string
	Price   float64
	Daily   float64
	Weekly  float64
	Monthly float64
	Annual  float64
}

type FundType uint8

const (
	ParaPiyasasi            FundType = 5
	BorclanmaAraclari       FundType = 6
	Katilim                 FundType = 4
	Degisken                FundType = 2
	HisseSenedi             FundType = 1
	YabanciHisseSenedi      FundType = 7
	DegerliMaden            FundType = 3
	FonSepeti               FundType = 9
	GayrimenkulYatirim      FundType = 32
	GirisimSermayesiYatirim FundType = 30
	DovizSerbest            FundType = 71
)

func prettyPrint(bitbar bool, funds ...Fund) string {
	if bitbar {
		return printBitbar(funds...)
	}

	return printLong(funds...)
}

func printBitbar(funds ...Fund) string {
	color := func(f float64) string {
		if f < 0 {
			return "red"
		}
		return "green"
	}

	var buf bytes.Buffer

	format := "%v %v (%v%%) | color=%v href=http://www.tefas.gov.tr/FonAnaliz.aspx?FonKod=%v\n"

	for _, f := range funds {
		fmt.Fprintf(&buf, format, f.Code, f.Price, f.Daily, color(f.Daily), f.Code)
		fmt.Fprintf(&buf, "----\n")
		fmt.Fprintf(&buf, "-- weekly:  %v%% | color=%v\n", f.Weekly, color(f.Weekly))
		fmt.Fprintf(&buf, "-- monthly: %v%% | color=%v\n", f.Monthly, color(f.Monthly))
		fmt.Fprintf(&buf, "-- annual:  %v%% | color=%v\n", f.Annual, color(f.Annual))
	}
	return buf.String()
}

func printLong(funds ...Fund) string {
	var buf bytes.Buffer
	w := tabwriter.NewWriter(&buf, 0, 2, 2, ' ', 0)

	format := "%v\t%v\t%v\t%v\t%v\t%v\t%v\n"

	fmt.Fprintf(w, format, "code", "name", "price", "daily", "weekly", "monthly", "annual")
	for _, f := range funds {
		fmt.Fprintf(w, format, f.Code, f.Name, f.Price, f.Daily, f.Weekly, f.Monthly, f.Annual)
	}
	w.Flush()

	return buf.String()
}
<|endoftext|>


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

package v1alpha2

import (
	"github.com/gogo/protobuf/proto"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IstioControlPlane) DeepCopyInto(out *IstioControlPlane) {
	*out = *proto.Clone(in).(*IstioControlPlane)
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioControlPlane.
func (in *IstioControlPlane) DeepCopy() *IstioControlPlane {
	if in == nil {
		return nil
	}
	out := new(IstioControlPlane)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IstioControlPlane) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// TODO: needs to be implemented or generated.
func (in *IstioControlPlane) GetObjectKind() schema.ObjectKind {
	return EmptyObjectKind
}

// EmptyObjectKind implements the ObjectKind interface as a noop
var EmptyObjectKind = emptyObjectKind{}

type emptyObjectKind struct{}

// SetGroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) SetGroupVersionKind(gvk schema.GroupVersionKind) {}

// GroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) GroupVersionKind() schema.GroupVersionKind { return schema.GroupVersionKind{} }

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IstioControlPlaneList) DeepCopyInto(out *IstioControlPlaneList) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	out.ListMeta = in.ListMeta
	if in.Items != nil {
		in, out := &in.Items, &out.Items
		*out = make([]IstioControlPlane, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioControlPlaneList.
func (in *IstioControlPlaneList) DeepCopy() *IstioControlPlaneList {
	if in == nil {
		return nil
	}
	out := new(IstioControlPlaneList)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IstioControlPlaneList) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}
<commit_msg>Revert DeepCopy to use un/marshal (#327)<commit_after>// Copyright 2019 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1alpha2

import (
	"github.com/gogo/protobuf/proto"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"

	"istio.io/operator/pkg/util"
	"istio.io/pkg/log"
)

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IstioControlPlane) DeepCopyInto(out proto.Message) {
	inj, err := util.MarshalWithJSONPB(in)
	if err != nil {
		log.Error(err.Error())
		return
	}
	if err := util.UnmarshalWithJSONPB(inj, out); err != nil {
		log.Error(err.Error())
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioControlPlane.
func (in *IstioControlPlane) DeepCopy() *IstioControlPlane {
	if in == nil {
		return nil
	}
	out := new(IstioControlPlane)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IstioControlPlane) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// TODO: needs to be implemented or generated.
func (in *IstioControlPlane) GetObjectKind() schema.ObjectKind {
	return EmptyObjectKind
}

// EmptyObjectKind implements the ObjectKind interface as a noop
var EmptyObjectKind = emptyObjectKind{}

type emptyObjectKind struct{}

// SetGroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) SetGroupVersionKind(gvk schema.GroupVersionKind) {}

// GroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) GroupVersionKind() schema.GroupVersionKind { return schema.GroupVersionKind{} }

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IstioControlPlaneList) DeepCopyInto(out *IstioControlPlaneList) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	out.ListMeta = in.ListMeta
	if in.Items != nil {
		in, out := &in.Items, &out.Items
		*out = make([]IstioControlPlane, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioControlPlaneList.
func (in *IstioControlPlaneList) DeepCopy() *IstioControlPlaneList {
	if in == nil {
		return nil
	}
	out := new(IstioControlPlaneList)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IstioControlPlaneList) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}
<|endoftext|>


<commit_before>package describe

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/openshift/origin/pkg/api/graph"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kctl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"

	"github.com/openshift/origin/pkg/client"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
)

// DeploymentConfigDescriber generates information about a DeploymentConfig
type DeploymentConfigDescriber struct {
	client deploymentDescriberClient
}

type deploymentDescriberClient interface {
	getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error)
	getDeployment(namespace, name string) (*kapi.ReplicationController, error)
	listPods(namespace string, selector labels.Selector) (*kapi.PodList, error)
	listEvents(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error)
}

type genericDeploymentDescriberClient struct {
	getDeploymentConfigFunc func(namespace, name string) (*deployapi.DeploymentConfig, error)
	getDeploymentFunc       func(namespace, name string) (*kapi.ReplicationController, error)
	listPodsFunc            func(namespace string, selector labels.Selector) (*kapi.PodList, error)
	listEventsFunc          func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error)
}

func (c *genericDeploymentDescriberClient) getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) {
	return c.getDeploymentConfigFunc(namespace, name)
}

func (c *genericDeploymentDescriberClient) getDeployment(namespace, name string) (*kapi.ReplicationController, error) {
	return c.getDeploymentFunc(namespace, name)
}

func (c *genericDeploymentDescriberClient) listPods(namespace string, selector labels.Selector) (*kapi.PodList, error) {
	return c.listPodsFunc(namespace, selector)
}

func (c *genericDeploymentDescriberClient) listEvents(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
	return c.listEventsFunc(deploymentConfig)
}

// NewDeploymentConfigDescriberForConfig returns a new DeploymentConfigDescriber
// for a DeploymentConfig
func NewDeploymentConfigDescriberForConfig(config *deployapi.DeploymentConfig) *DeploymentConfigDescriber {
	return &DeploymentConfigDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return config, nil
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return nil, kerrors.NewNotFound("ReplicatonController", name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return nil, kerrors.NewNotFound("PodList", fmt.Sprintf("%v", selector))
			},
		},
	}
}

// NewDeploymentConfigDescriber returns a new DeploymentConfigDescriber
func NewDeploymentConfigDescriber(client client.Interface, kclient kclient.Interface) *DeploymentConfigDescriber {
	return &DeploymentConfigDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return client.DeploymentConfigs(namespace).Get(name)
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return kclient.ReplicationControllers(namespace).Get(name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return kclient.Pods(namespace).List(selector)
			},
			listEventsFunc: func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
				return kclient.Events(deploymentConfig.Namespace).Search(deploymentConfig)
			},
		},
	}
}

// Describe returns a description of a DeploymentConfigDescriber
func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, error) {
	deploymentConfig, err := d.client.getDeploymentConfig(namespace, name)
	if err != nil {
		return "", err
	}
	events, err := d.client.listEvents(deploymentConfig)
	if err != nil {
		return "", err
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		formatMeta(out, deploymentConfig.ObjectMeta)

		if deploymentConfig.LatestVersion == 0 {
			formatString(out, "Latest Version", "Not deployed")
		} else {
			formatString(out, "Latest Version", strconv.Itoa(deploymentConfig.LatestVersion))
		}

		printTriggers(deploymentConfig.Triggers, out)

		formatString(out, "Strategy", deploymentConfig.Template.Strategy.Type)
		printStrategy(deploymentConfig.Template.Strategy, out)
		printReplicationControllerSpec(deploymentConfig.Template.ControllerTemplate, out)

		deploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig)
		deployment, err := d.client.getDeployment(namespace, deploymentName)
		if err != nil {
			if kerrors.IsNotFound(err) {
				formatString(out, "Latest Deployment", "<none>")
			} else {
				formatString(out, "Latest Deployment", fmt.Sprintf("error: %v", err))
			}
		} else {
			printDeploymentRc(deployment, d.client, out)
		}

		if events != nil {
			kctl.DescribeEvents(events, out)
		}
		return nil
	})
}

func printStrategy(strategy deployapi.DeploymentStrategy, w *tabwriter.Writer) {
	switch strategy.Type {
	case deployapi.DeploymentStrategyTypeRecreate:
		if strategy.RecreateParams != nil {
			pre := strategy.RecreateParams.Pre
			post := strategy.RecreateParams.Post
			if pre != nil {
				printHook("Pre-deployment", pre, w)
			}
			if post != nil {
				printHook("Post-deployment", post, w)
			}
		}
	case deployapi.DeploymentStrategyTypeCustom:
		fmt.Fprintf(w, "\t  Image:\t%s\n", strategy.CustomParams.Image)

		if len(strategy.CustomParams.Environment) > 0 {
			fmt.Fprintf(w, "\t  Environment:\t%s\n", formatLabels(convertEnv(strategy.CustomParams.Environment)))
		}

		if len(strategy.CustomParams.Command) > 0 {
			fmt.Fprintf(w, "\t  Command:\t%v\n", strings.Join(strategy.CustomParams.Command, " "))
		}
	}
}

func printHook(prefix string, hook *deployapi.LifecycleHook, w io.Writer) {
	if hook.ExecNewPod != nil {
		fmt.Fprintf(w, "\t  %s hook (pod type, failure policy: %s)\n", prefix, hook.FailurePolicy)
		fmt.Fprintf(w, "\t    Container:\t%s\n", hook.ExecNewPod.ContainerName)
		fmt.Fprintf(w, "\t    Command:\t%v\n", strings.Join(hook.ExecNewPod.Command, " "))
		fmt.Fprintf(w, "\t    Env:\t%s\n", formatLabels(convertEnv(hook.ExecNewPod.Env)))
	}
}

func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w *tabwriter.Writer) {
	if len(triggers) == 0 {
		formatString(w, "Triggers", "<none>")
		return
	}

	labels := []string{}

	for _, t := range triggers {
		switch t.Type {
		case deployapi.DeploymentTriggerOnConfigChange:
			labels = append(labels, "Config")
		case deployapi.DeploymentTriggerOnImageChange:
			if len(t.ImageChangeParams.RepositoryName) > 0 {
				labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.RepositoryName, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
			} else if len(t.ImageChangeParams.From.Name) > 0 {
				labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.From.Name, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
			}
		}
	}

	desc := strings.Join(labels, ", ")
	formatString(w, "Triggers", desc)
}

func printReplicationControllerSpec(spec kapi.ReplicationControllerSpec, w io.Writer) error {
	fmt.Fprint(w, "Template:\n")

	fmt.Fprintf(w, "\tSelector:\t%s\n\tReplicas:\t%d\n",
		formatLabels(spec.Selector),
		spec.Replicas)

	fmt.Fprintf(w, "\tContainers:\n\t\tNAME\tIMAGE\tENV\n")
	for _, container := range spec.Template.Spec.Containers {
		fmt.Fprintf(w, "\t\t%s\t%s\t%s\n",
			container.Name,
			container.Image,
			formatLabels(convertEnv(container.Env)))
	}
	return nil
}

func printDeploymentRc(deployment *kapi.ReplicationController, client deploymentDescriberClient, w io.Writer) error {
	running, waiting, succeeded, failed, err := getPodStatusForDeployment(deployment, client)
	if err != nil {
		return err
	}

	fmt.Fprint(w, "Latest Deployment:\n")
	fmt.Fprintf(w, "\tName:\t%s\n", deployment.Name)
	fmt.Fprintf(w, "\tStatus:\t%s\n", deployment.Annotations[deployapi.DeploymentStatusAnnotation])
	fmt.Fprintf(w, "\tSelector:\t%s\n", formatLabels(deployment.Spec.Selector))
	fmt.Fprintf(w, "\tLabels:\t%s\n", formatLabels(deployment.Labels))
	fmt.Fprintf(w, "\tReplicas:\t%d current / %d desired\n", deployment.Status.Replicas, deployment.Spec.Replicas)
	fmt.Fprintf(w, "\tPods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)

	return nil
}

func getPodStatusForDeployment(deployment *kapi.ReplicationController, client deploymentDescriberClient) (running, waiting, succeeded, failed int, err error) {
	rcPods, err := client.listPods(deployment.Namespace, labels.SelectorFromSet(deployment.Spec.Selector))
	if err != nil {
		return
	}
	for _, pod := range rcPods.Items {
		switch pod.Status.Phase {
		case kapi.PodRunning:
			running++
		case kapi.PodPending:
			waiting++
		case kapi.PodSucceeded:
			succeeded++
		case kapi.PodFailed:
			failed++
		}
	}
	return
}

type LatestDeploymentDescriber struct {
	client deploymentDescriberClient
}

func NewLatestDeploymentDescriber(client client.Interface, kclient kclient.Interface) *LatestDeploymentDescriber {
	return &LatestDeploymentDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return client.DeploymentConfigs(namespace).Get(name)
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return kclient.ReplicationControllers(namespace).Get(name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return kclient.Pods(namespace).List(selector)
			},
			listEventsFunc: func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
				return kclient.Events(deploymentConfig.Namespace).Search(deploymentConfig)
			},
		},
	}
}

func (d *LatestDeploymentDescriber) Describe(namespace, name string) (string, error) {
	config, err := d.client.getDeploymentConfig(namespace, name)
	if err != nil {
		return "", err
	}

	deploymentName := deployutil.LatestDeploymentNameForConfig(config)
	deployment, err := d.client.getDeployment(config.Namespace, deploymentName)
	if err != nil && !kerrors.IsNotFound(err) {
		return "", err
	}

	g := graph.New()
	deploy := graph.DeploymentConfig(g, config)
	if deployment != nil {
		graph.JoinDeployments(deploy.(*graph.DeploymentConfigNode), []kapi.ReplicationController{*deployment})
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		indent := "  "
		fmt.Fprintf(out, "Latest deployment for %s/%s:\n", name, namespace)
		printLines(out, indent, 1, d.describeDeployment(deploy.(*graph.DeploymentConfigNode))...)
		return nil
	})
}

func (d *LatestDeploymentDescriber) describeDeployment(node *graph.DeploymentConfigNode) []string {
	if node == nil {
		return nil
	}
	out := []string{}

	if node.ActiveDeployment == nil {
		on, auto := describeDeploymentConfigTriggers(node.DeploymentConfig)
		if node.DeploymentConfig.LatestVersion == 0 {
			out = append(out, fmt.Sprintf("#1 waiting %s. Run osc deploy --latest to deploy now.", on))
		} else if auto {
			out = append(out, fmt.Sprintf("#%d pending %s. Run osc deploy --latest to deploy now.", node.DeploymentConfig.LatestVersion, on))
		}
		// TODO: detect new image available?
	} else {
		out = append(out, d.describeDeploymentStatus(node.ActiveDeployment))
	}
	return out
}

func (d *LatestDeploymentDescriber) describeDeploymentStatus(deploy *kapi.ReplicationController) string {
	timeAt := strings.ToLower(formatRelativeTime(deploy.CreationTimestamp.Time))
	switch s := deploy.Annotations[deployapi.DeploymentStatusAnnotation]; deployapi.DeploymentStatus(s) {
	case deployapi.DeploymentStatusFailed:
		// TODO: encode fail time in the rc
		return fmt.Sprintf("#%s failed %s ago. You can restart this deployment with osc deploy --retry.", deploy.Annotations[deployapi.DeploymentVersionAnnotation], timeAt)
	case deployapi.DeploymentStatusComplete:
		// TODO: pod status output
		return fmt.Sprintf("#%s deployed %s ago", deploy.Annotations[deployapi.DeploymentVersionAnnotation], timeAt)
	default:
		return fmt.Sprintf("#%s deployment %s %s ago", deploy.Annotations[deployapi.DeploymentVersionAnnotation], strings.ToLower(s), timeAt)
	}
}

// DeploymentDescriber generates information about a deployment
// DEPRECATED.
type DeploymentDescriber struct {
	client.Interface
}

// Describe returns a description of a DeploymentDescriber
func (d *DeploymentDescriber) Describe(namespace, name string) (string, error) {
	c := d.Deployments(namespace)
	deployment, err := c.Get(name)
	if err != nil {
		return "", err
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		formatMeta(out, deployment.ObjectMeta)
		formatString(out, "Status", bold(deployment.Status))
		formatString(out, "Strategy", deployment.Strategy.Type)
		causes := []string{}
		if deployment.Details != nil {
			for _, c := range deployment.Details.Causes {
				causes = append(causes, string(c.Type))
			}
		}
		formatString(out, "Causes", strings.Join(causes, ","))
		return nil
	})
}
<commit_msg>Fix minor deploy cmd typo<commit_after>package describe

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/openshift/origin/pkg/api/graph"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kctl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"

	"github.com/openshift/origin/pkg/client"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
)

// DeploymentConfigDescriber generates information about a DeploymentConfig
type DeploymentConfigDescriber struct {
	client deploymentDescriberClient
}

type deploymentDescriberClient interface {
	getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error)
	getDeployment(namespace, name string) (*kapi.ReplicationController, error)
	listPods(namespace string, selector labels.Selector) (*kapi.PodList, error)
	listEvents(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error)
}

type genericDeploymentDescriberClient struct {
	getDeploymentConfigFunc func(namespace, name string) (*deployapi.DeploymentConfig, error)
	getDeploymentFunc       func(namespace, name string) (*kapi.ReplicationController, error)
	listPodsFunc            func(namespace string, selector labels.Selector) (*kapi.PodList, error)
	listEventsFunc          func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error)
}

func (c *genericDeploymentDescriberClient) getDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) {
	return c.getDeploymentConfigFunc(namespace, name)
}

func (c *genericDeploymentDescriberClient) getDeployment(namespace, name string) (*kapi.ReplicationController, error) {
	return c.getDeploymentFunc(namespace, name)
}

func (c *genericDeploymentDescriberClient) listPods(namespace string, selector labels.Selector) (*kapi.PodList, error) {
	return c.listPodsFunc(namespace, selector)
}

func (c *genericDeploymentDescriberClient) listEvents(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
	return c.listEventsFunc(deploymentConfig)
}

// NewDeploymentConfigDescriberForConfig returns a new DeploymentConfigDescriber
// for a DeploymentConfig
func NewDeploymentConfigDescriberForConfig(config *deployapi.DeploymentConfig) *DeploymentConfigDescriber {
	return &DeploymentConfigDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return config, nil
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return nil, kerrors.NewNotFound("ReplicatonController", name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return nil, kerrors.NewNotFound("PodList", fmt.Sprintf("%v", selector))
			},
		},
	}
}

// NewDeploymentConfigDescriber returns a new DeploymentConfigDescriber
func NewDeploymentConfigDescriber(client client.Interface, kclient kclient.Interface) *DeploymentConfigDescriber {
	return &DeploymentConfigDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return client.DeploymentConfigs(namespace).Get(name)
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return kclient.ReplicationControllers(namespace).Get(name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return kclient.Pods(namespace).List(selector)
			},
			listEventsFunc: func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
				return kclient.Events(deploymentConfig.Namespace).Search(deploymentConfig)
			},
		},
	}
}

// Describe returns a description of a DeploymentConfigDescriber
func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, error) {
	deploymentConfig, err := d.client.getDeploymentConfig(namespace, name)
	if err != nil {
		return "", err
	}
	events, err := d.client.listEvents(deploymentConfig)
	if err != nil {
		return "", err
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		formatMeta(out, deploymentConfig.ObjectMeta)

		if deploymentConfig.LatestVersion == 0 {
			formatString(out, "Latest Version", "Not deployed")
		} else {
			formatString(out, "Latest Version", strconv.Itoa(deploymentConfig.LatestVersion))
		}

		printTriggers(deploymentConfig.Triggers, out)

		formatString(out, "Strategy", deploymentConfig.Template.Strategy.Type)
		printStrategy(deploymentConfig.Template.Strategy, out)
		printReplicationControllerSpec(deploymentConfig.Template.ControllerTemplate, out)

		deploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig)
		deployment, err := d.client.getDeployment(namespace, deploymentName)
		if err != nil {
			if kerrors.IsNotFound(err) {
				formatString(out, "Latest Deployment", "<none>")
			} else {
				formatString(out, "Latest Deployment", fmt.Sprintf("error: %v", err))
			}
		} else {
			printDeploymentRc(deployment, d.client, out)
		}

		if events != nil {
			kctl.DescribeEvents(events, out)
		}
		return nil
	})
}

func printStrategy(strategy deployapi.DeploymentStrategy, w *tabwriter.Writer) {
	switch strategy.Type {
	case deployapi.DeploymentStrategyTypeRecreate:
		if strategy.RecreateParams != nil {
			pre := strategy.RecreateParams.Pre
			post := strategy.RecreateParams.Post
			if pre != nil {
				printHook("Pre-deployment", pre, w)
			}
			if post != nil {
				printHook("Post-deployment", post, w)
			}
		}
	case deployapi.DeploymentStrategyTypeCustom:
		fmt.Fprintf(w, "\t  Image:\t%s\n", strategy.CustomParams.Image)

		if len(strategy.CustomParams.Environment) > 0 {
			fmt.Fprintf(w, "\t  Environment:\t%s\n", formatLabels(convertEnv(strategy.CustomParams.Environment)))
		}

		if len(strategy.CustomParams.Command) > 0 {
			fmt.Fprintf(w, "\t  Command:\t%v\n", strings.Join(strategy.CustomParams.Command, " "))
		}
	}
}

func printHook(prefix string, hook *deployapi.LifecycleHook, w io.Writer) {
	if hook.ExecNewPod != nil {
		fmt.Fprintf(w, "\t  %s hook (pod type, failure policy: %s)\n", prefix, hook.FailurePolicy)
		fmt.Fprintf(w, "\t    Container:\t%s\n", hook.ExecNewPod.ContainerName)
		fmt.Fprintf(w, "\t    Command:\t%v\n", strings.Join(hook.ExecNewPod.Command, " "))
		fmt.Fprintf(w, "\t    Env:\t%s\n", formatLabels(convertEnv(hook.ExecNewPod.Env)))
	}
}

func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w *tabwriter.Writer) {
	if len(triggers) == 0 {
		formatString(w, "Triggers", "<none>")
		return
	}

	labels := []string{}

	for _, t := range triggers {
		switch t.Type {
		case deployapi.DeploymentTriggerOnConfigChange:
			labels = append(labels, "Config")
		case deployapi.DeploymentTriggerOnImageChange:
			if len(t.ImageChangeParams.RepositoryName) > 0 {
				labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.RepositoryName, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
			} else if len(t.ImageChangeParams.From.Name) > 0 {
				labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", t.ImageChangeParams.From.Name, t.ImageChangeParams.Tag, t.ImageChangeParams.Automatic))
			}
		}
	}

	desc := strings.Join(labels, ", ")
	formatString(w, "Triggers", desc)
}

func printReplicationControllerSpec(spec kapi.ReplicationControllerSpec, w io.Writer) error {
	fmt.Fprint(w, "Template:\n")

	fmt.Fprintf(w, "\tSelector:\t%s\n\tReplicas:\t%d\n",
		formatLabels(spec.Selector),
		spec.Replicas)

	fmt.Fprintf(w, "\tContainers:\n\t\tNAME\tIMAGE\tENV\n")
	for _, container := range spec.Template.Spec.Containers {
		fmt.Fprintf(w, "\t\t%s\t%s\t%s\n",
			container.Name,
			container.Image,
			formatLabels(convertEnv(container.Env)))
	}
	return nil
}

func printDeploymentRc(deployment *kapi.ReplicationController, client deploymentDescriberClient, w io.Writer) error {
	running, waiting, succeeded, failed, err := getPodStatusForDeployment(deployment, client)
	if err != nil {
		return err
	}

	fmt.Fprint(w, "Latest Deployment:\n")
	fmt.Fprintf(w, "\tName:\t%s\n", deployment.Name)
	fmt.Fprintf(w, "\tStatus:\t%s\n", deployment.Annotations[deployapi.DeploymentStatusAnnotation])
	fmt.Fprintf(w, "\tSelector:\t%s\n", formatLabels(deployment.Spec.Selector))
	fmt.Fprintf(w, "\tLabels:\t%s\n", formatLabels(deployment.Labels))
	fmt.Fprintf(w, "\tReplicas:\t%d current / %d desired\n", deployment.Status.Replicas, deployment.Spec.Replicas)
	fmt.Fprintf(w, "\tPods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)

	return nil
}

func getPodStatusForDeployment(deployment *kapi.ReplicationController, client deploymentDescriberClient) (running, waiting, succeeded, failed int, err error) {
	rcPods, err := client.listPods(deployment.Namespace, labels.SelectorFromSet(deployment.Spec.Selector))
	if err != nil {
		return
	}
	for _, pod := range rcPods.Items {
		switch pod.Status.Phase {
		case kapi.PodRunning:
			running++
		case kapi.PodPending:
			waiting++
		case kapi.PodSucceeded:
			succeeded++
		case kapi.PodFailed:
			failed++
		}
	}
	return
}

type LatestDeploymentDescriber struct {
	client deploymentDescriberClient
}

func NewLatestDeploymentDescriber(client client.Interface, kclient kclient.Interface) *LatestDeploymentDescriber {
	return &LatestDeploymentDescriber{
		client: &genericDeploymentDescriberClient{
			getDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) {
				return client.DeploymentConfigs(namespace).Get(name)
			},
			getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return kclient.ReplicationControllers(namespace).Get(name)
			},
			listPodsFunc: func(namespace string, selector labels.Selector) (*kapi.PodList, error) {
				return kclient.Pods(namespace).List(selector)
			},
			listEventsFunc: func(deploymentConfig *deployapi.DeploymentConfig) (*kapi.EventList, error) {
				return kclient.Events(deploymentConfig.Namespace).Search(deploymentConfig)
			},
		},
	}
}

func (d *LatestDeploymentDescriber) Describe(namespace, name string) (string, error) {
	config, err := d.client.getDeploymentConfig(namespace, name)
	if err != nil {
		return "", err
	}

	deploymentName := deployutil.LatestDeploymentNameForConfig(config)
	deployment, err := d.client.getDeployment(config.Namespace, deploymentName)
	if err != nil && !kerrors.IsNotFound(err) {
		return "", err
	}

	g := graph.New()
	deploy := graph.DeploymentConfig(g, config)
	if deployment != nil {
		graph.JoinDeployments(deploy.(*graph.DeploymentConfigNode), []kapi.ReplicationController{*deployment})
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		indent := "  "
		fmt.Fprintf(out, "Latest deployment for %s/%s:\n", namespace, name)
		printLines(out, indent, 1, d.describeDeployment(deploy.(*graph.DeploymentConfigNode))...)
		return nil
	})
}

func (d *LatestDeploymentDescriber) describeDeployment(node *graph.DeploymentConfigNode) []string {
	if node == nil {
		return nil
	}
	out := []string{}

	if node.ActiveDeployment == nil {
		on, auto := describeDeploymentConfigTriggers(node.DeploymentConfig)
		if node.DeploymentConfig.LatestVersion == 0 {
			out = append(out, fmt.Sprintf("#1 waiting %s. Run osc deploy --latest to deploy now.", on))
		} else if auto {
			out = append(out, fmt.Sprintf("#%d pending %s. Run osc deploy --latest to deploy now.", node.DeploymentConfig.LatestVersion, on))
		}
		// TODO: detect new image available?
	} else {
		out = append(out, d.describeDeploymentStatus(node.ActiveDeployment))
	}
	return out
}

func (d *LatestDeploymentDescriber) describeDeploymentStatus(deploy *kapi.ReplicationController) string {
	timeAt := strings.ToLower(formatRelativeTime(deploy.CreationTimestamp.Time))
	switch s := deploy.Annotations[deployapi.DeploymentStatusAnnotation]; deployapi.DeploymentStatus(s) {
	case deployapi.DeploymentStatusFailed:
		// TODO: encode fail time in the rc
		return fmt.Sprintf("#%s failed %s ago. You can restart this deployment with osc deploy --retry.", deploy.Annotations[deployapi.DeploymentVersionAnnotation], timeAt)
	case deployapi.DeploymentStatusComplete:
		// TODO: pod status output
		return fmt.Sprintf("#%s deployed %s ago", deploy.Annotations[deployapi.DeploymentVersionAnnotation], timeAt)
	default:
		return fmt.Sprintf("#%s deployment %s %s ago", deploy.Annotations[deployapi.DeploymentVersionAnnotation], strings.ToLower(s), timeAt)
	}
}

// DeploymentDescriber generates information about a deployment
// DEPRECATED.
type DeploymentDescriber struct {
	client.Interface
}

// Describe returns a description of a DeploymentDescriber
func (d *DeploymentDescriber) Describe(namespace, name string) (string, error) {
	c := d.Deployments(namespace)
	deployment, err := c.Get(name)
	if err != nil {
		return "", err
	}

	return tabbedString(func(out *tabwriter.Writer) error {
		formatMeta(out, deployment.ObjectMeta)
		formatString(out, "Status", bold(deployment.Status))
		formatString(out, "Strategy", deployment.Strategy.Type)
		causes := []string{}
		if deployment.Details != nil {
			for _, c := range deployment.Details.Causes {
				causes = append(causes, string(c.Type))
			}
		}
		formatString(out, "Causes", strings.Join(causes, ","))
		return nil
	})
}
<|endoftext|>