<commit_before>/*
 * Pretty command-line password manager.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import "fmt"
import "log"
import "os"
import "regexp"
import "syscall"
import "time"

import "code.google.com/p/go.crypto/ssh/terminal"
import "github.com/docopt/docopt-go"

import "db"
import "gpg"
import "util"

func main() {
	log.SetFlags(0);

	usage := `safely [options]

Usage:
  safely [options] --keys <keys> --create
  safely [options] --keys <keys> --add <name>
  safely [options] --user <name>
  safely [options] --pass <name>
  safely [options] --keys <keys> --edit <name>
  safely [options] --keys <keys> --remove <name>
  safely [options] --search <query>
  safely [options] --dump

Options:
  -c, --create                  Create a new password database.
  -a <name>, --add <name>       Add a new account to the database.
  -u <name>, --user <name>      Print the user name of the given account.
  -p <name>, --pass <name>      Print the password of the given account.
  -e <name>, --edit <name>      Modify the given account.
  -r <name>, --remove <name>    Remove the given account.
  -s <query>, --search <query>  Search the database for the given query.
  -d, --dump                    Dump the database in JSON format.
  -D <file>, --db <file>        Use this database file [default: ~/.config/safely/passwords.db].
  -K <keys>, --keys <keys>      Use these space-separated GPG keys [default: ].
  -F, --fuzzy                   Enable non-exact (fuzzy) matches.
  -N, --print-newline           Force printing a trailing newline.
  -Q, --quiet                   Quiet output.
  -B, --no-backup               Do not create a backup of the database.
  -h, --help                    Show the program's help message and exit.`

	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		log.Fatal("Invalid arguments: ", err);
	}

	db_file   := util.ExpandUser(args["--db"].(string));
	keys_spec := args["--keys"].(string);

	fuzzy     := args["--fuzzy"].(bool);
	quiet     := args["--quiet"].(bool);
	no_backup := args["--no-backup"].(bool);

	SecurityCheck();

	gpg.Init(keys_spec);

	switch {
		case args["--create"].(bool) == true:
			mydb, err := db.Create(db_file);
			if err != nil {
				log.Fatal("Error creating database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					mydb.Remove();
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Database '%s' created",
					           db_file);
				}
			}();

			defer mydb.DeferredSync(false);

			break;

		case args["--add"] != nil:
			account := args["--add"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' added",
					           account);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			if mydb.Search(account, false) != nil {
				log.Fatalf("Account '%s' already exists",
				           account);
			}

			user := util.ReadLine(fmt.Sprintf(
				"Enter user name for '%s': ", account,
			));

			pass := util.ReadPass(fmt.Sprintf(
				"Enter password for '%s': ", account,
			));

			mydb.Accounts[account] = &db.Account{
				user, pass, time.Now().Format(time.RFC3339),
			};

			break;

		case args["--user"] != nil:
			query := args["--user"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			account := mydb.Search(query, fuzzy);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			fmt.Print(account.User);

			if args["--print-newline"].(bool) || terminal.IsTerminal(int(os.Stdout.Fd())) {
				fmt.Println("");
			}

			break;

		case args["--pass"] != nil:
			query := args["--pass"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			account := mydb.Search(query, fuzzy);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			fmt.Print(account.Pass);

			if args["--print-newline"].(bool) || terminal.IsTerminal(int(os.Stdout.Fd())) {
				fmt.Println("");
			}

			break;

		case args["--edit"] != nil:
			query := args["--edit"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' edited",
					           query);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			account := mydb.Search(query, false);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			user := util.ReadLine(fmt.Sprintf(
				"Enter new user name for '%s' [%s]: ",
				query, account.User,
			));

			pass := util.ReadPass(fmt.Sprintf(
				"Enter new password for '%s' [%s]: ",
				query, account.Pass,
			));

			mydb.Accounts[query] = &db.Account{
				user, pass, time.Now().Format(time.RFC3339),
			};

			break;

		case args["--remove"] != nil:
			account := args["--remove"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' removed",
					           account);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			if mydb.Search(account, false) == nil {
				log.Fatalf("Account '%s' not found", account);
			}

			delete(mydb.Accounts, account);

			break;

		case args["--search"] != nil:
			query := args["--search"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			for name, _ := range mydb.Accounts {
				if m, _ := regexp.MatchString(query, name); m {
					fmt.Println(name);
				}
			}

			break;

		case args["--dump"].(bool) == true:
			plain, err := db.Dump(db_file);
			if err != nil {
				log.Fatal("Error dumping database: ", err);
			}

			fmt.Printf("%s\n", plain);

			break;

		default:
			log.Fatal("Invalid command");
			break;
	}
}

func SecurityCheck() {
	var lim syscall.Rlimit;

	if os.Getuid() == 0 || os.Getgid() == 0 {
		log.Fatal("Running as root");
	}

	err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim);
	if err != nil {
		log.Fatal("Could not get rlimit: ", err);
	}

	if lim.Cur != 0 {
		log.Fatal("Core dumps are enabled");
	}
}
<commit_msg>safely: fix default values when editing account<commit_after>/*
 * Pretty command-line password manager.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import "fmt"
import "log"
import "os"
import "regexp"
import "syscall"
import "time"

import "code.google.com/p/go.crypto/ssh/terminal"
import "github.com/docopt/docopt-go"

import "db"
import "gpg"
import "util"

func main() {
	log.SetFlags(0);

	usage := `safely [options]

Usage:
  safely [options] --keys <keys> --create
  safely [options] --keys <keys> --add <name>
  safely [options] --user <name>
  safely [options] --pass <name>
  safely [options] --keys <keys> --edit <name>
  safely [options] --keys <keys> --remove <name>
  safely [options] --search <query>
  safely [options] --dump

Options:
  -c, --create                  Create a new password database.
  -a <name>, --add <name>       Add a new account to the database.
  -u <name>, --user <name>      Print the user name of the given account.
  -p <name>, --pass <name>      Print the password of the given account.
  -e <name>, --edit <name>      Modify the given account.
  -r <name>, --remove <name>    Remove the given account.
  -s <query>, --search <query>  Search the database for the given query.
  -d, --dump                    Dump the database in JSON format.
  -D <file>, --db <file>        Use this database file [default: ~/.config/safely/passwords.db].
  -K <keys>, --keys <keys>      Use these space-separated GPG keys [default: ].
  -F, --fuzzy                   Enable non-exact (fuzzy) matches.
  -N, --print-newline           Force printing a trailing newline.
  -Q, --quiet                   Quiet output.
  -B, --no-backup               Do not create a backup of the database.
  -h, --help                    Show the program's help message and exit.`

	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		log.Fatal("Invalid arguments: ", err);
	}

	db_file   := util.ExpandUser(args["--db"].(string));
	keys_spec := args["--keys"].(string);

	fuzzy     := args["--fuzzy"].(bool);
	quiet     := args["--quiet"].(bool);
	no_backup := args["--no-backup"].(bool);

	SecurityCheck();

	gpg.Init(keys_spec);

	switch {
		case args["--create"].(bool) == true:
			mydb, err := db.Create(db_file);
			if err != nil {
				log.Fatal("Error creating database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					mydb.Remove();
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Database '%s' created",
					           db_file);
				}
			}();

			defer mydb.DeferredSync(false);

			break;

		case args["--add"] != nil:
			account := args["--add"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' added",
					           account);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			if mydb.Search(account, false) != nil {
				log.Fatalf("Account '%s' already exists",
				           account);
			}

			user := util.ReadLine(fmt.Sprintf(
				"Enter user name for '%s': ", account,
			));

			pass := util.ReadPass(fmt.Sprintf(
				"Enter password for '%s': ", account,
			));

			mydb.Accounts[account] = &db.Account{
				user, pass, time.Now().Format(time.RFC3339),
			};

			break;

		case args["--user"] != nil:
			query := args["--user"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			account := mydb.Search(query, fuzzy);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			fmt.Print(account.User);

			if args["--print-newline"].(bool) || terminal.IsTerminal(int(os.Stdout.Fd())) {
				fmt.Println("");
			}

			break;

		case args["--pass"] != nil:
			query := args["--pass"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			account := mydb.Search(query, fuzzy);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			fmt.Print(account.Pass);

			if args["--print-newline"].(bool) || terminal.IsTerminal(int(os.Stdout.Fd())) {
				fmt.Println("");
			}

			break;

		case args["--edit"] != nil:
			query := args["--edit"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' edited",
					           query);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			account := mydb.Search(query, false);
			if account == nil {
				log.Fatalf("Account '%s' not found", query);
			}

			user := util.ReadLine(fmt.Sprintf(
				"Enter new user name for '%s' [%s]: ",
				query, account.User,
			));

			if user == "" {
				user = account.User;
			}

			pass := util.ReadPass(fmt.Sprintf(
				"Enter new password for '%s' [%s]: ",
				query, account.Pass,
			));

			if pass == "" {
				pass = account.Pass;
			}

			mydb.Accounts[query] = &db.Account{
				user, pass, time.Now().Format(time.RFC3339),
			};

			break;

		case args["--remove"] != nil:
			account := args["--remove"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			defer func() {
				if r := recover(); r != nil {
					os.Exit(1);
				}

				if quiet != true {
					log.Printf("Account '%s' removed",
					           account);
				}
			}();

			defer mydb.DeferredSync(!no_backup);

			if mydb.Search(account, false) == nil {
				log.Fatalf("Account '%s' not found", account);
			}

			delete(mydb.Accounts, account);

			break;

		case args["--search"] != nil:
			query := args["--search"].(string);

			mydb, err := db.Open(db_file);
			if err != nil {
				log.Fatal("Error opening database: ", err);
			}

			for name, _ := range mydb.Accounts {
				if m, _ := regexp.MatchString(query, name); m {
					fmt.Println(name);
				}
			}

			break;

		case args["--dump"].(bool) == true:
			plain, err := db.Dump(db_file);
			if err != nil {
				log.Fatal("Error dumping database: ", err);
			}

			fmt.Printf("%s\n", plain);

			break;

		default:
			log.Fatal("Invalid command");
			break;
	}
}

func SecurityCheck() {
	var lim syscall.Rlimit;

	if os.Getuid() == 0 || os.Getgid() == 0 {
		log.Fatal("Running as root");
	}

	err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim);
	if err != nil {
		log.Fatal("Could not get rlimit: ", err);
	}

	if lim.Cur != 0 {
		log.Fatal("Core dumps are enabled");
	}
}
<|endoftext|>


<commit_before>package plugin

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"text/template"
)

var HELM_BIN = "/bin/helm"
var KUBECONFIG = "/root/.kube/kubeconfig"

type (
	// Config maps the params we need to run Helm
	Config struct {
		APIServer          string   `json:"api_server"`
		Token              string   `json:"token"`
		Certificate        string   `json:"certificate"`
		ServiceAccount     string   `json:"service_account"`
		KubeConfig         string   `json:"kube_config"`
		HelmCommand        string   `json:"helm_command"`
		SkipTLSVerify      bool     `json:"tls_skip_verify"`
		Namespace          string   `json:"namespace"`
		Release            string   `json:"release"`
		Chart              string   `json:"chart"`
		Version            string   `json:"version"`
		Values             string   `json:"values"`
		StringValues       string   `json:"string_values"`
		ValuesFiles        string   `json:"values_files"`
		Debug              bool     `json:"debug"`
		DryRun             bool     `json:"dry_run"`
		Secrets            []string `json:"secrets"`
		Prefix             string   `json:"prefix"`
		TillerNs           string   `json:"tiller_ns"`
		Wait               bool     `json:"wait"`
		RecreatePods       bool     `json:"recreate_pods"`
		Upgrade            bool     `json:"upgrade"`
		CanaryImage        bool     `json:"canary_image"`
		ClientOnly         bool     `json:"client_only"`
		ReuseValues        bool     `json:"reuse_values"`
		Timeout            string   `json:"timeout"`
		Force              bool     `json:"force"`
		HelmRepos          []string `json:"helm_repos"`
		Purge              bool     `json:"purge"`
		UpdateDependencies bool     `json:"update_dependencies"`
	}
	// Plugin default
	Plugin struct {
		Config  Config
		command []string
	}
)

func setHelpCommand(p *Plugin) {
	p.command = []string{""}
}
func setDeleteCommand(p *Plugin) {
	delete := make([]string, 2)
	delete[0] = "delete"
	delete[1] = p.Config.Release

	if p.Config.TillerNs != "" {
		delete = append(delete, "--tiller-namespace")
		delete = append(delete, p.Config.TillerNs)
	}
	if p.Config.DryRun {
		delete = append(delete, "--dry-run")
	}
	if p.Config.Purge {
		delete = append(delete, "--purge")
	}

	p.command = delete
}

func setUpgradeCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "upgrade"
	upgrade[1] = "--install"

	if p.Config.Release != "" {
		upgrade = append(upgrade, p.Config.Release)
	}
	upgrade = append(upgrade, p.Config.Chart)
	if p.Config.Version != "" {
		upgrade = append(upgrade, "--version")
		upgrade = append(upgrade, p.Config.Version)
	}
	if p.Config.Values != "" {
		upgrade = append(upgrade, "--set")
		upgrade = append(upgrade, unQuote(p.Config.Values))
	}
	if p.Config.StringValues != "" {
		upgrade = append(upgrade, "--set-string")
		upgrade = append(upgrade, unQuote(p.Config.StringValues))
	}
	if p.Config.ValuesFiles != "" {
		for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") {
			upgrade = append(upgrade, "--values")
			upgrade = append(upgrade, valuesFile)
		}
	}
	if p.Config.Namespace != "" {
		upgrade = append(upgrade, "--namespace")
		upgrade = append(upgrade, p.Config.Namespace)
	}
	if p.Config.TillerNs != "" {
		upgrade = append(upgrade, "--tiller-namespace")
		upgrade = append(upgrade, p.Config.TillerNs)
	}
	if p.Config.DryRun {
		upgrade = append(upgrade, "--dry-run")
	}
	if p.Config.Debug {
		upgrade = append(upgrade, "--debug")
	}
	if p.Config.Wait {
		upgrade = append(upgrade, "--wait")
	}
	if p.Config.RecreatePods {
		upgrade = append(upgrade, "--recreate-pods")
	}
	if p.Config.ReuseValues {
		upgrade = append(upgrade, "--reuse-values")
	}
	if p.Config.Timeout != "" {
		upgrade = append(upgrade, "--timeout")
		upgrade = append(upgrade, p.Config.Timeout)
	}
	if p.Config.Force {
		upgrade = append(upgrade, "--force")
	}
	p.command = upgrade

}

func setHelmCommand(p *Plugin) {

	switch p.Config.HelmCommand {
	case "upgrade":
		setUpgradeCommand(p)
	case "delete":
		setDeleteCommand(p)
	default:
		switch os.Getenv("DRONE_BUILD_EVENT") {
		case "push", "tag", "deployment", "pull_request":
			setUpgradeCommand(p)
		case "delete":
			setDeleteCommand(p)
		default:
			setHelpCommand(p)
		}
	}

}

var repoExp = regexp.MustCompile(`^(?P<name>[\w-]+)=(?P<url>(http|https)://[\w-./:]+)`)

// parseRepo returns map of regex capture groups (name, url)
func parseRepo(repo string) (map[string]string, error) {
	matches := repoExp.FindStringSubmatch(repo)
	if len(matches) < 1 {
		return nil, fmt.Errorf("Invalid repo definition: %s", repo)
	}
	result := make(map[string]string)
	for i, name := range repoExp.SubexpNames() {
		if i != 0 {
			result[name] = matches[i]
		}
	}
	return result, nil
}

func doHelmRepoAdd(repo string) ([]string, error) {
	repoMap, err := parseRepo(unQuote(repo))
	if err != nil {
		return nil, err
	}
	repoAdd := []string{
		"repo",
		"add",
		repoMap["name"],
		repoMap["url"],
	}
	return repoAdd, nil
}

func doHelmInit(p *Plugin) []string {
	init := make([]string, 1)
	init[0] = "init"
	if p.Config.TillerNs != "" {
		init = append(init, "--tiller-namespace")
		init = append(init, p.Config.TillerNs)
	}
	if p.Config.ClientOnly {
		init = append(init, "--client-only")
	}
	if p.Config.Upgrade {
		init = append(init, "--upgrade")
	}
	if p.Config.CanaryImage {
		init = append(init, "--canary-image")
	}

	return init

}

func doDependencyUpdate(chart string) []string {
	dependencyUpdate := []string{
		"dependency",
		"update",
		chart,
	}

	return dependencyUpdate
}

// Exec default method
func (p *Plugin) Exec() error {
	if p.Config.Debug {
		p.debugEnv()
	}

	// create /root/.kube/config file if not exists
	if _, err := os.Stat(p.Config.KubeConfig); os.IsNotExist(err) {
		resolveSecrets(p)
		if p.Config.APIServer == "" {
			return fmt.Errorf("Error: API Server is needed to deploy.")
		}
		if p.Config.Token == "" {
			return fmt.Errorf("Error: Token is needed to deploy.")
		}
		initialiseKubeconfig(&p.Config, KUBECONFIG, p.Config.KubeConfig)
	}

	if p.Config.Debug {
		p.debug()
	}

	init := doHelmInit(p)
	err := runCommand(init)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(init[:], " "))
	}

	if len(p.Config.HelmRepos) > 0 {
		for _, repo := range p.Config.HelmRepos {
			repoAdd, err := doHelmRepoAdd(repo)
			if err == nil {
				if p.Config.Debug {
					log.Println("adding helm repo: " + strings.Join(repoAdd[:], " "))
				}

				if err = runCommand(repoAdd); err != nil {
					return fmt.Errorf("Error adding helm repo: " + err.Error())
				}
			} else {
				return err
			}
		}
	}

	if p.Config.UpdateDependencies {
		if err = runCommand(doDependencyUpdate(p.Config.Chart)); err != nil {
			return fmt.Errorf("Error updating dependencies: " + err.Error())
		}
	}

	setHelmCommand(p)

	if p.Config.Debug {
		log.Println("helm command: " + strings.Join(p.command, " "))
	}

	err = runCommand(p.command)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(p.command[:], " "))
	}

	return nil
}

func initialiseKubeconfig(params *Config, source string, target string) error {
	f, err := os.Create(target)
	if err != nil {
		return err
	}
	defer f.Close()
	// parse template
	t, _ := template.ParseFiles(source)
	// execute template
	return t.Execute(f, params)
}

func runCommand(params []string) error {
	cmd := new(exec.Cmd)
	cmd = exec.Command(HELM_BIN, params...)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	return err
}

func resolveSecrets(p *Plugin) {
	p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix, p.Config.Debug)
	p.Config.StringValues = resolveEnvVar(p.Config.StringValues, p.Config.Prefix, p.Config.Debug)

	if p.Config.APIServer == "" {
		p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.Token == "" {
		p.Config.Token = resolveEnvVar("${KUBERNETES_TOKEN}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.Certificate == "" {
		p.Config.Certificate = resolveEnvVar("${KUBERNETES_CERTIFICATE}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.ServiceAccount == "" {
		p.Config.ServiceAccount = resolveEnvVar("${SERVICE_ACCOUNT}", p.Config.Prefix, p.Config.Debug)
		if p.Config.ServiceAccount == "" {
			p.Config.ServiceAccount = "helm"
		}
	}
}

// getEnvVars will return [${TAG} {TAG} TAG]
func getEnvVars(envvars string) [][]string {
	re := regexp.MustCompile(`\$(\{?(\w+)\}?)\.?`)
	extracted := re.FindAllStringSubmatch(envvars, -1)
	return extracted
}

func resolveEnvVar(key string, prefix string, debug bool) string {
	envvars := getEnvVars(key)
	return replaceEnvvars(envvars, prefix, key, debug)
}

func replaceEnvvars(envvars [][]string, prefix string, s string, debug bool) string {
	for _, envvar := range envvars {
		envvarName := envvar[0]
		envvarKey := envvar[2]
		prefixedKey := strings.ToUpper(prefix + "_" + envvarKey)
		envval := os.Getenv(prefixedKey)
		if debug {
			fmt.Printf("-ReplVar: %s => %s-- %s\n", prefixedKey, envvarKey, envval)
		}

		if envval == "" {
			envval = os.Getenv(envvarKey)
		}

		if strings.Contains(s, envvarName) {
			s = strings.Replace(s, envvarName, envval, -1)
		}
	}

	return s
}

// unQuote removes quotes if present
func unQuote(s string) string {
	unquoted, err := strconv.Unquote(s)
	if err != nil {
		// ignore error and return original string
		return s
	}
	return unquoted
}

func (p *Plugin) debugEnv() {
	// debug env vars
	for _, e := range os.Environ() {
		fmt.Println("-Var:--", e)
	}
}

func (p *Plugin) debug() {
	fmt.Println(p)
	// debug plugin obj
	fmt.Printf("Api server: %s \n", p.Config.APIServer)
	fmt.Printf("Values: %s \n", p.Config.Values)
	fmt.Printf("StringValues: %s \n", p.Config.StringValues)
	fmt.Printf("Secrets: %s \n", p.Config.Secrets)
	fmt.Printf("Helm Repos: %s \n", p.Config.HelmRepos)
	fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles)
	kubeconfig, err := ioutil.ReadFile(KUBECONFIG)
	if err == nil {
		fmt.Println(string(kubeconfig))
	}

	config, err := ioutil.ReadFile(p.Config.KubeConfig)
	if err == nil {
		fmt.Println(string(config))
	}
}
<commit_msg>adding promote and roolback to the upgrade event from drone 1.0 rc<commit_after>package plugin

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"text/template"
)

var HELM_BIN = "/bin/helm"
var KUBECONFIG = "/root/.kube/kubeconfig"

type (
	// Config maps the params we need to run Helm
	Config struct {
		APIServer          string   `json:"api_server"`
		Token              string   `json:"token"`
		Certificate        string   `json:"certificate"`
		ServiceAccount     string   `json:"service_account"`
		KubeConfig         string   `json:"kube_config"`
		HelmCommand        string   `json:"helm_command"`
		SkipTLSVerify      bool     `json:"tls_skip_verify"`
		Namespace          string   `json:"namespace"`
		Release            string   `json:"release"`
		Chart              string   `json:"chart"`
		Version            string   `json:"version"`
		Values             string   `json:"values"`
		StringValues       string   `json:"string_values"`
		ValuesFiles        string   `json:"values_files"`
		Debug              bool     `json:"debug"`
		DryRun             bool     `json:"dry_run"`
		Secrets            []string `json:"secrets"`
		Prefix             string   `json:"prefix"`
		TillerNs           string   `json:"tiller_ns"`
		Wait               bool     `json:"wait"`
		RecreatePods       bool     `json:"recreate_pods"`
		Upgrade            bool     `json:"upgrade"`
		CanaryImage        bool     `json:"canary_image"`
		ClientOnly         bool     `json:"client_only"`
		ReuseValues        bool     `json:"reuse_values"`
		Timeout            string   `json:"timeout"`
		Force              bool     `json:"force"`
		HelmRepos          []string `json:"helm_repos"`
		Purge              bool     `json:"purge"`
		UpdateDependencies bool     `json:"update_dependencies"`
	}
	// Plugin default
	Plugin struct {
		Config  Config
		command []string
	}
)

func setHelpCommand(p *Plugin) {
	p.command = []string{""}
}
func setDeleteCommand(p *Plugin) {
	delete := make([]string, 2)
	delete[0] = "delete"
	delete[1] = p.Config.Release

	if p.Config.TillerNs != "" {
		delete = append(delete, "--tiller-namespace")
		delete = append(delete, p.Config.TillerNs)
	}
	if p.Config.DryRun {
		delete = append(delete, "--dry-run")
	}
	if p.Config.Purge {
		delete = append(delete, "--purge")
	}

	p.command = delete
}

func setUpgradeCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "upgrade"
	upgrade[1] = "--install"

	if p.Config.Release != "" {
		upgrade = append(upgrade, p.Config.Release)
	}
	upgrade = append(upgrade, p.Config.Chart)
	if p.Config.Version != "" {
		upgrade = append(upgrade, "--version")
		upgrade = append(upgrade, p.Config.Version)
	}
	if p.Config.Values != "" {
		upgrade = append(upgrade, "--set")
		upgrade = append(upgrade, unQuote(p.Config.Values))
	}
	if p.Config.StringValues != "" {
		upgrade = append(upgrade, "--set-string")
		upgrade = append(upgrade, unQuote(p.Config.StringValues))
	}
	if p.Config.ValuesFiles != "" {
		for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") {
			upgrade = append(upgrade, "--values")
			upgrade = append(upgrade, valuesFile)
		}
	}
	if p.Config.Namespace != "" {
		upgrade = append(upgrade, "--namespace")
		upgrade = append(upgrade, p.Config.Namespace)
	}
	if p.Config.TillerNs != "" {
		upgrade = append(upgrade, "--tiller-namespace")
		upgrade = append(upgrade, p.Config.TillerNs)
	}
	if p.Config.DryRun {
		upgrade = append(upgrade, "--dry-run")
	}
	if p.Config.Debug {
		upgrade = append(upgrade, "--debug")
	}
	if p.Config.Wait {
		upgrade = append(upgrade, "--wait")
	}
	if p.Config.RecreatePods {
		upgrade = append(upgrade, "--recreate-pods")
	}
	if p.Config.ReuseValues {
		upgrade = append(upgrade, "--reuse-values")
	}
	if p.Config.Timeout != "" {
		upgrade = append(upgrade, "--timeout")
		upgrade = append(upgrade, p.Config.Timeout)
	}
	if p.Config.Force {
		upgrade = append(upgrade, "--force")
	}
	p.command = upgrade

}

func setHelmCommand(p *Plugin) {

	switch p.Config.HelmCommand {
	case "upgrade":
		setUpgradeCommand(p)
	case "delete":
		setDeleteCommand(p)
	default:
		switch os.Getenv("DRONE_BUILD_EVENT") {
		case "push", "tag", "deployment", "pull_request", "promote", "rollback":
			setUpgradeCommand(p)
		case "delete":
			setDeleteCommand(p)
		default:
			setHelpCommand(p)
		}
	}

}

var repoExp = regexp.MustCompile(`^(?P<name>[\w-]+)=(?P<url>(http|https)://[\w-./:]+)`)

// parseRepo returns map of regex capture groups (name, url)
func parseRepo(repo string) (map[string]string, error) {
	matches := repoExp.FindStringSubmatch(repo)
	if len(matches) < 1 {
		return nil, fmt.Errorf("Invalid repo definition: %s", repo)
	}
	result := make(map[string]string)
	for i, name := range repoExp.SubexpNames() {
		if i != 0 {
			result[name] = matches[i]
		}
	}
	return result, nil
}

func doHelmRepoAdd(repo string) ([]string, error) {
	repoMap, err := parseRepo(unQuote(repo))
	if err != nil {
		return nil, err
	}
	repoAdd := []string{
		"repo",
		"add",
		repoMap["name"],
		repoMap["url"],
	}
	return repoAdd, nil
}

func doHelmInit(p *Plugin) []string {
	init := make([]string, 1)
	init[0] = "init"
	if p.Config.TillerNs != "" {
		init = append(init, "--tiller-namespace")
		init = append(init, p.Config.TillerNs)
	}
	if p.Config.ClientOnly {
		init = append(init, "--client-only")
	}
	if p.Config.Upgrade {
		init = append(init, "--upgrade")
	}
	if p.Config.CanaryImage {
		init = append(init, "--canary-image")
	}

	return init

}

func doDependencyUpdate(chart string) []string {
	dependencyUpdate := []string{
		"dependency",
		"update",
		chart,
	}

	return dependencyUpdate
}

// Exec default method
func (p *Plugin) Exec() error {
	if p.Config.Debug {
		p.debugEnv()
	}

	// create /root/.kube/config file if not exists
	if _, err := os.Stat(p.Config.KubeConfig); os.IsNotExist(err) {
		resolveSecrets(p)
		if p.Config.APIServer == "" {
			return fmt.Errorf("Error: API Server is needed to deploy.")
		}
		if p.Config.Token == "" {
			return fmt.Errorf("Error: Token is needed to deploy.")
		}
		initialiseKubeconfig(&p.Config, KUBECONFIG, p.Config.KubeConfig)
	}

	if p.Config.Debug {
		p.debug()
	}

	init := doHelmInit(p)
	err := runCommand(init)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(init[:], " "))
	}

	if len(p.Config.HelmRepos) > 0 {
		for _, repo := range p.Config.HelmRepos {
			repoAdd, err := doHelmRepoAdd(repo)
			if err == nil {
				if p.Config.Debug {
					log.Println("adding helm repo: " + strings.Join(repoAdd[:], " "))
				}

				if err = runCommand(repoAdd); err != nil {
					return fmt.Errorf("Error adding helm repo: " + err.Error())
				}
			} else {
				return err
			}
		}
	}

	if p.Config.UpdateDependencies {
		if err = runCommand(doDependencyUpdate(p.Config.Chart)); err != nil {
			return fmt.Errorf("Error updating dependencies: " + err.Error())
		}
	}

	setHelmCommand(p)

	if p.Config.Debug {
		log.Println("helm command: " + strings.Join(p.command, " "))
	}

	err = runCommand(p.command)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(p.command[:], " "))
	}

	return nil
}

func initialiseKubeconfig(params *Config, source string, target string) error {
	f, err := os.Create(target)
	if err != nil {
		return err
	}
	defer f.Close()
	// parse template
	t, _ := template.ParseFiles(source)
	// execute template
	return t.Execute(f, params)
}

func runCommand(params []string) error {
	cmd := new(exec.Cmd)
	cmd = exec.Command(HELM_BIN, params...)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	return err
}

func resolveSecrets(p *Plugin) {
	p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix, p.Config.Debug)
	p.Config.StringValues = resolveEnvVar(p.Config.StringValues, p.Config.Prefix, p.Config.Debug)

	if p.Config.APIServer == "" {
		p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.Token == "" {
		p.Config.Token = resolveEnvVar("${KUBERNETES_TOKEN}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.Certificate == "" {
		p.Config.Certificate = resolveEnvVar("${KUBERNETES_CERTIFICATE}", p.Config.Prefix, p.Config.Debug)
	}
	if p.Config.ServiceAccount == "" {
		p.Config.ServiceAccount = resolveEnvVar("${SERVICE_ACCOUNT}", p.Config.Prefix, p.Config.Debug)
		if p.Config.ServiceAccount == "" {
			p.Config.ServiceAccount = "helm"
		}
	}
}

// getEnvVars will return [${TAG} {TAG} TAG]
func getEnvVars(envvars string) [][]string {
	re := regexp.MustCompile(`\$(\{?(\w+)\}?)\.?`)
	extracted := re.FindAllStringSubmatch(envvars, -1)
	return extracted
}

func resolveEnvVar(key string, prefix string, debug bool) string {
	envvars := getEnvVars(key)
	return replaceEnvvars(envvars, prefix, key, debug)
}

func replaceEnvvars(envvars [][]string, prefix string, s string, debug bool) string {
	for _, envvar := range envvars {
		envvarName := envvar[0]
		envvarKey := envvar[2]
		prefixedKey := strings.ToUpper(prefix + "_" + envvarKey)
		envval := os.Getenv(prefixedKey)
		if debug {
			fmt.Printf("-ReplVar: %s => %s-- %s\n", prefixedKey, envvarKey, envval)
		}

		if envval == "" {
			envval = os.Getenv(envvarKey)
		}

		if strings.Contains(s, envvarName) {
			s = strings.Replace(s, envvarName, envval, -1)
		}
	}

	return s
}

// unQuote removes quotes if present
func unQuote(s string) string {
	unquoted, err := strconv.Unquote(s)
	if err != nil {
		// ignore error and return original string
		return s
	}
	return unquoted
}

func (p *Plugin) debugEnv() {
	// debug env vars
	for _, e := range os.Environ() {
		fmt.Println("-Var:--", e)
	}
}

func (p *Plugin) debug() {
	fmt.Println(p)
	// debug plugin obj
	fmt.Printf("Api server: %s \n", p.Config.APIServer)
	fmt.Printf("Values: %s \n", p.Config.Values)
	fmt.Printf("StringValues: %s \n", p.Config.StringValues)
	fmt.Printf("Secrets: %s \n", p.Config.Secrets)
	fmt.Printf("Helm Repos: %s \n", p.Config.HelmRepos)
	fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles)
	kubeconfig, err := ioutil.ReadFile(KUBECONFIG)
	if err == nil {
		fmt.Println(string(kubeconfig))
	}

	config, err := ioutil.ReadFile(p.Config.KubeConfig)
	if err == nil {
		fmt.Println(string(config))
	}
}
<|endoftext|>


<commit_before>// +build darwin

package memory

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
)

// Get memory statistics
func Get() (*Memory, error) {
	cmd := exec.Command("vm_stat")
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	memory, err := collectMemoryStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	cmd = exec.Command("sysctl", "-n", "vm.swapusage")
	out, err = cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	swap, err := collectSwapStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	memory.SwapTotal = swap.total
	memory.SwapUsed = swap.used
	memory.SwapFree = swap.free
	return memory, nil
}

// Memory represents memory statistics for darwin
type Memory struct {
	Total, Used, Cached, Free, Active, Inactive, SwapTotal, SwapUsed, SwapFree uint64
}

// References:
//   - https://support.apple.com/en-us/HT201464#memory
//   - https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
//   - https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/
func collectMemoryStats(out io.Reader) (*Memory, error) {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() { // skip the first line
		return nil, fmt.Errorf("failed to scan output of vm_stat")
	}

	var memory Memory
	var speculative, wired, purgeable, fileBacked, compressed uint64
	memStats := map[string]*uint64{
		"Pages free":                   &memory.Free,
		"Pages active":                 &memory.Active,
		"Pages inactive":               &memory.Inactive,
		"Pages speculative":            &speculative,
		"Pages wired down":             &wired,
		"Pages purgeable":              &purgeable,
		"File-backed pages":            &fileBacked,
		"Pages occupied by compressor": &compressed,
	}
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.IndexRune(line, ':')
		if i < 0 {
			continue
		}
		if ptr := memStats[line[:i]]; ptr != nil {
			val := strings.TrimRight(strings.TrimSpace(line[i+1:]), ".")
			if v, err := strconv.ParseUint(val, 10, 64); err == nil {
				*ptr = v * 4096
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	memory.Cached = purgeable + fileBacked
	memory.Used = wired + compressed + memory.Active + memory.Inactive + speculative - memory.Cached
	memory.Total = memory.Used + memory.Cached + memory.Free
	return &memory, nil
}

type memorySwap struct {
	total, free, used uint64
}

func collectSwapStats(out io.Reader) (*memorySwap, error) {
	var total, used, free float64
	cnt, err := fmt.Fscanf(out, "total = %fM used = %fM free = %fM", &total, &used, &free)
	if err != nil {
		return nil, fmt.Errorf("failed to scan output of sysctl -n vmswapusage: %s", err)
	}
	if cnt != 3 {
		return nil, errors.New("failed to parse output of 'sysctl -n vm.swapusage'")
	}
	return &memorySwap{
		total: uint64(total * 1024 * 1024),
		used:  uint64(used * 1024 * 1024),
		free:  uint64(free * 1024 * 1024),
	}, nil
}
<commit_msg>improve error handling for memory on darwin<commit_after>// +build darwin

package memory

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
)

// Get memory statistics
func Get() (*Memory, error) {
	cmd := exec.Command("vm_stat")
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	memory, err := collectMemoryStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	cmd = exec.Command("sysctl", "-n", "vm.swapusage")
	out, err = cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	swap, err := collectSwapStats(out)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	memory.SwapTotal = swap.total
	memory.SwapUsed = swap.used
	memory.SwapFree = swap.free
	return memory, nil
}

// Memory represents memory statistics for darwin
type Memory struct {
	Total, Used, Cached, Free, Active, Inactive, SwapTotal, SwapUsed, SwapFree uint64
}

// References:
//   - https://support.apple.com/en-us/HT201464#memory
//   - https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
//   - https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/
func collectMemoryStats(out io.Reader) (*Memory, error) {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() { // skip the first line
		return nil, fmt.Errorf("failed to scan output of vm_stat")
	}

	var memory Memory
	var speculative, wired, purgeable, fileBacked, compressed uint64
	memStats := map[string]*uint64{
		"Pages free":                   &memory.Free,
		"Pages active":                 &memory.Active,
		"Pages inactive":               &memory.Inactive,
		"Pages speculative":            &speculative,
		"Pages wired down":             &wired,
		"Pages purgeable":              &purgeable,
		"File-backed pages":            &fileBacked,
		"Pages occupied by compressor": &compressed,
	}
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.IndexRune(line, ':')
		if i < 0 {
			continue
		}
		if ptr := memStats[line[:i]]; ptr != nil {
			val := strings.TrimRight(strings.TrimSpace(line[i+1:]), ".")
			if v, err := strconv.ParseUint(val, 10, 64); err == nil {
				*ptr = v * 4096
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	memory.Cached = purgeable + fileBacked
	memory.Used = wired + compressed + memory.Active + memory.Inactive + speculative - memory.Cached
	memory.Total = memory.Used + memory.Cached + memory.Free
	return &memory, nil
}

type memorySwap struct {
	total, free, used uint64
}

func collectSwapStats(out io.Reader) (*memorySwap, error) {
	var total, used, free float64
	cnt, err := fmt.Fscanf(out, "total = %fM used = %fM free = %fM", &total, &used, &free)
	if err != nil {
		return nil, fmt.Errorf("failed to scan output of 'sysctl -n vmswapusage': %s", err)
	}
	if cnt != 3 {
		return nil, errors.New("failed to parse output of 'sysctl -n vm.swapusage'")
	}
	return &memorySwap{
		total: uint64(total * 1024 * 1024),
		used:  uint64(used * 1024 * 1024),
		free:  uint64(free * 1024 * 1024),
	}, nil
}
<|endoftext|>


<commit_before>package hash_table_test

import (
	"testing"

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

	"github.com/oinume/algo/datastructure/hash_table"
)

//type hashable struct {
//	object types.Object
//}
//
//func (h *hashable) Get() interface{} {
//	return h.object
//}
//
//func (h *hashable) Receive(v interface{}) error {
//	return h.object.Receive(v)
//}
//
//func (h *hashable) String() string {
//	return h.object.String()
//}
//
//func (h *hashable) Int() int {
//	return h.object.Int()
//}

//// Always return same hash code
//func (h *hashable) HashCode() int {
//	return 1
//}

func TestHashTableChaining_Put(t *testing.T) {
	h := hash_table.NewChaining(10)
	_, err := h.Put(1, 1)
	if err != nil {
		t.Fatalf("Put: unexpected error: %v", err)
	}
	if got, want := 1, h.Size(); got != want {
		t.Fatalf("unexpected Size: got=%v, want=%v", got, want)
	}
}

func TestHashTableChaining_Put_Collision(t *testing.T) {
	a := assert.New(t)
	r := require.New(t)

	table := NewChaining(10)
	_, _ = table.Put("abc", "ABC")
	_, _ = table.Put("cba", "CBA")
	a.Equal(2, table.Size())

	actual, err := table.Get("cba")
	r.NoError(err)
	a.Equal("CBA", actual)
}

func TestHashTableChaining_Put_Collision_Exists(t *testing.T) {
	a := assert.New(t)
	r := require.New(t)

	table := NewChaining(10)
	_, _ = table.Put("abc", "ABC")
	_, _ = table.Put("abc", "AABBCC")
	_, _ = table.Put("cba", "CBA")
	a.Equal(2, table.Size())

	actual, err := table.Get("abc")
	r.NoError(err)
	a.Equal("AABBCC", actual)
}

func TestHashTableChaining_Get(t *testing.T) {
	a := assert.New(t)
	r := require.New(t)
	hashMap := NewChaining(10)
	_, err := hashMap.Put(1, 1)
	r.NoError(err)
	value, err := hashMap.Get(1)
	a.NoError(err)
	a.Equal(1, value)
}

func TestHashTableChaining_Remove(t *testing.T) {
	a := assert.New(t)
	r := require.New(t)
	hashMap := NewChaining(10)

	_, err := hashMap.Put(1, 1)
	r.NoError(err)
	value, err := hashMap.Remove(1)
	r.NoError(err)
	a.Equal(1, value)
	a.Equal(0, hashMap.Size())

	_, err = hashMap.Remove(100)
	a.Error(err)
}
<commit_msg>Not to use testify<commit_after>package hash_table_test

import (
	"testing"

	"github.com/oinume/algo/datastructure/hash_table"
	"github.com/oinume/algo/testings"
)

//type hashable struct {
//	object types.Object
//}
//
//func (h *hashable) Get() interface{} {
//	return h.object
//}
//
//func (h *hashable) Receive(v interface{}) error {
//	return h.object.Receive(v)
//}
//
//func (h *hashable) String() string {
//	return h.object.String()
//}
//
//func (h *hashable) Int() int {
//	return h.object.Int()
//}

//// Always return same hash code
//func (h *hashable) HashCode() int {
//	return 1
//}

func TestHashTableChaining_Put(t *testing.T) {
	table := hash_table.NewChaining(10)
	_, err := table.Put(1, 1)
	if err != nil {
		t.Fatalf("Put: unexpected error: %v", err)
	}
	testings.AssertEqual(t, 1, table.Size(), "table.Size()")
}

func TestHashTableChaining_Put_Collision(t *testing.T) {
	table := hash_table.NewChaining(10)
	_, _ = table.Put("abc", "ABC")
	_, _ = table.Put("cba", "CBA")
	testings.AssertEqual(t, 2, table.Size(), "table.Size()")

	got, err := table.Get("cba")
	if err != nil {
		t.Fatalf("Get returns unexpected error: %v", err)
	}
	testings.AssertEqual(t, "CBA", got, "table.Get()")
}

func TestHashTableChaining_Put_Collision_Exists(t *testing.T) {
	table := hash_table.NewChaining(10)
	_, _ = table.Put("abc", "ABC")
	_, _ = table.Put("abc", "AABBCC")
	_, _ = table.Put("cba", "CBA")
	testings.AssertEqual(t, 2, table.Size(), "table.Size()")

	got, err := table.Get("abc")
	if err != nil {
		t.Fatalf("Get returns unexpected error: %v", err)
	}
	testings.AssertEqual(t, "AABBCC", got, "table.Get()")
}

func TestHashTableChaining_Get(t *testing.T) {
	table := hash_table.NewChaining(10)
	_, err := table.Put(1, 1)
	if err != nil {
		t.Fatalf("Put returns unexpected error: %v", err)
	}
	got, err := table.Get(1)
	if err != nil {
		t.Fatalf("Get returns unexpected error: %v", err)
	}
	testings.AssertEqual(t, 1, got, "table.Get()")
}

func TestHashTableChaining_Remove(t *testing.T) {
	table := hash_table.NewChaining(10)

	_, err := table.Put(1, 1)
	if err != nil {
		t.Fatalf("Put returns unexpected error: %v", err)
	}
	removed, err := table.Remove(1)
	if err != nil {
		t.Fatalf("Remove returns unexpected error: %v", err)
	}
	testings.AssertEqual(t, 1, removed, "table.Remove()")
	testings.AssertEqual(t, 0, table.Size(), "table must be empty")

	if _, err := table.Remove(100); err == nil {
		t.Fatalf("Remove must return err but nil")
	}
}
<|endoftext|>


<commit_before><commit_msg>Adds github-token for integration suite<commit_after><|endoftext|>


<commit_before>/*
Copyright 2017 The Nuclio 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 timeout

import (
	"testing"
	"time"

	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/processor/runtime"
	"github.com/nuclio/nuclio/pkg/processor/trigger"
	"github.com/nuclio/nuclio/pkg/processor/worker"

	"github.com/nuclio/zap"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

type mockTestRuntime struct {
	runtime.AbstractRuntime
	mock.Mock
}

func (r *mockTestRuntime) Restart() error {
	r.Called()
	return nil
}

type mockTestTrigger struct {
	trigger.AbstractTrigger
	workers []*worker.Worker
	mock.Mock
}

func (t *mockTestTrigger) GetWorkers() []*worker.Worker {
	t.Called()
	return t.workers
}

func (t *mockTestTrigger) GetConfig() map[string]interface{} {
	return nil
}

func (t *mockTestTrigger) Start(checkpoint functionconfig.Checkpoint) error {
	return nil
}

func (t *mockTestTrigger) Stop(force bool) (functionconfig.Checkpoint, error) {
	return nil, nil
}

type mockTestProcessor struct {
	triggers []trigger.Trigger
	mock.Mock
}

func (tp *mockTestProcessor) GetTriggers() []trigger.Trigger {
	tp.Called()

	return tp.triggers
}

func (tp *mockTestProcessor) Stop() {
}

type eventTimeoutSuite struct {
	suite.Suite
}

func (suite *eventTimeoutSuite) TestWatcher() {
	logger, err := nucliozap.NewNuclioZapTest("EventTimeout")
	suite.Require().NoError(err, "Can't create logger")

	mockTrigger := &mockTestTrigger{
		workers: []*worker.Worker{&worker.Worker{}},
	}

	mockTrigger.On("GetWorkers").Return(mockTrigger.GetWorkers())

	mockProcessor := &mockTestProcessor{
		triggers: []trigger.Trigger{
			mockTrigger,
		},
	}

	mockProcessor.On("GetTriggers").Return(nil)

	timeout := time.Millisecond
	NewEventTimeoutWatcher(logger, timeout, mockProcessor)
	time.Sleep(3 * timeout) // Give watcher time to work

	mockTrigger.AssertExpectations(suite.T())
	mockProcessor.AssertExpectations(suite.T())
}

func TestEventTimeoutWatcher(t *testing.T) {
	suite.Run(t, &eventTimeoutSuite{})
}
<commit_msg>Fixed unittest timeout (#1422)<commit_after>/*
Copyright 2017 The Nuclio 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 timeout

import (
	"testing"
	"time"

	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/processor/runtime"
	"github.com/nuclio/nuclio/pkg/processor/trigger"
	"github.com/nuclio/nuclio/pkg/processor/worker"

	"github.com/nuclio/zap"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

type mockTestRuntime struct {
	runtime.AbstractRuntime
	mock.Mock
}

func (r *mockTestRuntime) Restart() error {
	r.Called()
	return nil
}

type mockTestTrigger struct {
	trigger.AbstractTrigger
	workers []*worker.Worker
	mock.Mock
}

func (t *mockTestTrigger) GetWorkers() []*worker.Worker {
	t.Called()
	return t.workers
}

func (t *mockTestTrigger) GetConfig() map[string]interface{} {
	return nil
}

func (t *mockTestTrigger) Start(checkpoint functionconfig.Checkpoint) error {
	return nil
}

func (t *mockTestTrigger) Stop(force bool) (functionconfig.Checkpoint, error) {
	return nil, nil
}

type mockTestProcessor struct {
	triggers []trigger.Trigger
	mock.Mock
}

func (tp *mockTestProcessor) GetTriggers() []trigger.Trigger {
	tp.Called()

	return tp.triggers
}

func (tp *mockTestProcessor) Stop() {
}

type eventTimeoutSuite struct {
	suite.Suite
}

func (suite *eventTimeoutSuite) TestWatcher() {
	logger, err := nucliozap.NewNuclioZapTest("EventTimeout")
	suite.Require().NoError(err, "Can't create logger")

	mockTrigger := &mockTestTrigger{
		workers: []*worker.Worker{&worker.Worker{}},
	}

	mockTrigger.On("GetWorkers").Return(mockTrigger.GetWorkers())

	mockProcessor := &mockTestProcessor{
		triggers: []trigger.Trigger{
			mockTrigger,
		},
	}

	mockProcessor.On("GetTriggers").Return(nil)

	timeout := time.Millisecond
	NewEventTimeoutWatcher(logger, timeout, mockProcessor)
	time.Sleep(5 * timeout) // Give watcher time to work

	mockTrigger.AssertExpectations(suite.T())
	mockProcessor.AssertExpectations(suite.T())
}

func TestEventTimeoutWatcher(t *testing.T) {
	suite.Run(t, &eventTimeoutSuite{})
}
<|endoftext|>


<commit_before>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package topology

import (
	"encoding/json"
	"path"

	topo "github.com/wandoulabs/go-zookeeper/zk"

	"github.com/wandoulabs/codis/pkg/models"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
	"github.com/wandoulabs/zkhelper"
)

type TopoUpdate interface {
	OnGroupChange(groupId int)
	OnSlotChange(slotId int)
}

type ZkFactory func(zkAddr string) (zkhelper.Conn, error)

type Topology struct {
	ProductName string
	zkAddr      string
	zkConn      zkhelper.Conn
	fact        ZkFactory
	provider    string
}

func (top *Topology) GetGroup(groupId int) (*models.ServerGroup, error) {
	return models.GetGroup(top.zkConn, top.ProductName, groupId)
}

func (top *Topology) Exist(path string) (bool, error) {
	return zkhelper.NodeExists(top.zkConn, path)
}

func (top *Topology) GetSlotByIndex(i int) (*models.Slot, *models.ServerGroup, error) {
	slot, err := models.GetSlot(top.zkConn, top.ProductName, i)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	log.Debugf("get slot %d : %+v", i, slot)
	if slot.State.Status != models.SLOT_STATUS_ONLINE && slot.State.Status != models.SLOT_STATUS_MIGRATE {
		log.Errorf("slot not online, %+v", slot)
	}

	groupServer, err := models.GetGroup(top.zkConn, top.ProductName, slot.GroupId)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	return slot, groupServer, nil
}

func NewTopo(ProductName string, zkAddr string, f ZkFactory, provider string) *Topology {
	t := &Topology{zkAddr: zkAddr, ProductName: ProductName, fact: f, provider: provider}
	if t.fact == nil {
		switch t.provider {
		case "etcd":
			t.fact = zkhelper.NewEtcdConn
		case "zookeeper":
			t.fact = zkhelper.ConnectToZk
		default:
			log.Panicf("coordinator not found in config")
		}
	}
	t.InitZkConn()
	return t
}

func (top *Topology) InitZkConn() {
	var err error
	top.zkConn, err = top.fact(top.zkAddr)
	if err != nil {
		log.PanicErrorf(err, "init failed")
	}
}

func (top *Topology) GetActionWithSeq(seq int64) (*models.Action, error) {
	return models.GetActionWithSeq(top.zkConn, top.ProductName, seq, top.provider)
}

func (top *Topology) GetActionWithSeqObject(seq int64, act *models.Action) error {
	return models.GetActionObject(top.zkConn, top.ProductName, seq, act, top.provider)
}

func (top *Topology) GetActionSeqList(productName string) ([]int, error) {
	return models.GetActionSeqList(top.zkConn, productName)
}

func (top *Topology) IsChildrenChangedEvent(e interface{}) bool {
	return e.(topo.Event).Type == topo.EventNodeChildrenChanged
}

func (top *Topology) CreateProxyInfo(pi *models.ProxyInfo) (string, error) {
	return models.CreateProxyInfo(top.zkConn, top.ProductName, pi)
}

func (top *Topology) CreateProxyFenceNode(pi *models.ProxyInfo) (string, error) {
	return models.CreateProxyFenceNode(top.zkConn, top.ProductName, pi)
}

func (top *Topology) GetProxyInfo(proxyName string) (*models.ProxyInfo, error) {
	return models.GetProxyInfo(top.zkConn, top.ProductName, proxyName)
}

func (top *Topology) GetActionResponsePath(seq int) string {
	return path.Join(models.GetActionResponsePath(top.ProductName), top.zkConn.Seq2Str(int64(seq)))
}

func (top *Topology) SetProxyStatus(proxyName string, status string) error {
	return models.SetProxyStatus(top.zkConn, top.ProductName, proxyName, status)
}

func (top *Topology) Close(proxyName string) {
	// delete fence znode
	pi, err := models.GetProxyInfo(top.zkConn, top.ProductName, proxyName)
	if err != nil {
		log.Errorf("killing fence error, proxy %s is not exists", proxyName)
	} else {
		zkhelper.DeleteRecursive(top.zkConn, path.Join(models.GetProxyFencePath(top.ProductName), pi.Addr), -1)
	}
	// delete ephemeral znode
	zkhelper.DeleteRecursive(top.zkConn, path.Join(models.GetProxyPath(top.ProductName), proxyName), -1)
	top.zkConn.Close()
}

func (top *Topology) DoResponse(seq int, pi *models.ProxyInfo) error {
	//create response node
	actionPath := top.GetActionResponsePath(seq)
	//log.Debug("actionPath:", actionPath)
	data, err := json.Marshal(pi)
	if err != nil {
		return errors.Trace(err)
	}

	_, err = top.zkConn.Create(path.Join(actionPath, pi.Id), data,
		0, zkhelper.DefaultFileACLs())

	return err
}

func (top *Topology) doWatch(evtch <-chan topo.Event, evtbus chan interface{}) {
	e := <-evtch
	if e.State == topo.StateExpired || e.Type == topo.EventNotWatching {
		log.Panicf("session expired: %+v", e)
	}

	log.Warnf("topo event %+v", e)

	switch e.Type {
	//case topo.EventNodeCreated:
	//case topo.EventNodeDataChanged:
	case topo.EventNodeChildrenChanged: //only care children changed
		//todo:get changed node and decode event
	default:
		log.Warnf("%+v", e)
	}

	evtbus <- e
}

func (top *Topology) WatchChildren(path string, evtbus chan interface{}) ([]string, error) {
	content, _, evtch, err := top.zkConn.ChildrenW(path)
	if err != nil {
		return nil, errors.Trace(err)
	}

	go top.doWatch(evtch, evtbus)
	return content, nil
}

func (top *Topology) WatchNode(path string, evtbus chan interface{}) ([]byte, error) {
	content, _, evtch, err := top.zkConn.GetW(path)
	if err != nil {
		return nil, errors.Trace(err)
	}

	go top.doWatch(evtch, evtbus)
	return content, nil
}
<commit_msg>cleanup logs<commit_after>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package topology

import (
	"encoding/json"
	"path"

	topo "github.com/wandoulabs/go-zookeeper/zk"

	"github.com/wandoulabs/codis/pkg/models"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
	"github.com/wandoulabs/zkhelper"
)

type TopoUpdate interface {
	OnGroupChange(groupId int)
	OnSlotChange(slotId int)
}

type ZkFactory func(zkAddr string) (zkhelper.Conn, error)

type Topology struct {
	ProductName string
	zkAddr      string
	zkConn      zkhelper.Conn
	fact        ZkFactory
	provider    string
}

func (top *Topology) GetGroup(groupId int) (*models.ServerGroup, error) {
	return models.GetGroup(top.zkConn, top.ProductName, groupId)
}

func (top *Topology) Exist(path string) (bool, error) {
	return zkhelper.NodeExists(top.zkConn, path)
}

func (top *Topology) GetSlotByIndex(i int) (*models.Slot, *models.ServerGroup, error) {
	slot, err := models.GetSlot(top.zkConn, top.ProductName, i)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	groupServer, err := models.GetGroup(top.zkConn, top.ProductName, slot.GroupId)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	return slot, groupServer, nil
}

func NewTopo(ProductName string, zkAddr string, f ZkFactory, provider string) *Topology {
	t := &Topology{zkAddr: zkAddr, ProductName: ProductName, fact: f, provider: provider}
	if t.fact == nil {
		switch t.provider {
		case "etcd":
			t.fact = zkhelper.NewEtcdConn
		case "zookeeper":
			t.fact = zkhelper.ConnectToZk
		default:
			log.Panicf("coordinator not found in config")
		}
	}
	t.InitZkConn()
	return t
}

func (top *Topology) InitZkConn() {
	var err error
	top.zkConn, err = top.fact(top.zkAddr)
	if err != nil {
		log.PanicErrorf(err, "init failed")
	}
}

func (top *Topology) GetActionWithSeq(seq int64) (*models.Action, error) {
	return models.GetActionWithSeq(top.zkConn, top.ProductName, seq, top.provider)
}

func (top *Topology) GetActionWithSeqObject(seq int64, act *models.Action) error {
	return models.GetActionObject(top.zkConn, top.ProductName, seq, act, top.provider)
}

func (top *Topology) GetActionSeqList(productName string) ([]int, error) {
	return models.GetActionSeqList(top.zkConn, productName)
}

func (top *Topology) IsChildrenChangedEvent(e interface{}) bool {
	return e.(topo.Event).Type == topo.EventNodeChildrenChanged
}

func (top *Topology) CreateProxyInfo(pi *models.ProxyInfo) (string, error) {
	return models.CreateProxyInfo(top.zkConn, top.ProductName, pi)
}

func (top *Topology) CreateProxyFenceNode(pi *models.ProxyInfo) (string, error) {
	return models.CreateProxyFenceNode(top.zkConn, top.ProductName, pi)
}

func (top *Topology) GetProxyInfo(proxyName string) (*models.ProxyInfo, error) {
	return models.GetProxyInfo(top.zkConn, top.ProductName, proxyName)
}

func (top *Topology) GetActionResponsePath(seq int) string {
	return path.Join(models.GetActionResponsePath(top.ProductName), top.zkConn.Seq2Str(int64(seq)))
}

func (top *Topology) SetProxyStatus(proxyName string, status string) error {
	return models.SetProxyStatus(top.zkConn, top.ProductName, proxyName, status)
}

func (top *Topology) Close(proxyName string) {
	// delete fence znode
	pi, err := models.GetProxyInfo(top.zkConn, top.ProductName, proxyName)
	if err != nil {
		log.Errorf("killing fence error, proxy %s is not exists", proxyName)
	} else {
		zkhelper.DeleteRecursive(top.zkConn, path.Join(models.GetProxyFencePath(top.ProductName), pi.Addr), -1)
	}
	// delete ephemeral znode
	zkhelper.DeleteRecursive(top.zkConn, path.Join(models.GetProxyPath(top.ProductName), proxyName), -1)
	top.zkConn.Close()
}

func (top *Topology) DoResponse(seq int, pi *models.ProxyInfo) error {
	//create response node
	actionPath := top.GetActionResponsePath(seq)
	//log.Debug("actionPath:", actionPath)
	data, err := json.Marshal(pi)
	if err != nil {
		return errors.Trace(err)
	}

	_, err = top.zkConn.Create(path.Join(actionPath, pi.Id), data,
		0, zkhelper.DefaultFileACLs())

	return err
}

func (top *Topology) doWatch(evtch <-chan topo.Event, evtbus chan interface{}) {
	e := <-evtch
	if e.State == topo.StateExpired || e.Type == topo.EventNotWatching {
		log.Panicf("session expired: %+v", e)
	}

	log.Warnf("topo event %+v", e)

	switch e.Type {
	//case topo.EventNodeCreated:
	//case topo.EventNodeDataChanged:
	case topo.EventNodeChildrenChanged: //only care children changed
		//todo:get changed node and decode event
	default:
		log.Warnf("%+v", e)
	}

	evtbus <- e
}

func (top *Topology) WatchChildren(path string, evtbus chan interface{}) ([]string, error) {
	content, _, evtch, err := top.zkConn.ChildrenW(path)
	if err != nil {
		return nil, errors.Trace(err)
	}

	go top.doWatch(evtch, evtbus)
	return content, nil
}

func (top *Topology) WatchNode(path string, evtbus chan interface{}) ([]byte, error) {
	content, _, evtch, err := top.zkConn.GetW(path)
	if err != nil {
		return nil, errors.Trace(err)
	}

	go top.doWatch(evtch, evtbus)
	return content, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 Google LLC

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 identity contains the identity reconciler
package identity

import (
	"context"
	"fmt"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	kubeclient "knative.dev/pkg/client/injection/kube/client"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/kmeta"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/ptr"

	"github.com/google/knative-gcp/pkg/apis/configs/gcpauth"
	duck "github.com/google/knative-gcp/pkg/duck/v1"
	metadataClient "github.com/google/knative-gcp/pkg/gclient/metadata"
	"github.com/google/knative-gcp/pkg/reconciler/identity/iam"
	"github.com/google/knative-gcp/pkg/reconciler/identity/resources"
	"github.com/google/knative-gcp/pkg/utils"
)

const (
	Role                         = "roles/iam.workloadIdentityUser"
	deleteWorkloadIdentityFailed = "WorkloadIdentityDeleteFailed"
	workloadIdentityFailed       = "WorkloadIdentityReconcileFailed"
)

func NewIdentity(ctx context.Context, policyManager iam.IAMPolicyManager, gcpAuthStore *gcpauth.Store) *Identity {
	return &Identity{
		kubeClient:    kubeclient.Get(ctx),
		policyManager: policyManager,
		gcpAuthStore:  gcpAuthStore,
	}
}

func NewGCPAuthStore(ctx context.Context, cmw configmap.Watcher) *gcpauth.Store {
	gcpAuthStore := gcpauth.NewStore(logging.FromContext(ctx).Named("config-gcp-auth-store"))
	gcpAuthStore.WatchConfigs(cmw)
	return gcpAuthStore
}

type Identity struct {
	kubeClient    kubernetes.Interface
	policyManager iam.IAMPolicyManager
	gcpAuthStore  *gcpauth.Store
}

// ReconcileWorkloadIdentity will create a k8s service account, add ownerReference to it,
// and add iam policy binding between this k8s service account and its corresponding GCP service account.
func (i *Identity) ReconcileWorkloadIdentity(ctx context.Context, projectID string, identifiable duck.Identifiable) (*corev1.ServiceAccount, error) {
	status := identifiable.IdentityStatus()
	// Create corresponding k8s ServiceAccount if it doesn't exist.

	identityNames, err := i.getGoogleServiceAccountName(ctx, identifiable)
	if err != nil {
		logging.FromContext(ctx).Desugar().Error("failed to get Google service account name", zap.Error(err))
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return nil, fmt.Errorf(`failed to get Google service account name: %w`, err)
	} else if identityNames.GoogleServiceAccountName == "" {
		// If there is no Google service account paired with current Kubernetes service account in GCP auth configmap, no further reconciliation.
		return nil, nil
	}

	kServiceAccount, err := i.createServiceAccount(ctx, identityNames)
	if err != nil {
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return nil, fmt.Errorf("failed to get k8s ServiceAccount: %w", err)
	}
	// Add ownerReference to K8s ServiceAccount.
	expectOwnerReference := *kmeta.NewControllerRef(identifiable)
	expectOwnerReference.Controller = ptr.Bool(false)
	if !ownerReferenceExists(kServiceAccount, expectOwnerReference) {
		kServiceAccount.OwnerReferences = append(kServiceAccount.OwnerReferences, expectOwnerReference)
		if _, err := i.kubeClient.CoreV1().ServiceAccounts(kServiceAccount.Namespace).Update(kServiceAccount); err != nil {
			logging.FromContext(ctx).Desugar().Error("Failed to update OwnerReferences", zap.Error(err))
			status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
			return nil, fmt.Errorf("failed to update OwnerReferences: %w", err)
		}
	}

	// Add iam policy binding to GCP ServiceAccount.
	if err := i.addIamPolicyBinding(ctx, projectID, identityNames); err != nil {
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return kServiceAccount, fmt.Errorf("adding iam policy binding failed with: %w", err)
	}
	status.MarkWorkloadIdentityReady(identifiable.ConditionSet())
	return kServiceAccount, nil
}

// DeleteWorkloadIdentity will remove iam policy binding between k8s service account and its corresponding GCP service account,
// if this k8s service account only has one ownerReference.
func (i *Identity) DeleteWorkloadIdentity(ctx context.Context, projectID string, identifiable duck.Identifiable) error {
	status := identifiable.IdentityStatus()

	identityNames, err := i.getGoogleServiceAccountName(ctx, identifiable)
	if err != nil {
		logging.FromContext(ctx).Desugar().Error("failed to get Google service account name", zap.Error(err))
		status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
		return fmt.Errorf(`failed to get Google service account name: %w`, err)
	} else if identityNames.GoogleServiceAccountName == "" {
		// If there is no Google service account paired with current Kubernetes service account in GCP auth configmap, no further reconciliation.
		return nil
	}

	kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(identityNames.Namespace).Get(identityNames.KServiceAccountName, metav1.GetOptions{})
	if err != nil {
		status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
		// k8s ServiceAccount should be there.
		return fmt.Errorf("getting k8s service account failed with: %w", err)
	}
	if kServiceAccount != nil && len(kServiceAccount.OwnerReferences) == 1 {
		logging.FromContext(ctx).Desugar().Debug("Removing iam policy binding.")
		if err := i.removeIamPolicyBinding(ctx, projectID, identityNames); err != nil {
			status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
			return fmt.Errorf("removing iam policy binding failed with: %w", err)
		}
	}
	return nil
}

// getGoogleServiceAccountName will return Google service account name and corresponding raw Kubernetes service account name.
func (i *Identity) getGoogleServiceAccountName(ctx context.Context, identifiable duck.Identifiable) (resources.IdentityNames, error) {
	namespace := identifiable.GetObjectMeta().GetNamespace()
	ad := i.gcpAuthStore.Load()
	if ad == nil || ad.GCPAuthDefaults == nil {
		logging.FromContext(ctx).Desugar().Error("Failed to get default config from GCP auth configmap")
		return resources.IdentityNames{}, fmt.Errorf("failed to get default config from GCP auth configmap")
	}
	return resources.IdentityNames{
		KServiceAccountName:      identifiable.IdentitySpec().ServiceAccountName,
		GoogleServiceAccountName: ad.GCPAuthDefaults.WorkloadIdentityGSA(namespace, identifiable.IdentitySpec().ServiceAccountName),
		Namespace:                namespace,
	}, nil
}

func (i *Identity) createServiceAccount(ctx context.Context, identityNames resources.IdentityNames) (*corev1.ServiceAccount, error) {
	kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(identityNames.Namespace).Get(identityNames.KServiceAccountName, metav1.GetOptions{})
	if err != nil {
		if apierrs.IsNotFound(err) {
			expect := resources.MakeServiceAccount(identityNames)
			logging.FromContext(ctx).Desugar().Debug("Creating k8s service account", zap.Any("ksa", expect))
			kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(expect.Namespace).Create(expect)
			if err != nil {
				logging.FromContext(ctx).Desugar().Error("Failed to create k8s service account", zap.Error(err))
				return nil, fmt.Errorf("failed to create k8s service account: %w", err)
			}
			return kServiceAccount, nil
		}
		logging.FromContext(ctx).Desugar().Error("Failed to get k8s service account", zap.Error(err))
		return nil, fmt.Errorf("getting k8s service account failed with: %w", err)
	}
	return kServiceAccount, nil
}

// TODO he iam policy binding should be mocked so that we can unit test it. issue https://github.com/google/knative-gcp/issues/657
// addIamPolicyBinding will add iam policy binding, which is related to a provided k8s ServiceAccount, to a GCP ServiceAccount.
func (i *Identity) addIamPolicyBinding(ctx context.Context, projectID string, identityNames resources.IdentityNames) error {
	projectID, err := utils.ProjectID(projectID, metadataClient.NewDefaultMetadataClient())
	if err != nil {
		return fmt.Errorf("failed to get project id: %w", err)
	}

	// currentMember will end up as "serviceAccount:projectId.svc.id.goog[k8s-namespace/ksa-name]".
	currentMember := fmt.Sprintf("serviceAccount:%s.svc.id.goog[%s/%s]", projectID, identityNames.Namespace, identityNames.KServiceAccountName)

	return i.policyManager.AddIAMPolicyBinding(ctx, iam.GServiceAccount(identityNames.GoogleServiceAccountName), currentMember, Role)
}

// removeIamPolicyBinding will remove iam policy binding, which is related to a provided k8s ServiceAccount, from a GCP ServiceAccount.
func (i *Identity) removeIamPolicyBinding(ctx context.Context, projectID string, identityNames resources.IdentityNames) error {
	projectID, err := utils.ProjectID(projectID, metadataClient.NewDefaultMetadataClient())
	if err != nil {
		return fmt.Errorf("failed to get project id: %w", err)
	}

	// currentMember will end up as "serviceAccount:projectId.svc.id.goog[k8s-namespace/ksa-name]".
	currentMember := fmt.Sprintf("serviceAccount:%s.svc.id.goog[%s/%s]", projectID, identityNames.Namespace, identityNames.KServiceAccountName)

	return i.policyManager.RemoveIAMPolicyBinding(ctx, iam.GServiceAccount(identityNames.GoogleServiceAccountName), currentMember, Role)
}

// ownerReferenceExists checks if a K8s ServiceAccount contains specific ownerReference
func ownerReferenceExists(kServiceAccount *corev1.ServiceAccount, expect metav1.OwnerReference) bool {
	references := kServiceAccount.OwnerReferences
	for _, reference := range references {
		if reference.Name == expect.Name {
			return true
		}
	}
	return false
}
<commit_msg>remove unused code. (#1686)<commit_after>/*
Copyright 2020 Google LLC

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 identity contains the identity reconciler
package identity

import (
	"context"
	"fmt"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	kubeclient "knative.dev/pkg/client/injection/kube/client"
	"knative.dev/pkg/kmeta"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/ptr"

	"github.com/google/knative-gcp/pkg/apis/configs/gcpauth"
	duck "github.com/google/knative-gcp/pkg/duck/v1"
	metadataClient "github.com/google/knative-gcp/pkg/gclient/metadata"
	"github.com/google/knative-gcp/pkg/reconciler/identity/iam"
	"github.com/google/knative-gcp/pkg/reconciler/identity/resources"
	"github.com/google/knative-gcp/pkg/utils"
)

const (
	Role                         = "roles/iam.workloadIdentityUser"
	deleteWorkloadIdentityFailed = "WorkloadIdentityDeleteFailed"
	workloadIdentityFailed       = "WorkloadIdentityReconcileFailed"
)

func NewIdentity(ctx context.Context, policyManager iam.IAMPolicyManager, gcpAuthStore *gcpauth.Store) *Identity {
	return &Identity{
		kubeClient:    kubeclient.Get(ctx),
		policyManager: policyManager,
		gcpAuthStore:  gcpAuthStore,
	}
}

type Identity struct {
	kubeClient    kubernetes.Interface
	policyManager iam.IAMPolicyManager
	gcpAuthStore  *gcpauth.Store
}

// ReconcileWorkloadIdentity will create a k8s service account, add ownerReference to it,
// and add iam policy binding between this k8s service account and its corresponding GCP service account.
func (i *Identity) ReconcileWorkloadIdentity(ctx context.Context, projectID string, identifiable duck.Identifiable) (*corev1.ServiceAccount, error) {
	status := identifiable.IdentityStatus()
	// Create corresponding k8s ServiceAccount if it doesn't exist.

	identityNames, err := i.getGoogleServiceAccountName(ctx, identifiable)
	if err != nil {
		logging.FromContext(ctx).Desugar().Error("failed to get Google service account name", zap.Error(err))
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return nil, fmt.Errorf(`failed to get Google service account name: %w`, err)
	} else if identityNames.GoogleServiceAccountName == "" {
		// If there is no Google service account paired with current Kubernetes service account in GCP auth configmap, no further reconciliation.
		return nil, nil
	}

	kServiceAccount, err := i.createServiceAccount(ctx, identityNames)
	if err != nil {
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return nil, fmt.Errorf("failed to get k8s ServiceAccount: %w", err)
	}
	// Add ownerReference to K8s ServiceAccount.
	expectOwnerReference := *kmeta.NewControllerRef(identifiable)
	expectOwnerReference.Controller = ptr.Bool(false)
	if !ownerReferenceExists(kServiceAccount, expectOwnerReference) {
		kServiceAccount.OwnerReferences = append(kServiceAccount.OwnerReferences, expectOwnerReference)
		if _, err := i.kubeClient.CoreV1().ServiceAccounts(kServiceAccount.Namespace).Update(kServiceAccount); err != nil {
			logging.FromContext(ctx).Desugar().Error("Failed to update OwnerReferences", zap.Error(err))
			status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
			return nil, fmt.Errorf("failed to update OwnerReferences: %w", err)
		}
	}

	// Add iam policy binding to GCP ServiceAccount.
	if err := i.addIamPolicyBinding(ctx, projectID, identityNames); err != nil {
		status.MarkWorkloadIdentityFailed(identifiable.ConditionSet(), workloadIdentityFailed, err.Error())
		return kServiceAccount, fmt.Errorf("adding iam policy binding failed with: %w", err)
	}
	status.MarkWorkloadIdentityReady(identifiable.ConditionSet())
	return kServiceAccount, nil
}

// DeleteWorkloadIdentity will remove iam policy binding between k8s service account and its corresponding GCP service account,
// if this k8s service account only has one ownerReference.
func (i *Identity) DeleteWorkloadIdentity(ctx context.Context, projectID string, identifiable duck.Identifiable) error {
	status := identifiable.IdentityStatus()

	identityNames, err := i.getGoogleServiceAccountName(ctx, identifiable)
	if err != nil {
		logging.FromContext(ctx).Desugar().Error("failed to get Google service account name", zap.Error(err))
		status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
		return fmt.Errorf(`failed to get Google service account name: %w`, err)
	} else if identityNames.GoogleServiceAccountName == "" {
		// If there is no Google service account paired with current Kubernetes service account in GCP auth configmap, no further reconciliation.
		return nil
	}

	kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(identityNames.Namespace).Get(identityNames.KServiceAccountName, metav1.GetOptions{})
	if err != nil {
		status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
		// k8s ServiceAccount should be there.
		return fmt.Errorf("getting k8s service account failed with: %w", err)
	}
	if kServiceAccount != nil && len(kServiceAccount.OwnerReferences) == 1 {
		logging.FromContext(ctx).Desugar().Debug("Removing iam policy binding.")
		if err := i.removeIamPolicyBinding(ctx, projectID, identityNames); err != nil {
			status.MarkWorkloadIdentityUnknown(identifiable.ConditionSet(), deleteWorkloadIdentityFailed, err.Error())
			return fmt.Errorf("removing iam policy binding failed with: %w", err)
		}
	}
	return nil
}

// getGoogleServiceAccountName will return Google service account name and corresponding raw Kubernetes service account name.
func (i *Identity) getGoogleServiceAccountName(ctx context.Context, identifiable duck.Identifiable) (resources.IdentityNames, error) {
	namespace := identifiable.GetObjectMeta().GetNamespace()
	ad := i.gcpAuthStore.Load()
	if ad == nil || ad.GCPAuthDefaults == nil {
		logging.FromContext(ctx).Desugar().Error("Failed to get default config from GCP auth configmap")
		return resources.IdentityNames{}, fmt.Errorf("failed to get default config from GCP auth configmap")
	}
	return resources.IdentityNames{
		KServiceAccountName:      identifiable.IdentitySpec().ServiceAccountName,
		GoogleServiceAccountName: ad.GCPAuthDefaults.WorkloadIdentityGSA(namespace, identifiable.IdentitySpec().ServiceAccountName),
		Namespace:                namespace,
	}, nil
}

func (i *Identity) createServiceAccount(ctx context.Context, identityNames resources.IdentityNames) (*corev1.ServiceAccount, error) {
	kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(identityNames.Namespace).Get(identityNames.KServiceAccountName, metav1.GetOptions{})
	if err != nil {
		if apierrs.IsNotFound(err) {
			expect := resources.MakeServiceAccount(identityNames)
			logging.FromContext(ctx).Desugar().Debug("Creating k8s service account", zap.Any("ksa", expect))
			kServiceAccount, err := i.kubeClient.CoreV1().ServiceAccounts(expect.Namespace).Create(expect)
			if err != nil {
				logging.FromContext(ctx).Desugar().Error("Failed to create k8s service account", zap.Error(err))
				return nil, fmt.Errorf("failed to create k8s service account: %w", err)
			}
			return kServiceAccount, nil
		}
		logging.FromContext(ctx).Desugar().Error("Failed to get k8s service account", zap.Error(err))
		return nil, fmt.Errorf("getting k8s service account failed with: %w", err)
	}
	return kServiceAccount, nil
}

// TODO he iam policy binding should be mocked so that we can unit test it. issue https://github.com/google/knative-gcp/issues/657
// addIamPolicyBinding will add iam policy binding, which is related to a provided k8s ServiceAccount, to a GCP ServiceAccount.
func (i *Identity) addIamPolicyBinding(ctx context.Context, projectID string, identityNames resources.IdentityNames) error {
	projectID, err := utils.ProjectID(projectID, metadataClient.NewDefaultMetadataClient())
	if err != nil {
		return fmt.Errorf("failed to get project id: %w", err)
	}

	// currentMember will end up as "serviceAccount:projectId.svc.id.goog[k8s-namespace/ksa-name]".
	currentMember := fmt.Sprintf("serviceAccount:%s.svc.id.goog[%s/%s]", projectID, identityNames.Namespace, identityNames.KServiceAccountName)

	return i.policyManager.AddIAMPolicyBinding(ctx, iam.GServiceAccount(identityNames.GoogleServiceAccountName), currentMember, Role)
}

// removeIamPolicyBinding will remove iam policy binding, which is related to a provided k8s ServiceAccount, from a GCP ServiceAccount.
func (i *Identity) removeIamPolicyBinding(ctx context.Context, projectID string, identityNames resources.IdentityNames) error {
	projectID, err := utils.ProjectID(projectID, metadataClient.NewDefaultMetadataClient())
	if err != nil {
		return fmt.Errorf("failed to get project id: %w", err)
	}

	// currentMember will end up as "serviceAccount:projectId.svc.id.goog[k8s-namespace/ksa-name]".
	currentMember := fmt.Sprintf("serviceAccount:%s.svc.id.goog[%s/%s]", projectID, identityNames.Namespace, identityNames.KServiceAccountName)

	return i.policyManager.RemoveIAMPolicyBinding(ctx, iam.GServiceAccount(identityNames.GoogleServiceAccountName), currentMember, Role)
}

// ownerReferenceExists checks if a K8s ServiceAccount contains specific ownerReference
func ownerReferenceExists(kServiceAccount *corev1.ServiceAccount, expect metav1.OwnerReference) bool {
	references := kServiceAccount.OwnerReferences
	for _, reference := range references {
		if reference.Name == expect.Name {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 releasetesting

import (
	"io"
	"testing"
	"time"

	"k8s.io/kubernetes/pkg/apis/core"

	"k8s.io/helm/pkg/hapi"
	"k8s.io/helm/pkg/hapi/release"
	"k8s.io/helm/pkg/tiller/environment"
)

const manifestWithTestSuccessHook = `
apiVersion: v1
kind: Pod
metadata:
  name: finding-nemo,
  annotations:
    "helm.sh/hook": test-success
spec:
  containers:
  - name: nemo-test
    image: fake-image
    cmd: fake-command
`

const manifestWithTestFailureHook = `
apiVersion: v1
kind: Pod
metadata:
  name: gold-rush,
  annotations:
    "helm.sh/hook": test-failure
spec:
  containers:
  - name: gold-finding-test
    image: fake-gold-finding-image
    cmd: fake-gold-finding-command
`
const manifestWithInstallHooks = `apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  annotations:
    "helm.sh/hook": post-install,pre-delete
data:
  name: value
`

func TestRun(t *testing.T) {
	testManifests := []string{manifestWithTestSuccessHook, manifestWithTestFailureHook}
	ts := testSuiteFixture(testManifests)
	env := testEnvFixture()

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	for i := 0; i <= 4; i++ {
		<-env.Mesages
	}
	if _, ok := <-env.Mesages; ok {
		t.Errorf("Expected 4 messages streamed")
	}

	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 2 {
		t.Errorf("Expected 2 test result. Got %v", len(ts.Results))
	}

	result := ts.Results[0]
	if result.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt)
	}
	if result.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt)
	}
	if result.Name != "finding-nemo" {
		t.Errorf("Expected test name to be finding-nemo. Got: %v", result.Name)
	}
	if result.Status != release.TestRun_SUCCESS {
		t.Errorf("Expected test result to be successful, got: %v", result.Status)
	}
	result2 := ts.Results[1]
	if result2.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result2.StartedAt)
	}
	if result2.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result2.CompletedAt)
	}
	if result2.Name != "gold-rush" {
		t.Errorf("Expected test name to be gold-rush, Got: %v", result2.Name)
	}
	if result2.Status != release.TestRun_FAILURE {
		t.Errorf("Expected test result to be successful, got: %v", result2.Status)
	}
}

func TestRunEmptyTestSuite(t *testing.T) {
	ts := testSuiteFixture([]string{})
	env := testEnvFixture()

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	msg := <-env.Mesages
	if msg.Msg != "No Tests Found" {
		t.Errorf("Expected message 'No Tests Found', Got: %v", msg.Msg)
	}
	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 0 {
		t.Errorf("Expected 0 test result. Got %v", len(ts.Results))
	}
}

func TestRunSuccessWithTestFailureHook(t *testing.T) {
	ts := testSuiteFixture([]string{manifestWithTestFailureHook})
	env := testEnvFixture()
	env.KubeClient = &mockKubeClient{podFail: true}

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	for i := 0; i <= 4; i++ {
		<-env.Mesages
	}
	if _, ok := <-env.Mesages; ok {
		t.Errorf("Expected 4 messages streamed")
	}

	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 1 {
		t.Errorf("Expected 1 test result. Got %v", len(ts.Results))
	}

	result := ts.Results[0]
	if result.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt)
	}
	if result.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt)
	}
	if result.Name != "gold-rush" {
		t.Errorf("Expected test name to be gold-rush, Got: %v", result.Name)
	}
	if result.Status != release.TestRun_SUCCESS {
		t.Errorf("Expected test result to be successful, got: %v", result.Status)
	}
}

func TestExtractTestManifestsFromHooks(t *testing.T) {
	testManifests := extractTestManifestsFromHooks(hooksStub)

	if len(testManifests) != 1 {
		t.Errorf("Expected 1 test manifest, Got: %v", len(testManifests))
	}
}

var hooksStub = []*release.Hook{
	{
		Manifest: manifestWithTestSuccessHook,
		Events: []release.HookEvent{
			release.Hook_RELEASE_TEST_SUCCESS,
		},
	},
	{
		Manifest: manifestWithInstallHooks,
		Events: []release.HookEvent{
			release.Hook_POST_INSTALL,
		},
	},
}

func testFixture() *test {
	return &test{
		manifest: manifestWithTestSuccessHook,
		result:   &release.TestRun{},
	}
}

func testSuiteFixture(testManifests []string) *TestSuite {
	testResults := []*release.TestRun{}
	ts := &TestSuite{
		TestManifests: testManifests,
		Results:       testResults,
	}
	return ts
}

func testEnvFixture() *Environment {
	return &Environment{
		Namespace:  "default",
		KubeClient: &mockKubeClient{},
		Timeout:    1,
		Mesages:    make(chan *hapi.TestReleaseResponse, 1),
	}
}

type mockKubeClient struct {
	environment.KubeClient
	podFail bool
	err     error
}

func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (core.PodPhase, error) {
	if c.podFail {
		return core.PodFailed, nil
	}
	return core.PodSucceeded, nil
}
func (c *mockKubeClient) Get(_ string, _ io.Reader) (string, error) {
	return "", nil
}
func (c *mockKubeClient) Create(_ string, _ io.Reader, _ int64, _ bool) error {
	return c.err
}
func (c *mockKubeClient) Delete(_ string, _ io.Reader) error {
	return nil
}
<commit_msg>fix(tests): fix race in releasetesting test<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 releasetesting

import (
	"io"
	"testing"
	"time"

	"k8s.io/kubernetes/pkg/apis/core"

	"k8s.io/helm/pkg/hapi"
	"k8s.io/helm/pkg/hapi/release"
	"k8s.io/helm/pkg/tiller/environment"
)

const manifestWithTestSuccessHook = `
apiVersion: v1
kind: Pod
metadata:
  name: finding-nemo,
  annotations:
    "helm.sh/hook": test-success
spec:
  containers:
  - name: nemo-test
    image: fake-image
    cmd: fake-command
`

const manifestWithTestFailureHook = `
apiVersion: v1
kind: Pod
metadata:
  name: gold-rush,
  annotations:
    "helm.sh/hook": test-failure
spec:
  containers:
  - name: gold-finding-test
    image: fake-gold-finding-image
    cmd: fake-gold-finding-command
`
const manifestWithInstallHooks = `apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  annotations:
    "helm.sh/hook": post-install,pre-delete
data:
  name: value
`

func TestRun(t *testing.T) {
	testManifests := []string{manifestWithTestSuccessHook, manifestWithTestFailureHook}
	ts := testSuiteFixture(testManifests)
	env := testEnvFixture()

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	for i := 0; i <= 4; i++ {
		<-env.Mesages
	}
	if _, ok := <-env.Mesages; ok {
		t.Errorf("Expected 4 messages streamed")
	}

	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 2 {
		t.Errorf("Expected 2 test result. Got %v", len(ts.Results))
	}

	result := ts.Results[0]
	if result.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt)
	}
	if result.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt)
	}
	if result.Name != "finding-nemo" {
		t.Errorf("Expected test name to be finding-nemo. Got: %v", result.Name)
	}
	if result.Status != release.TestRun_SUCCESS {
		t.Errorf("Expected test result to be successful, got: %v", result.Status)
	}
	result2 := ts.Results[1]
	if result2.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result2.StartedAt)
	}
	if result2.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result2.CompletedAt)
	}
	if result2.Name != "gold-rush" {
		t.Errorf("Expected test name to be gold-rush, Got: %v", result2.Name)
	}
	if result2.Status != release.TestRun_FAILURE {
		t.Errorf("Expected test result to be successful, got: %v", result2.Status)
	}
}

func TestRunEmptyTestSuite(t *testing.T) {
	ts := testSuiteFixture([]string{})
	env := testEnvFixture()

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	msg := <-env.Mesages
	if msg.Msg != "No Tests Found" {
		t.Errorf("Expected message 'No Tests Found', Got: %v", msg.Msg)
	}

	for range env.Mesages {
	}

	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 0 {
		t.Errorf("Expected 0 test result. Got %v", len(ts.Results))
	}
}

func TestRunSuccessWithTestFailureHook(t *testing.T) {
	ts := testSuiteFixture([]string{manifestWithTestFailureHook})
	env := testEnvFixture()
	env.KubeClient = &mockKubeClient{podFail: true}

	go func() {
		defer close(env.Mesages)
		if err := ts.Run(env); err != nil {
			t.Error(err)
		}
	}()

	for i := 0; i <= 4; i++ {
		<-env.Mesages
	}
	if _, ok := <-env.Mesages; ok {
		t.Errorf("Expected 4 messages streamed")
	}

	if ts.StartedAt.IsZero() {
		t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt)
	}
	if ts.CompletedAt.IsZero() {
		t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt)
	}
	if len(ts.Results) != 1 {
		t.Errorf("Expected 1 test result. Got %v", len(ts.Results))
	}

	result := ts.Results[0]
	if result.StartedAt.IsZero() {
		t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt)
	}
	if result.CompletedAt.IsZero() {
		t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt)
	}
	if result.Name != "gold-rush" {
		t.Errorf("Expected test name to be gold-rush, Got: %v", result.Name)
	}
	if result.Status != release.TestRun_SUCCESS {
		t.Errorf("Expected test result to be successful, got: %v", result.Status)
	}
}

func TestExtractTestManifestsFromHooks(t *testing.T) {
	testManifests := extractTestManifestsFromHooks(hooksStub)

	if len(testManifests) != 1 {
		t.Errorf("Expected 1 test manifest, Got: %v", len(testManifests))
	}
}

var hooksStub = []*release.Hook{
	{
		Manifest: manifestWithTestSuccessHook,
		Events: []release.HookEvent{
			release.Hook_RELEASE_TEST_SUCCESS,
		},
	},
	{
		Manifest: manifestWithInstallHooks,
		Events: []release.HookEvent{
			release.Hook_POST_INSTALL,
		},
	},
}

func testFixture() *test {
	return &test{
		manifest: manifestWithTestSuccessHook,
		result:   &release.TestRun{},
	}
}

func testSuiteFixture(testManifests []string) *TestSuite {
	testResults := []*release.TestRun{}
	ts := &TestSuite{
		TestManifests: testManifests,
		Results:       testResults,
	}
	return ts
}

func testEnvFixture() *Environment {
	return &Environment{
		Namespace:  "default",
		KubeClient: &mockKubeClient{},
		Timeout:    1,
		Mesages:    make(chan *hapi.TestReleaseResponse, 1),
	}
}

type mockKubeClient struct {
	environment.KubeClient
	podFail bool
	err     error
}

func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (core.PodPhase, error) {
	if c.podFail {
		return core.PodFailed, nil
	}
	return core.PodSucceeded, nil
}
func (c *mockKubeClient) Get(_ string, _ io.Reader) (string, error) {
	return "", nil
}
func (c *mockKubeClient) Create(_ string, _ io.Reader, _ int64, _ bool) error {
	return c.err
}
func (c *mockKubeClient) Delete(_ string, _ io.Reader) error {
	return nil
}
<|endoftext|>