<commit_before>/*
 Copyright 2016 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 distributed under the License is distributed on an "AS IS" BASIS,
 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
 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 programs

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/utils"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"github.com/linkosmos/mapop"
)

var (
	programs     []Program = make([]Program, 0)
	ServerFolder string = utils.JoinPath("data", "servers")
)

func LoadFromFolder() {
	os.Mkdir(ServerFolder, 0755)
	var programFiles, err = ioutil.ReadDir(ServerFolder)
	if err != nil {
		logging.Critical("Error reading from server data folder", err)
	}
	var program Program
	for _, element := range programFiles {
		if element.IsDir() {
			continue
		}
		id := strings.TrimSuffix(element.Name(), filepath.Ext(element.Name()))
		program, err = Load(id)
		if err != nil {
			logging.Error(fmt.Sprintf("Error loading server details from json (%s)", element.Name()), err)
			continue
		}
		logging.Infof("Loaded server %s", program.Id())
		programs = append(programs, program)
	}
}

func Get(id string) (program Program, err error) {
	program = GetFromCache(id)
	if program == nil {
		program, err = Load(id)
	}
	return
}

func GetAll() []Program {
	return programs
}

func Load(id string) (program Program, err error) {
	var data []byte
	data, err = ioutil.ReadFile(utils.JoinPath(ServerFolder, id + ".json"))
	if len(data) == 0 || err != nil {
		return
	}

	program, err = LoadFromData(id, data)
	return
}

func LoadFromData(id string, source []byte) (program Program, err error) {
	var data map[string]interface{}
	err = json.Unmarshal(source, &data)
	if err != nil {
		return
	}
	program, err = LoadFromMapping(id, data)
	return
}

func LoadFromMapping(id string, source map[string]interface{}) (program Program, err error) {
	var pufferdData = utils.GetMapOrNull(source, "pufferd")
	var installSection = getInstallSection(utils.GetMapOrNull(pufferdData, "install"))
	var runSection = utils.GetMapOrNull(pufferdData, "run")
	var environmentSection = utils.GetMapOrNull(runSection, "environment")
	var environment environments.Environment
	var defaultEnvType = "system"
	var environmentType = utils.GetStringOrDefault(environmentSection, "type", defaultEnvType)
	var dataSection = utils.GetMapOrNull(pufferdData, "data")
	dataCasted := make(map[string]interface{}, len(dataSection))
	for key, value := range dataSection {
		dataCasted[key] = value
	}

	switch environmentType {
	case "system":
		serverRoot := utils.JoinPath(ServerFolder, id)
		environment = &environments.System{RootDirectory: utils.GetStringOrDefault(environmentSection, "root", serverRoot), ConsoleBuffer: utils.CreateCache(), WSManager: utils.CreateWSManager()}
	}

	var runBlock Runtime
	if pufferdData["run"] == nil {
		runBlock = Runtime{}
	} else {
		var stop = utils.GetStringOrDefault(runSection, "stop", "")
		var pre = utils.GetStringArrayOrNull(runSection, "pre")
		var post = utils.GetStringArrayOrNull(runSection, "post")
		var arguments = utils.GetStringArrayOrNull(runSection, "arguments")
		var enabled = utils.GetBooleanOrDefault(runSection, "enabled", true)
		var autostart = utils.GetBooleanOrDefault(runSection, "autostart", true)
		var program = utils.GetStringOrDefault(runSection, "program", "")
		runBlock = Runtime{Stop: stop, Pre: pre, Post: post, Arguments: arguments, Enabled: enabled, AutoStart: autostart, Program: program}
	}
	program = &ProgramStruct{Data: dataCasted, Identifier: id, RunData: runBlock, InstallData: installSection, Environment: environment}
	return
}

func Create(id string, serverType string, data map[string]interface{}) bool {
	if GetFromCache(id) != nil {
		return false
	}

	templateData, err := ioutil.ReadFile(utils.JoinPath(templates.Folder, serverType + ".json"))

	var templateJson map[string]interface{}
	err = json.Unmarshal(templateData, &templateJson)
	segment := utils.GetMapOrNull(templateJson, "pufferd")

	if err != nil {
		logging.Error("Error reading template file for type " + serverType, err)
		return false
	}

	if data != nil {
		var mapper map[string]interface{}
		mapper = segment["data"].(map[string]interface{})
		for k, v := range data {
			newMap := make(map[string]interface{})
			newMap["value"] = v
			newMap["desc"] = "No description"
			newMap["display"] = k
			newMap["required"] = false
			newMap["internal"] = true
			if mapper[k] == nil {
				mapper[k] = newMap
			} else {
				mapper[k] = mapop.Merge(newMap, mapper[k].(map[string]interface{}))
			}
		}
		segment["data"] = mapper
	}

	templateData, _ = json.Marshal(templateJson)
	err = ioutil.WriteFile(utils.JoinPath(ServerFolder, id + ".json"), templateData, 0644)

	if err != nil {
		logging.Error("Error writing server file", err)
		return false
	}

	program, _ := LoadFromMapping(id, templateJson)
	programs = append(programs, program)
	program.Create()
	return true
}

func Delete(id string) (err error) {
	var index int
	var program Program
	for i, element := range programs {
		if element.Id() == id {
			program = element
			index = i
			break
		}
	}
	if program == nil {
		return
	}

	err = program.Destroy()
	os.Remove(utils.JoinPath(ServerFolder, program.Id() + ".json"))
	programs = append(programs[:index], programs[index + 1:]...)
	return
}

func GetFromCache(id string) Program {
	for _, element := range programs {
		if element.Id() == id {
			return element
		}
	}
	return nil
}

func Save(id string) (err error) {
	program := GetFromCache(id)
	if program == nil {
		err = errors.New("No server with given id")
		return
	}
	err = program.Save(utils.JoinPath(ServerFolder, id + ".json"))
	return
}

func Reload(id string) error {
	oldPg, err := Get(id)
	if err != nil {
		return err
	}

	var newPg Program

	newPg, err = Load(id)
	if err != nil {
		return err
	}

	oldPg.Reload(newPg)
	return nil
}

func GetPlugins() map[string]interface{} {

	temps, _ := ioutil.ReadDir(templates.Folder)

	mapping := make(map[string]interface{})

	for _, element := range temps {
		if element.IsDir() {
			continue
		}
		name := strings.TrimSuffix(element.Name(), filepath.Ext(element.Name()))
		templateData, _ := ioutil.ReadFile(utils.JoinPath(templates.Folder, name + ".json"))

		var templateJson map[string]interface{}
		json.Unmarshal(templateData, &templateJson)
		segment := utils.GetMapOrNull(templateJson, "pufferd")
		dataSec := segment["data"].(map[string]interface{})
		mapping[name] = dataSec
	}

	return mapping
}

func getInstallSection(mapping map[string]interface{}) install.InstallSection {
	return install.InstallSection{
		Global:  utils.GetObjectArrayOrNull(mapping, "commands"),
		Linux:   utils.GetObjectArrayOrNull(mapping, "linux"),
		Mac:     utils.GetObjectArrayOrNull(mapping, "mac"),
		Windows: utils.GetObjectArrayOrNull(mapping, "windows"),
	}
}
<commit_msg>Pretty-print server JSON<commit_after>/*
 Copyright 2016 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 distributed under the License is distributed on an "AS IS" BASIS,
 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
 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 programs

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/linkosmos/mapop"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/utils"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	programs     []Program = make([]Program, 0)
	ServerFolder string    = utils.JoinPath("data", "servers")
)

func LoadFromFolder() {
	os.Mkdir(ServerFolder, 0755)
	var programFiles, err = ioutil.ReadDir(ServerFolder)
	if err != nil {
		logging.Critical("Error reading from server data folder", err)
	}
	var program Program
	for _, element := range programFiles {
		if element.IsDir() {
			continue
		}
		id := strings.TrimSuffix(element.Name(), filepath.Ext(element.Name()))
		program, err = Load(id)
		if err != nil {
			logging.Error(fmt.Sprintf("Error loading server details from json (%s)", element.Name()), err)
			continue
		}
		logging.Infof("Loaded server %s", program.Id())
		programs = append(programs, program)
	}
}

func Get(id string) (program Program, err error) {
	program = GetFromCache(id)
	if program == nil {
		program, err = Load(id)
	}
	return
}

func GetAll() []Program {
	return programs
}

func Load(id string) (program Program, err error) {
	var data []byte
	data, err = ioutil.ReadFile(utils.JoinPath(ServerFolder, id+".json"))
	if len(data) == 0 || err != nil {
		return
	}

	program, err = LoadFromData(id, data)
	return
}

func LoadFromData(id string, source []byte) (program Program, err error) {
	var data map[string]interface{}
	err = json.Unmarshal(source, &data)
	if err != nil {
		return
	}
	program, err = LoadFromMapping(id, data)
	return
}

func LoadFromMapping(id string, source map[string]interface{}) (program Program, err error) {
	var pufferdData = utils.GetMapOrNull(source, "pufferd")
	var installSection = getInstallSection(utils.GetMapOrNull(pufferdData, "install"))
	var runSection = utils.GetMapOrNull(pufferdData, "run")
	var environmentSection = utils.GetMapOrNull(runSection, "environment")
	var environment environments.Environment
	var defaultEnvType = "system"
	var environmentType = utils.GetStringOrDefault(environmentSection, "type", defaultEnvType)
	var dataSection = utils.GetMapOrNull(pufferdData, "data")
	dataCasted := make(map[string]interface{}, len(dataSection))
	for key, value := range dataSection {
		dataCasted[key] = value
	}

	switch environmentType {
	case "system":
		serverRoot := utils.JoinPath(ServerFolder, id)
		environment = &environments.System{RootDirectory: utils.GetStringOrDefault(environmentSection, "root", serverRoot), ConsoleBuffer: utils.CreateCache(), WSManager: utils.CreateWSManager()}
	}

	var runBlock Runtime
	if pufferdData["run"] == nil {
		runBlock = Runtime{}
	} else {
		var stop = utils.GetStringOrDefault(runSection, "stop", "")
		var pre = utils.GetStringArrayOrNull(runSection, "pre")
		var post = utils.GetStringArrayOrNull(runSection, "post")
		var arguments = utils.GetStringArrayOrNull(runSection, "arguments")
		var enabled = utils.GetBooleanOrDefault(runSection, "enabled", true)
		var autostart = utils.GetBooleanOrDefault(runSection, "autostart", true)
		var program = utils.GetStringOrDefault(runSection, "program", "")
		runBlock = Runtime{Stop: stop, Pre: pre, Post: post, Arguments: arguments, Enabled: enabled, AutoStart: autostart, Program: program}
	}
	program = &ProgramStruct{Data: dataCasted, Identifier: id, RunData: runBlock, InstallData: installSection, Environment: environment}
	return
}

func Create(id string, serverType string, data map[string]interface{}) bool {
	if GetFromCache(id) != nil {
		return false
	}

	templateData, err := ioutil.ReadFile(utils.JoinPath(templates.Folder, serverType+".json"))

	var templateJson map[string]interface{}
	err = json.Unmarshal(templateData, &templateJson)
	segment := utils.GetMapOrNull(templateJson, "pufferd")

	if err != nil {
		logging.Error("Error reading template file for type "+serverType, err)
		return false
	}

	if data != nil {
		var mapper map[string]interface{}
		mapper = segment["data"].(map[string]interface{})
		for k, v := range data {
			newMap := make(map[string]interface{})
			newMap["value"] = v
			newMap["desc"] = "No description"
			newMap["display"] = k
			newMap["required"] = false
			newMap["internal"] = true
			if mapper[k] == nil {
				mapper[k] = newMap
			} else {
				mapper[k] = mapop.Merge(newMap, mapper[k].(map[string]interface{}))
			}
		}
		segment["data"] = mapper
	}

	templateData, _ = json.MarshalIndent(templateJson, "", "  ")
	err = ioutil.WriteFile(utils.JoinPath(ServerFolder, id+".json"), templateData, 0644)

	if err != nil {
		logging.Error("Error writing server file", err)
		return false
	}

	program, _ := LoadFromMapping(id, templateJson)
	programs = append(programs, program)
	program.Create()
	return true
}

func Delete(id string) (err error) {
	var index int
	var program Program
	for i, element := range programs {
		if element.Id() == id {
			program = element
			index = i
			break
		}
	}
	if program == nil {
		return
	}

	err = program.Destroy()
	os.Remove(utils.JoinPath(ServerFolder, program.Id()+".json"))
	programs = append(programs[:index], programs[index+1:]...)
	return
}

func GetFromCache(id string) Program {
	for _, element := range programs {
		if element.Id() == id {
			return element
		}
	}
	return nil
}

func Save(id string) (err error) {
	program := GetFromCache(id)
	if program == nil {
		err = errors.New("No server with given id")
		return
	}
	err = program.Save(utils.JoinPath(ServerFolder, id+".json"))
	return
}

func Reload(id string) error {
	oldPg, err := Get(id)
	if err != nil {
		return err
	}

	var newPg Program

	newPg, err = Load(id)
	if err != nil {
		return err
	}

	oldPg.Reload(newPg)
	return nil
}

func GetPlugins() map[string]interface{} {

	temps, _ := ioutil.ReadDir(templates.Folder)

	mapping := make(map[string]interface{})

	for _, element := range temps {
		if element.IsDir() {
			continue
		}
		name := strings.TrimSuffix(element.Name(), filepath.Ext(element.Name()))
		templateData, _ := ioutil.ReadFile(utils.JoinPath(templates.Folder, name+".json"))

		var templateJson map[string]interface{}
		json.Unmarshal(templateData, &templateJson)
		segment := utils.GetMapOrNull(templateJson, "pufferd")
		dataSec := segment["data"].(map[string]interface{})
		mapping[name] = dataSec
	}

	return mapping
}

func getInstallSection(mapping map[string]interface{}) install.InstallSection {
	return install.InstallSection{
		Global:  utils.GetObjectArrayOrNull(mapping, "commands"),
		Linux:   utils.GetObjectArrayOrNull(mapping, "linux"),
		Mac:     utils.GetObjectArrayOrNull(mapping, "mac"),
		Windows: utils.GetObjectArrayOrNull(mapping, "windows"),
	}
}
<|endoftext|>


<commit_before>package mssql

import (
	"database/sql"
	"encoding/hex"
	"log"
	"math"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestBulkcopy(t *testing.T) {
	// TDS level Bulk Insert is not supported on Azure SQL Server.
	if dsn := makeConnStr(t); strings.HasSuffix(strings.Split(dsn.Host, ":")[0], ".database.windows.net") {
		t.Skip("TDS level bulk copy is not supported on Azure SQL Server")
	}
	type testValue struct {
		colname string
		val     interface{}
	}
	tableName := "#table_test"
	geom, _ := hex.DecodeString("E6100000010C00000000000034400000000000004440")
	testValues := []testValue{

		{"test_nvarchar", "ab©ĎéⒻghïjklmnopqЯ☀tuvwxyz"},
		{"test_varchar", "abcdefg"},
		{"test_char", "abcdefg   "},
		{"test_nchar", "abcdefg   "},
		{"test_text", "abcdefg"},
		{"test_ntext", "abcdefg"},
		{"test_float", 1234.56},
		{"test_floatn", 1234.56},
		{"test_real", 1234.56},
		{"test_realn", 1234.56},
		{"test_bit", true},
		{"test_bitn", nil},
		{"test_smalldatetime", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_smalldatetimen", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_datetime", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetimen", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetime2_1", time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)},
		{"test_datetime2_3", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_datetime2_7", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_date", time.Date(2010, 11, 12, 00, 00, 00, 0, time.UTC)},
		{"test_tinyint", 255},
		{"test_smallint", 32767},
		{"test_smallintn", nil},
		{"test_int", 2147483647},
		{"test_bigint", 9223372036854775807},
		{"test_bigintn", nil},
		{"test_geom", geom},
		//{"test_smallmoney", nil},
		//{"test_money", nil},
		//{"test_decimal_18_0", nil},
		//{"test_decimal_9_2", nil},
		//{"test_decimal_18_0", nil},
	}

	columns := make([]string, len(testValues))
	for i, val := range testValues {
		columns[i] = val.colname
	}

	values := make([]interface{}, len(testValues))
	for i, val := range testValues {
		values[i] = val.val
	}

	conn := open(t)
	defer conn.Close()

	setupTable(conn, tableName)

	stmt, err := conn.Prepare(CopyIn(tableName, MssqlBulkOptions{}, columns...))

	for i := 0; i < 10; i++ {
		_, err = stmt.Exec(values...)
		if err != nil {
			t.Error("AddRow failed: ", err.Error())
			return
		}
	}

	result, err := stmt.Exec()
	if err != nil {
		t.Fatal("bulkcopy failed: ", err.Error())
	}

	insertedRowCount, _ := result.RowsAffected()
	if insertedRowCount == 0 {
		t.Fatal("0 row inserted!")
	}

	//check that all rows are present
	var rowCount int
	err = conn.QueryRow("select count(*) c from " + tableName).Scan(&rowCount)

	if rowCount != 10 {
		t.Errorf("unexpected row count %d", rowCount)
	}

	//data verification
	rows, err := conn.Query("select " + strings.Join(columns, ",") + " from " + tableName)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {

		ptrs := make([]interface{}, len(columns))
		container := make([]interface{}, len(columns))
		for i, _ := range ptrs {
			ptrs[i] = &container[i]
		}
		if err := rows.Scan(ptrs...); err != nil {
			t.Fatal(err)
		}
		for i, c := range testValues {
			if !compareValue(container[i], c.val) {
				t.Errorf("columns %s : %s != %s\n", c.colname, container[i], c.val)
			}
		}
	}
	if err := rows.Err(); err != nil {
		t.Error(err)
	}
}

func compareValue(a interface{}, expected interface{}) bool {
	switch expected := expected.(type) {
	case int:
		return int64(expected) == a
	case int32:
		return int64(expected) == a
	case int64:
		return int64(expected) == a
	case float64:
		return math.Abs(expected-a.(float64)) < 0.0001
	default:
		return reflect.DeepEqual(expected, a)
	}
}
func setupTable(conn *sql.DB, tableName string) {

	tablesql := `CREATE TABLE ` + tableName + ` (
	[id] [int] IDENTITY(1,1) NOT NULL,
	[test_nvarchar] [nvarchar](50) NULL,
	[test_varchar] [varchar](50) NULL,
	[test_char] [char](10) NULL,
	[test_nchar] [nchar](10) NULL,
	[test_text] [text] NULL,
	[test_ntext] [ntext] NULL,
	[test_float] [float] NOT NULL,
	[test_floatn] [float] NULL,
	[test_real] [real] NULL,
	[test_realn] [real] NULL,
	[test_bit] [bit] NOT NULL,
	[test_bitn] [bit] NULL,
	[test_smalldatetime] [smalldatetime] NOT NULL,
	[test_smalldatetimen] [smalldatetime] NULL,
	[test_datetime] [datetime] NOT NULL,
	[test_datetimen] [datetime] NULL,
	[test_datetime2_1] [datetime2](1) NULL,
	[test_datetime2_3] [datetime2](3) NULL,
	[test_datetime2_7] [datetime2](7) NULL,
	[test_date] [date] NULL,
	[test_smallmoney] [smallmoney] NULL,
	[test_money] [money] NULL,
	[test_tinyint] [tinyint] NULL,
	[test_smallint] [smallint] NOT NULL,
	[test_smallintn] [smallint] NULL,
	[test_int] [int] NULL,
	[test_bigint] [bigint] NOT NULL,
	[test_bigintn] [bigint] NULL,
	[test_geom] [geometry] NULL,
	[test_geog] [geography] NULL,
	[text_xml] [xml] NULL,
	[test_uniqueidentifier] [uniqueidentifier] NULL,
	[test_decimal_18_0] [decimal](18, 0) NULL,
	[test_decimal_9_2] [decimal](9, 2) NULL,
	[test_decimal_20_0] [decimal](20, 0) NULL,
 CONSTRAINT [PK_` + tableName + `_id] PRIMARY KEY CLUSTERED
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];`
	_, err := conn.Exec(tablesql)
	if err != nil {
		log.Fatal("tablesql failed:", err)
	}

}
<commit_msg>improve bulkload test<commit_after>package mssql

import (
	"database/sql"
	"encoding/hex"
	"log"
	"math"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestBulkcopy(t *testing.T) {
	// TDS level Bulk Insert is not supported on Azure SQL Server.
	if dsn := makeConnStr(t); strings.HasSuffix(strings.Split(dsn.Host, ":")[0], ".database.windows.net") {
		t.Skip("TDS level bulk copy is not supported on Azure SQL Server")
	}
	type testValue struct {
		colname string
		val     interface{}
	}
	tableName := "#table_test"
	geom, _ := hex.DecodeString("E6100000010C00000000000034400000000000004440")
	testValues := []testValue{

		{"test_nvarchar", "ab©ĎéⒻghïjklmnopqЯ☀tuvwxyz"},
		{"test_varchar", "abcdefg"},
		{"test_char", "abcdefg   "},
		{"test_nchar", "abcdefg   "},
		{"test_text", "abcdefg"},
		{"test_ntext", "abcdefg"},
		{"test_float", 1234.56},
		{"test_floatn", 1234.56},
		{"test_real", 1234.56},
		{"test_realn", 1234.56},
		{"test_bit", true},
		{"test_bitn", nil},
		{"test_smalldatetime", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_smalldatetimen", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_datetime", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetimen", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetime2_1", time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)},
		{"test_datetime2_3", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_datetime2_7", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_date", time.Date(2010, 11, 12, 00, 00, 00, 0, time.UTC)},
		{"test_tinyint", 255},
		{"test_smallint", 32767},
		{"test_smallintn", nil},
		{"test_int", 2147483647},
		{"test_bigint", 9223372036854775807},
		{"test_bigintn", nil},
		{"test_geom", geom},
		//{"test_smallmoney", nil},
		//{"test_money", nil},
		//{"test_decimal_18_0", nil},
		//{"test_decimal_9_2", nil},
		//{"test_decimal_18_0", nil},
	}

	columns := make([]string, len(testValues))
	for i, val := range testValues {
		columns[i] = val.colname
	}

	values := make([]interface{}, len(testValues))
	for i, val := range testValues {
		values[i] = val.val
	}

	conn := open(t)
	defer conn.Close()

	err := setupTable(conn, tableName)
	if (err != nil) {
		t.Error("Setup table failed: ", err.Error())
		return
	}

	stmt, err := conn.Prepare(CopyIn(tableName, MssqlBulkOptions{}, columns...))

	for i := 0; i < 10; i++ {
		_, err = stmt.Exec(values...)
		if err != nil {
			t.Error("AddRow failed: ", err.Error())
			return
		}
	}

	result, err := stmt.Exec()
	if err != nil {
		t.Fatal("bulkcopy failed: ", err.Error())
	}

	insertedRowCount, _ := result.RowsAffected()
	if insertedRowCount == 0 {
		t.Fatal("0 row inserted!")
	}

	//check that all rows are present
	var rowCount int
	err = conn.QueryRow("select count(*) c from " + tableName).Scan(&rowCount)

	if rowCount != 10 {
		t.Errorf("unexpected row count %d", rowCount)
	}

	//data verification
	rows, err := conn.Query("select " + strings.Join(columns, ",") + " from " + tableName)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {

		ptrs := make([]interface{}, len(columns))
		container := make([]interface{}, len(columns))
		for i, _ := range ptrs {
			ptrs[i] = &container[i]
		}
		if err := rows.Scan(ptrs...); err != nil {
			t.Fatal(err)
		}
		for i, c := range testValues {
			if !compareValue(container[i], c.val) {
				t.Errorf("columns %s : %s != %s\n", c.colname, container[i], c.val)
			}
		}
	}
	if err := rows.Err(); err != nil {
		t.Error(err)
	}
}

func compareValue(a interface{}, expected interface{}) bool {
	switch expected := expected.(type) {
	case int:
		return int64(expected) == a
	case int32:
		return int64(expected) == a
	case int64:
		return int64(expected) == a
	case float64:
		return math.Abs(expected-a.(float64)) < 0.0001
	default:
		return reflect.DeepEqual(expected, a)
	}
}

func setupTable(conn *sql.DB, tableName string) (err error) {
	tablesql := `CREATE TABLE ` + tableName + ` (
	[id] [int] IDENTITY(1,1) NOT NULL,
	[test_nvarchar] [nvarchar](50) NULL,
	[test_varchar] [varchar](50) NULL,
	[test_char] [char](10) NULL,
	[test_nchar] [nchar](10) NULL,
	[test_text] [text] NULL,
	[test_ntext] [ntext] NULL,
	[test_float] [float] NOT NULL,
	[test_floatn] [float] NULL,
	[test_real] [real] NULL,
	[test_realn] [real] NULL,
	[test_bit] [bit] NOT NULL,
	[test_bitn] [bit] NULL,
	[test_smalldatetime] [smalldatetime] NOT NULL,
	[test_smalldatetimen] [smalldatetime] NULL,
	[test_datetime] [datetime] NOT NULL,
	[test_datetimen] [datetime] NULL,
	[test_datetime2_1] [datetime2](1) NULL,
	[test_datetime2_3] [datetime2](3) NULL,
	[test_datetime2_7] [datetime2](7) NULL,
	[test_date] [date] NULL,
	[test_smallmoney] [smallmoney] NULL,
	[test_money] [money] NULL,
	[test_tinyint] [tinyint] NULL,
	[test_smallint] [smallint] NOT NULL,
	[test_smallintn] [smallint] NULL,
	[test_int] [int] NULL,
	[test_bigint] [bigint] NOT NULL,
	[test_bigintn] [bigint] NULL,
	[test_geom] [geometry] NULL,
	[test_geog] [geography] NULL,
	[text_xml] [xml] NULL,
	[test_uniqueidentifier] [uniqueidentifier] NULL,
	[test_decimal_18_0] [decimal](18, 0) NULL,
	[test_decimal_9_2] [decimal](9, 2) NULL,
	[test_decimal_20_0] [decimal](20, 0) NULL,
 CONSTRAINT [PK_` + tableName + `_id] PRIMARY KEY CLUSTERED
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];`
	_, err = conn.Exec(tablesql)
	if err != nil {
		log.Fatal("tablesql failed:", err)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"log"
	"os"

	"github.com/gyuho/psn/ps"
)

func main() {
	rsPaths := []string{}
	tb, err := ps.ReadCSVs(rsPaths...)
	if err != nil {
		log.Fatal(err)
	}

	if err != toCSV(tb.Columns, tb.Rows, "results.csv"); err != nil {
		log.Fatal(err)
	}
}

func toCSV(header []string, rows [][]string, fpath string) error {
	f, err := os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0777)
	if err != nil {
		f, err = os.Create(fpath)
		if err != nil {
			return err
		}
	}
	defer f.Close()

	// func NewWriter(w io.Writer) *Writer
	wr := csv.NewWriter(f)

	if err := wr.Write(header); err != nil {
		return err
	}

	if err := wr.WriteAll(rows); err != nil {
		return err
	}

	wr.Flush()
	return wr.Error()
}
<commit_msg>ps/scripts: fix<commit_after>package main

import (
	"encoding/csv"
	"log"
	"os"

	"github.com/gyuho/psn/ps"
)

func main() {
	rsPaths := []string{}
	tb, err := ps.ReadCSVs(rsPaths...)
	if err != nil {
		log.Fatal(err)
	}

	cols := make([]string, len(tb.Columns))
	for k, v := range tb.Columns {
		cols[v] = k
	}
	if err := toCSV(cols, tb.Rows, "results.csv"); err != nil {
		log.Fatal(err)
	}
}

func toCSV(header []string, rows [][]string, fpath string) error {
	f, err := os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0777)
	if err != nil {
		f, err = os.Create(fpath)
		if err != nil {
			return err
		}
	}
	defer f.Close()

	// func NewWriter(w io.Writer) *Writer
	wr := csv.NewWriter(f)

	if err := wr.Write(header); err != nil {
		return err
	}

	if err := wr.WriteAll(rows); err != nil {
		return err
	}

	wr.Flush()
	return wr.Error()
}
<|endoftext|>


<commit_before>package loadtest

import (
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/actions/registry"
	"github.com/loadimpact/speedboat/master"
	"github.com/loadimpact/speedboat/message"
	"github.com/loadimpact/speedboat/runner"
	"github.com/loadimpact/speedboat/runner/js"
	"github.com/loadimpact/speedboat/worker"
)

func init() {
	registry.RegisterProcessor(func(*worker.Worker) master.Processor {
		return &LoadTestProcessor{}
	})
}

type LoadTestProcessor struct {
	// Close this channel to stop the currently running test
	stopChannel chan interface{}
}

func (p *LoadTestProcessor) Process(msg message.Message) <-chan message.Message {
	ch := make(chan message.Message)

	go func() {
		defer close(ch)

		switch msg.Type {
		case "test.run":
			p.stopChannel = make(chan interface{})

			// filename := msg.Fields["filename"].(string)
			// src := msg.Fields["src"].(string)
			// vus := int(msg.Fields["vus"].(float64))
			data := MessageTestRun{}
			if err := msg.Take(&data); err != nil {
				log.WithError(err).Error("Couldn't decode test.run")
				return
			}

			log.WithFields(log.Fields{
				"filename": data.Filename,
				"vus":      data.VUs,
			}).Debug("Running script")

			var r runner.Runner = nil

			r, err := js.New()
			if err != nil {
				ch <- message.NewToClient("run.error", message.Fields{"error": err})
				break
			}

			err = r.Load(data.Filename, data.Source)
			if err != nil {
				ch <- message.NewToClient("run.error", message.Fields{"error": err})
				break
			}

			for res := range runner.Run(r, data.VUs, p.stopChannel) {
				switch res := res.(type) {
				case runner.LogEntry:
					ch <- message.NewToClient("run.log", message.Fields{
						"text": res.Text,
					})
				case runner.Metric:
					ch <- message.NewToClient("run.metric", message.Fields{
						"start":    res.Start,
						"duration": res.Duration,
					})
				case error:
					ch <- message.NewToClient("run.error", message.Fields{
						"error": res.Error(),
					})
				}
			}
		case "run.stop":
			close(p.stopChannel)
		}
	}()

	return ch
}
<commit_msg>test.stop<commit_after>package loadtest

import (
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/actions/registry"
	"github.com/loadimpact/speedboat/master"
	"github.com/loadimpact/speedboat/message"
	"github.com/loadimpact/speedboat/runner"
	"github.com/loadimpact/speedboat/runner/js"
	"github.com/loadimpact/speedboat/worker"
)

func init() {
	registry.RegisterProcessor(func(*worker.Worker) master.Processor {
		return &LoadTestProcessor{}
	})
}

type LoadTestProcessor struct {
	// Close this channel to stop the currently running test
	stopChannel chan interface{}
}

func (p *LoadTestProcessor) Process(msg message.Message) <-chan message.Message {
	ch := make(chan message.Message)

	go func() {
		defer close(ch)

		switch msg.Type {
		case "test.run":
			p.stopChannel = make(chan interface{})

			// filename := msg.Fields["filename"].(string)
			// src := msg.Fields["src"].(string)
			// vus := int(msg.Fields["vus"].(float64))
			data := MessageTestRun{}
			if err := msg.Take(&data); err != nil {
				log.WithError(err).Error("Couldn't decode test.run")
				return
			}

			log.WithFields(log.Fields{
				"filename": data.Filename,
				"vus":      data.VUs,
			}).Debug("Running script")

			var r runner.Runner = nil

			r, err := js.New()
			if err != nil {
				ch <- message.NewToClient("run.error", message.Fields{"error": err})
				break
			}

			err = r.Load(data.Filename, data.Source)
			if err != nil {
				ch <- message.NewToClient("run.error", message.Fields{"error": err})
				break
			}

			for res := range runner.Run(r, data.VUs, p.stopChannel) {
				switch res := res.(type) {
				case runner.LogEntry:
					ch <- message.NewToClient("run.log", message.Fields{
						"text": res.Text,
					})
				case runner.Metric:
					ch <- message.NewToClient("run.metric", message.Fields{
						"start":    res.Start,
						"duration": res.Duration,
					})
				case error:
					ch <- message.NewToClient("run.error", message.Fields{
						"error": res.Error(),
					})
				}
			}
		case "test.stop":
			close(p.stopChannel)
		}
	}()

	return ch
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**
**!region** [region]`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist/watching")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.FilterQuotes(func(quote *arn.Quote) bool {
			return !quote.IsDraft && quote.IsValid()
		})

		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}

	// Set the specific region role for the user
	if strings.HasPrefix(msg.Content, "!region ") {

		regions := map[string]string{
			"africa":    "465876853236826112",
			"america":   "465876808311635979",
			"asia":      "465876834031108096",
			"australia": "465876893036707840",
			"europe":    "465876773029019659",
		}

		region := msg.Content[len("!region "):]

		// check to make sure the region is in the region map
		if _, ok := regions[region]; ok {
			// Get the channel, this is used to get the guild ID
			c, _ := s.Channel(msg.ChannelID)

			// Check to see if user already has a region role
			user, _ := s.GuildMember(c.GuildID, msg.Author.ID)

			for _, role := range user.Roles {
				match := false
				// we also need to loop through our map because discord doesn't return roles as names
				// but rather IDs.
				for _, id := range regions {

					if role == id {
						// remove the role and set match to true
						s.GuildMemberRoleRemove(c.GuildID, msg.Author.ID, id)
						match = true
						break
					}

				}

				if match {
					break
				}

			}

			// try to set the role
			err := s.GuildMemberRoleAdd(c.GuildID, msg.Author.ID, regions[region])
			if err != nil {
				s.ChannelMessageSend(msg.ChannelID, "The region role could not be set!")
				return
			}

			s.ChannelMessageSend(msg.ChannelID, "The "+region+" role has been set on your user!")

		} else {
			s.ChannelMessageSend(msg.ChannelID, "This is not a region!")
		}

		return
	}
}
<commit_msg>Made requested changes<commit_after>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

var regions = map[string]string{
	"africa":    "465876853236826112",
	"america":   "465876808311635979",
	"asia":      "465876834031108096",
	"australia": "465876893036707840",
	"europe":    "465876773029019659",
}

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**
**!region** [region]`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist/watching")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.FilterQuotes(func(quote *arn.Quote) bool {
			return !quote.IsDraft && quote.IsValid()
		})

		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}

	// Set the specific region role for the user
	if strings.HasPrefix(msg.Content, "!region ") {
		region := strings.ToLower(msg.Content[len("!region "):])

		// check to make sure the region is in the region map
		if _, ok := regions[region]; !ok {
			s.ChannelMessageSend(msg.ChannelID, "This is not a region!")
			return
		}

		// Get the channel, this is used to get the guild ID
		c, _ := s.Channel(msg.ChannelID)

		// Check to see if user already has a region role
		user, _ := s.GuildMember(c.GuildID, msg.Author.ID)

		for _, role := range user.Roles {
			match := false

			// We also need to loop through our map because discord doesn't return roles as names
			// but rather IDs.
			for _, id := range regions {
				if role == id {
					// Remove the role and set match to true.
					s.GuildMemberRoleRemove(c.GuildID, msg.Author.ID, id)
					match = true
					break
				}
			}

			if match {
				break
			}
		}

		// Try to set the role.
		err := s.GuildMemberRoleAdd(c.GuildID, msg.Author.ID, regions[region])

		if err != nil {
			s.ChannelMessageSend(msg.ChannelID, "The region role could not be set!")
			return
		}

		s.ChannelMessageSend(msg.ChannelID, "The "+region+" role has been set on your user!")
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2022 The Kubernetes 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 app

import (
	"net/http"
	"net/netip"
	"testing"
)

func TestGetClientIP(t *testing.T) {
	testCases := []struct {
		Name        string
		Request     http.Request
		ExpectedIP  netip.Addr
		ExpectError bool
	}{
		{
			Name: "NO X-Forwarded-For",
			Request: http.Request{
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("127.0.0.1"),
		},
		{
			Name: "NO X-Forwarded-For, somehow bogus RemoteAddr ??? gotta pump code coverage 🤷",
			Request: http.Request{
				RemoteAddr: "127.0.0.1asd;lfkj8888",
			},
			ExpectError: true,
		},
		{
			Name: "X-Forwarded-For without client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"8.8.8.8,8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "X-Forwarded-For with clean client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"127.0.0.1,8.8.8.8,8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "X-Forwarded-For with garbage client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"asd;lfkjaasdf;lk,,8.8.8.8,8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "Bogus crafted non-cloud X-Forwarded-For with no commas",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"8.8.8.8"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectError: true,
		},
	}
	for i := range testCases {
		tc := testCases[i]
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			ip, err := getClientIP(&tc.Request)
			if err != nil {
				if !tc.ExpectError {
					t.Fatalf("unexpted error: %v", err)
				}
			} else if tc.ExpectError {
				t.Fatal("expected error but err was nil")
			} else if ip != tc.ExpectedIP {
				t.Fatalf("IP does not match expected IP got: %q, expected: %q", ip, tc.ExpectedIP)
			}
		})
	}
}
<commit_msg>update GetClientIP gets to cover real data w/ spaces<commit_after>/*
Copyright 2022 The Kubernetes 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 app

import (
	"net/http"
	"net/netip"
	"testing"
)

func TestGetClientIP(t *testing.T) {
	testCases := []struct {
		Name        string
		Request     http.Request
		ExpectedIP  netip.Addr
		ExpectError bool
	}{
		{
			Name: "NO X-Forwarded-For",
			Request: http.Request{
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("127.0.0.1"),
		},
		{
			Name: "NO X-Forwarded-For, somehow bogus RemoteAddr ??? gotta pump code coverage 🤷",
			Request: http.Request{
				RemoteAddr: "127.0.0.1asd;lfkj8888",
			},
			ExpectError: true,
		},
		{
			Name: "X-Forwarded-For without client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"8.8.8.8,8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "X-Forwarded-For with clean client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"127.0.0.1, 8.8.8.8, 8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "X-Forwarded-For with garbage client-supplied",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"asd;lfkjaasdf;lk,,8.8.8.8,8.8.8.9"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectedIP: netip.MustParseAddr("8.8.8.8"),
		},
		{
			Name: "Bogus crafted non-cloud X-Forwarded-For with no commas",
			Request: http.Request{
				Header: http.Header{
					"X-Forwarded-For": []string{"8.8.8.8"},
				},
				RemoteAddr: "127.0.0.1:8888",
			},
			ExpectError: true,
		},
	}
	for i := range testCases {
		tc := testCases[i]
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			ip, err := getClientIP(&tc.Request)
			if err != nil {
				if !tc.ExpectError {
					t.Fatalf("unexpted error: %v", err)
				}
			} else if tc.ExpectError {
				t.Fatal("expected error but err was nil")
			} else if ip != tc.ExpectedIP {
				t.Fatalf("IP does not match expected IP got: %q, expected: %q", ip, tc.ExpectedIP)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/containers/storage"
	"github.com/containers/storage/pkg/mflag"
)

var testDeleteImage = false

func deleteThing(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.Delete(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

func deleteLayer(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.DeleteLayer(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

type deletedImage struct {
	DeletedLayers []string `json:"deleted-layers,omitifempty"`
	Error         string   `json:"error,omitifempty"`
}

func deleteImage(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]deletedImage)
	for _, what := range args {
		layers, err := m.DeleteImage(what, !testDeleteImage)
		errText := ""
		if err != nil {
			errText = err.Error()
		}
		deleted[what] = deletedImage{
			DeletedLayers: layers,
			Error:         errText,
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, record := range deleted {
			if record.Error != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, record.Error)
			} else {
				for _, layer := range record.DeletedLayers {
					fmt.Fprintf(os.Stderr, "%s: %s\n", what, layer)
				}
			}
		}
	}
	for _, record := range deleted {
		if record.Error != "" {
			return 1
		}
	}
	return 0
}

func deleteContainer(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.DeleteContainer(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

func init() {
	commands = append(commands, command{
		names:       []string{"delete"},
		optionsHelp: "[LayerOrImageOrContainerNameOrID [...]]",
		usage:       "Delete a layer or image or container, with no safety checks",
		minArgs:     1,
		action:      deleteThing,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-layer", "deletelayer"},
		optionsHelp: "[LayerNameOrID [...]]",
		usage:       "Delete a layer, with safety checks",
		minArgs:     1,
		action:      deleteLayer,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-image", "deleteimage"},
		optionsHelp: "[ImageNameOrID [...]]",
		usage:       "Delete an image, with safety checks",
		minArgs:     1,
		action:      deleteImage,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&testDeleteImage, []string{"-test", "t"}, jsonOutput, "Only test removal")
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-container", "deletecontainer"},
		optionsHelp: "[ContainerNameOrID [...]]",
		usage:       "Delete a container, with safety checks",
		minArgs:     1,
		action:      deleteContainer,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
}
<commit_msg>Fix JSON annotation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/containers/storage"
	"github.com/containers/storage/pkg/mflag"
)

var testDeleteImage = false

func deleteThing(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.Delete(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

func deleteLayer(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.DeleteLayer(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

type deletedImage struct {
	DeletedLayers []string `json:"deleted-layers,omitempty"`
	Error         string   `json:"error,omitempty"`
}

func deleteImage(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]deletedImage)
	for _, what := range args {
		layers, err := m.DeleteImage(what, !testDeleteImage)
		errText := ""
		if err != nil {
			errText = err.Error()
		}
		deleted[what] = deletedImage{
			DeletedLayers: layers,
			Error:         errText,
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, record := range deleted {
			if record.Error != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, record.Error)
			} else {
				for _, layer := range record.DeletedLayers {
					fmt.Fprintf(os.Stderr, "%s: %s\n", what, layer)
				}
			}
		}
	}
	for _, record := range deleted {
		if record.Error != "" {
			return 1
		}
	}
	return 0
}

func deleteContainer(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
	if len(args) < 1 {
		return 1
	}
	deleted := make(map[string]string)
	for _, what := range args {
		err := m.DeleteContainer(what)
		if err != nil {
			deleted[what] = err.Error()
		} else {
			deleted[what] = ""
		}
	}
	if jsonOutput {
		json.NewEncoder(os.Stdout).Encode(deleted)
	} else {
		for what, err := range deleted {
			if err != "" {
				fmt.Fprintf(os.Stderr, "%s: %s\n", what, err)
			}
		}
	}
	for _, err := range deleted {
		if err != "" {
			return 1
		}
	}
	return 0
}

func init() {
	commands = append(commands, command{
		names:       []string{"delete"},
		optionsHelp: "[LayerOrImageOrContainerNameOrID [...]]",
		usage:       "Delete a layer or image or container, with no safety checks",
		minArgs:     1,
		action:      deleteThing,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-layer", "deletelayer"},
		optionsHelp: "[LayerNameOrID [...]]",
		usage:       "Delete a layer, with safety checks",
		minArgs:     1,
		action:      deleteLayer,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-image", "deleteimage"},
		optionsHelp: "[ImageNameOrID [...]]",
		usage:       "Delete an image, with safety checks",
		minArgs:     1,
		action:      deleteImage,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&testDeleteImage, []string{"-test", "t"}, jsonOutput, "Only test removal")
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
	commands = append(commands, command{
		names:       []string{"delete-container", "deletecontainer"},
		optionsHelp: "[ContainerNameOrID [...]]",
		usage:       "Delete a container, with safety checks",
		minArgs:     1,
		action:      deleteContainer,
		addFlags: func(flags *mflag.FlagSet, cmd *command) {
			flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
		},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/gosuri/uilive"
	"github.com/spf13/cobra"

	flux "github.com/weaveworks/fluxy"
)

const largestHeartbeatDelta = 5 * time.Second

type serviceCheckReleaseOpts struct {
	*serviceOpts
	releaseID string
	follow    bool
}

func newServiceCheckRelease(parent *serviceOpts) *serviceCheckReleaseOpts {
	return &serviceCheckReleaseOpts{serviceOpts: parent}
}

func (opts *serviceCheckReleaseOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "check-release",
		Short: "Check the status of a release.",
		Example: makeExample(
			"fluxctl check-release --release-id=12345678-1234-5678-1234-567812345678",
		),
		RunE: opts.RunE,
	}
	cmd.Flags().StringVarP(&opts.releaseID, "release-id", "r", "", "release ID to check")
	cmd.Flags().BoolVarP(&opts.follow, "follow", "f", false, "continuously check the release, blocking until it is complete")
	return cmd
}

func (opts *serviceCheckReleaseOpts) RunE(_ *cobra.Command, args []string) error {
	if len(args) != 0 {
		return errorWantedNoArgs
	}

	if opts.releaseID == "" {
		return fmt.Errorf("-r, --release-id is required")
	}

	if !opts.follow {
		job, err := opts.Fluxd.GetRelease(noInstanceID, flux.ReleaseID(opts.releaseID))
		if err != nil {
			return err
		}
		buf, err := json.MarshalIndent(job, "", "    ")
		if err != nil {
			return err
		}
		_, err = os.Stdout.Write(buf)
		return err
	}

	var (
		w   = uilive.New()
		job flux.ReleaseJob
		err error
	)
	w.Start()
	for range time.Tick(250 * time.Millisecond) {
		job, err = opts.Fluxd.GetRelease(noInstanceID, flux.ReleaseID(opts.releaseID))
		if err != nil {
			fmt.Fprintf(w, "Status: error querying release.\n") // error will get printed below
			break
		}
		status := "Waiting for job to be claimed..."
		if job.Status != "" {
			status = job.Status
		}
		if delta := time.Since(job.Heartbeat); !job.Heartbeat.IsZero() && delta > largestHeartbeatDelta {
			status = status + fmt.Sprintf(" (warning: no heartbeat in %s, worker may have crashed)", delta)
		}
		fmt.Fprintf(w, "Status: %s\n", status)
		if job.IsFinished() {
			break
		}
	}
	w.Stop()

	if err != nil {
		return err
	}

	fmt.Fprintf(os.Stdout, "\n")
	if !job.Success {
		fmt.Fprintf(os.Stdout, "Here's as far as we got:\n")
	} else if job.Spec.Kind == flux.ReleaseKindPlan {
		fmt.Fprintf(os.Stdout, "Here's the plan:\n")
	} else {
		fmt.Fprintf(os.Stdout, "Here's what happened:\n")
	}
	for i, msg := range job.Log {
		fmt.Fprintf(os.Stdout, " %d) %s\n", i+1, msg)
	}

	if job.Spec.Kind == flux.ReleaseKindExecute {
		fmt.Fprintf(os.Stdout, "Took %s\n", time.Since(job.Submitted))
	}
	return nil
}
<commit_msg>Review feedback<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/gosuri/uilive"
	"github.com/spf13/cobra"
	"golang.org/x/crypto/ssh/terminal"

	flux "github.com/weaveworks/fluxy"
)

const largestHeartbeatDelta = 5 * time.Second

type serviceCheckReleaseOpts struct {
	*serviceOpts
	releaseID string
	follow    bool
	noTty     bool
}

func newServiceCheckRelease(parent *serviceOpts) *serviceCheckReleaseOpts {
	return &serviceCheckReleaseOpts{serviceOpts: parent}
}

func (opts *serviceCheckReleaseOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "check-release",
		Short: "Check the status of a release.",
		Example: makeExample(
			"fluxctl check-release --release-id=12345678-1234-5678-1234-567812345678",
		),
		RunE: opts.RunE,
	}
	cmd.Flags().StringVarP(&opts.releaseID, "release-id", "r", "", "release ID to check")
	cmd.Flags().BoolVarP(&opts.follow, "follow", "f", false, "continuously check the release, blocking until it is complete")
	cmd.Flags().BoolVar(&opts.noTty, "no-tty", false, "if --follow=true, forces non-TTY status output")
	return cmd
}

func (opts *serviceCheckReleaseOpts) RunE(_ *cobra.Command, args []string) error {
	if len(args) != 0 {
		return errorWantedNoArgs
	}

	if opts.releaseID == "" {
		return fmt.Errorf("-r, --release-id is required")
	}

	if !opts.follow {
		job, err := opts.Fluxd.GetRelease(noInstanceID, flux.ReleaseID(opts.releaseID))
		if err != nil {
			return err
		}
		buf, err := json.MarshalIndent(job, "", "    ")
		if err != nil {
			return err
		}
		_, err = os.Stdout.Write(buf)
		return err
	}

	var (
		w    io.Writer = os.Stdout
		stop           = func() {}
	)
	if !opts.noTty && terminal.IsTerminal(int(os.Stdout.Fd())) {
		liveWriter := uilive.New()
		liveWriter.Start()
		w, stop = liveWriter, liveWriter.Stop
	}
	var (
		prev string
		job  flux.ReleaseJob
		err  error
	)
	for range time.Tick(time.Second) {
		job, err = opts.Fluxd.GetRelease(noInstanceID, flux.ReleaseID(opts.releaseID))
		if err != nil {
			fmt.Fprintf(w, "Status: error querying release.\n") // error will get printed below
			break
		}
		status := "Waiting for job to be claimed..."
		if job.Status != "" {
			status = job.Status
		}
		if delta := time.Since(job.Heartbeat); !job.Heartbeat.IsZero() && delta > largestHeartbeatDelta {
			status = status + fmt.Sprintf(" (warning: no heartbeat in %s, worker may have crashed)", delta)
		}
		if status != prev {
			fmt.Fprintf(w, "Status: %s\n", status)
		}
		prev = status
		if job.IsFinished() {
			break
		}
	}
	stop()

	if err != nil {
		return err
	}

	fmt.Fprintf(os.Stdout, "\n")
	if !job.Success {
		fmt.Fprintf(os.Stdout, "Here's as far as we got:\n")
	} else if job.Spec.Kind == flux.ReleaseKindPlan {
		fmt.Fprintf(os.Stdout, "Here's the plan:\n")
	} else {
		fmt.Fprintf(os.Stdout, "Here's what happened:\n")
	}
	for i, msg := range job.Log {
		fmt.Fprintf(os.Stdout, " %d) %s\n", i+1, msg)
	}

	if job.Spec.Kind == flux.ReleaseKindExecute {
		fmt.Fprintf(os.Stdout, "Took %s\n", time.Since(job.Submitted))
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"errors"
	"fmt"

	"github.com/juju/cmd"

	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

var usageAddSSHKeySummary = `
Adds a public SSH key to a model.`[1:]

var usageAddSSHKeyDetails = `
Juju maintains a per-model cache of public SSH keys which it copies to
each unit (including units already deployed). By default this includes the
key of the user who created the model (assuming it is stored in the
default location ~/.ssh/). Additional keys may be added with this command,
quoting the entire public key as an argument.

Examples:
    juju add-ssh-key "ssh-rsa qYfS5LieM79HIOr535ret6xy
    AAAAB3NzaC1yc2EAAAADAQA6fgBAAABAQCygc6Rc9XgHdhQqTJ
    Wsoj+I3xGrOtk21xYtKijnhkGqItAHmrE5+VH6PY1rVIUXhpTg
    pSkJsHLmhE29OhIpt6yr8vQSOChqYfS5LieM79HIOJEgJEzIqC
    52rCYXLvr/BVkd6yr4IoM1vpb/n6u9o8v1a0VUGfc/J6tQAcPR
    ExzjZUVsfjj8HdLtcFq4JLYC41miiJtHw4b3qYu7qm3vh4eCiK
    1LqLncXnBCJfjj0pADXaL5OQ9dmD3aCbi8KFyOEs3UumPosgmh
    VCAfjjHObWHwNQ/ZU2KrX1/lv/+lBChx2tJliqQpyYMiA3nrtS
    jfqQgZfjVF5vz8LESQbGc6+vLcXZ9KQpuYDt joe@ubuntu"

For ease of use it is possible to use shell substitution to pass the key
to the command:

    juju add-ssh-key $(cat ~/mykey.pub)

See also:
    list-ssh-key
    remove-ssh-key
    import-ssh-key`[1:]

// NewAddKeysCommand is used to add a new ssh key to a model.
func NewAddKeysCommand() cmd.Command {
	return modelcmd.Wrap(&addKeysCommand{})
}

// addKeysCommand is used to add a new authorized ssh key for a user.
type addKeysCommand struct {
	SSHKeysBase
	user    string
	sshKeys []string
}

// Info implements Command.Info.
func (c *addKeysCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "add-ssh-key",
		Args:    "<ssh key> ...",
		Purpose: usageAddSSHKeySummary,
		Doc:     usageAddSSHKeyDetails,
		Aliases: []string{"add-ssh-keys"},
	}
}

// Init implements Command.Init.
func (c *addKeysCommand) Init(args []string) error {
	switch len(args) {
	case 0:
		return errors.New("no ssh key specified")
	default:
		c.sshKeys = args
	}
	return nil
}

// Run implements Command.Run.
func (c *addKeysCommand) Run(context *cmd.Context) error {
	client, err := c.NewKeyManagerClient()
	if err != nil {
		return err
	}
	defer client.Close()
	// TODO(alexisb) - currently keys are global which is not ideal.
	// keymanager needs to be updated to allow keys per user
	c.user = "admin"
	results, err := client.AddKeys(c.user, c.sshKeys...)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	for i, result := range results {
		if result.Error != nil {
			fmt.Fprintf(context.Stderr, "cannot add key %q: %v\n", c.sshKeys[i], result.Error)
		}
	}
	return nil
}
<commit_msg>Drive by helptext correction<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"errors"
	"fmt"

	"github.com/juju/cmd"

	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

var usageAddSSHKeySummary = `
Adds a public SSH key to a model.`[1:]

var usageAddSSHKeyDetails = `
Juju maintains a per-model cache of public SSH keys which it copies to
each unit (including units already deployed). By default this includes the
key of the user who created the model (assuming it is stored in the
default location ~/.ssh/). Additional keys may be added with this command,
quoting the entire public key as an argument.

Examples:
    juju add-ssh-key "ssh-rsa qYfS5LieM79HIOr535ret6xy
    AAAAB3NzaC1yc2EAAAADAQA6fgBAAABAQCygc6Rc9XgHdhQqTJ
    Wsoj+I3xGrOtk21xYtKijnhkGqItAHmrE5+VH6PY1rVIUXhpTg
    pSkJsHLmhE29OhIpt6yr8vQSOChqYfS5LieM79HIOJEgJEzIqC
    52rCYXLvr/BVkd6yr4IoM1vpb/n6u9o8v1a0VUGfc/J6tQAcPR
    ExzjZUVsfjj8HdLtcFq4JLYC41miiJtHw4b3qYu7qm3vh4eCiK
    1LqLncXnBCJfjj0pADXaL5OQ9dmD3aCbi8KFyOEs3UumPosgmh
    VCAfjjHObWHwNQ/ZU2KrX1/lv/+lBChx2tJliqQpyYMiA3nrtS
    jfqQgZfjVF5vz8LESQbGc6+vLcXZ9KQpuYDt joe@ubuntu"

For ease of use it is possible to use shell substitution to pass the key
to the command:

juju add-ssh-key "$(cat ~/mykey.pub)"

See also:
    list-ssh-key
    remove-ssh-key
    import-ssh-key`[1:]

// NewAddKeysCommand is used to add a new ssh key to a model.
func NewAddKeysCommand() cmd.Command {
	return modelcmd.Wrap(&addKeysCommand{})
}

// addKeysCommand is used to add a new authorized ssh key for a user.
type addKeysCommand struct {
	SSHKeysBase
	user    string
	sshKeys []string
}

// Info implements Command.Info.
func (c *addKeysCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "add-ssh-key",
		Args:    "<ssh key> ...",
		Purpose: usageAddSSHKeySummary,
		Doc:     usageAddSSHKeyDetails,
		Aliases: []string{"add-ssh-keys"},
	}
}

// Init implements Command.Init.
func (c *addKeysCommand) Init(args []string) error {
	switch len(args) {
	case 0:
		return errors.New("no ssh key specified")
	default:
		c.sshKeys = args
	}
	return nil
}

// Run implements Command.Run.
func (c *addKeysCommand) Run(context *cmd.Context) error {
	client, err := c.NewKeyManagerClient()
	if err != nil {
		return err
	}
	defer client.Close()
	// TODO(alexisb) - currently keys are global which is not ideal.
	// keymanager needs to be updated to allow keys per user
	c.user = "admin"
	results, err := client.AddKeys(c.user, c.sshKeys...)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	for i, result := range results {
		if result.Error != nil {
			fmt.Fprintf(context.Stderr, "cannot add key %q: %v\n", c.sshKeys[i], result.Error)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC. 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 cmd

import (
	"context"
	"fmt"
	"log"

	"github.com/apigee/registry/cmd/registry/core"
	"github.com/apigee/registry/connection"
	"github.com/apigee/registry/rpc"
	"github.com/apigee/registry/server/names"
	"github.com/spf13/cobra"
	"google.golang.org/protobuf/proto"
)

func init() {
	computeCmd.AddCommand(computeLintCmd)
	computeLintCmd.Flags().String("linter", "", "name of linter to use (aip, spectral, gnostic)")
}

var computeLintCmd = &cobra.Command{
	Use:   "lint",
	Short: "Compute lint results for API specs",
	Args:  cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		linter, err := cmd.LocalFlags().GetString("linter")
		if err != nil { // ignore errors
			linter = ""
		}
		ctx := context.TODO()
		client, err := connection.NewClient(ctx)
		if err != nil {
			log.Fatalf("%s", err.Error())
		}
		// Initialize task queue.
		taskQueue := make(chan core.Task, 1024)
		workerCount := 16
		for i := 0; i < workerCount; i++ {
			core.WaitGroup().Add(1)
			go core.Worker(ctx, taskQueue)
		}
		// Generate tasks.
		name := args[0]
		if m := names.SpecRegexp().FindStringSubmatch(name); m != nil {
			// Iterate through a collection of specs and evaluate each.
			err = core.ListSpecs(ctx, client, m, computeFilter, func(spec *rpc.ApiSpec) {
				taskQueue <- &computeLintTask{
					ctx:      ctx,
					client:   client,
					specName: spec.Name,
					linter:   linter,
				}
			})
			if err != nil {
				log.Fatalf("%s", err.Error())
			}
			close(taskQueue)
			core.WaitGroup().Wait()
		}
	},
}

type computeLintTask struct {
	ctx      context.Context
	client   connection.Client
	specName string
	linter   string
}

func (task *computeLintTask) String() string {
	return fmt.Sprintf("compute %s/lint-%s", task.specName, task.linter)
}

func lintRelation(linter string) string {
	return "lint-" + linter
}

func (task *computeLintTask) Run() error {
	request := &rpc.GetApiSpecRequest{
		Name: task.specName,
	}
	spec, err := task.client.GetApiSpec(task.ctx, request)
	if err != nil {
		return err
	}
	data, err := core.GetBytesForSpec(task.ctx, task.client, spec)
	if err != nil {
		return nil
	}
	var relation string
	var lint *rpc.Lint
	if core.IsOpenAPIv2(spec.GetMimeType()) || core.IsOpenAPIv3(spec.GetMimeType()) {
		// the default openapi linter is gnostic
		if task.linter == "" {
			task.linter = "gnostic"
		}
		relation = lintRelation(task.linter)
		log.Printf("computing %s/artifacts/%s", spec.Name, relation)
		lint, err = core.NewLintFromOpenAPI(spec.Name, data, task.linter)
		if err != nil {
			return fmt.Errorf("error processing OpenAPI: %s (%s)", spec.Name, err.Error())
		}
	} else if core.IsDiscovery(spec.GetMimeType()) {
		return fmt.Errorf("unsupported Discovery document: %s", spec.Name)
	} else if core.IsProto(spec.GetMimeType()) && core.IsZipArchive(spec.GetMimeType()) {
		// the default proto linter is the aip linter
		if task.linter == "" {
			task.linter = "aip"
		}
		relation = lintRelation(task.linter)
		log.Printf("computing %s/artifacts/%s", spec.Name, relation)
		lint, err = core.NewLintFromZippedProtos(spec.Name, data)
		if err != nil {
			return fmt.Errorf("error processing protos: %s (%s)", spec.Name, err.Error())
		}
	} else {
		return fmt.Errorf("we don't know how to lint %s", spec.Name)
	}
	subject := spec.GetName()
	messageData, err := proto.Marshal(lint)
	artifact := &rpc.Artifact{
		Name:     subject + "/artifacts/" + relation,
		MimeType: core.MimeTypeForMessageType("google.cloud.apigee.registry.applications.v1alpha1.Lint"),
		Contents: messageData,
	}
	err = core.SetArtifact(task.ctx, task.client, artifact)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Bug Fix: return error from GetBytesSpecs in compute-lint.go (#195)<commit_after>// Copyright 2020 Google LLC. 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 cmd

import (
	"context"
	"fmt"
	"log"

	"github.com/apigee/registry/cmd/registry/core"
	"github.com/apigee/registry/connection"
	"github.com/apigee/registry/rpc"
	"github.com/apigee/registry/server/names"
	"github.com/spf13/cobra"
	"google.golang.org/protobuf/proto"
)

func init() {
	computeCmd.AddCommand(computeLintCmd)
	computeLintCmd.Flags().String("linter", "", "name of linter to use (aip, spectral, gnostic)")
}

var computeLintCmd = &cobra.Command{
	Use:   "lint",
	Short: "Compute lint results for API specs",
	Args:  cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		linter, err := cmd.LocalFlags().GetString("linter")
		if err != nil { // ignore errors
			linter = ""
		}
		ctx := context.TODO()
		client, err := connection.NewClient(ctx)
		if err != nil {
			log.Fatalf("%s", err.Error())
		}
		// Initialize task queue.
		taskQueue := make(chan core.Task, 1024)
		workerCount := 16
		for i := 0; i < workerCount; i++ {
			core.WaitGroup().Add(1)
			go core.Worker(ctx, taskQueue)
		}
		// Generate tasks.
		name := args[0]
		if m := names.SpecRegexp().FindStringSubmatch(name); m != nil {
			// Iterate through a collection of specs and evaluate each.
			err = core.ListSpecs(ctx, client, m, computeFilter, func(spec *rpc.ApiSpec) {
				taskQueue <- &computeLintTask{
					ctx:      ctx,
					client:   client,
					specName: spec.Name,
					linter:   linter,
				}
			})
			if err != nil {
				log.Fatalf("%s", err.Error())
			}
			close(taskQueue)
			core.WaitGroup().Wait()
		}
	},
}

type computeLintTask struct {
	ctx      context.Context
	client   connection.Client
	specName string
	linter   string
}

func (task *computeLintTask) String() string {
	return fmt.Sprintf("compute %s/lint-%s", task.specName, task.linter)
}

func lintRelation(linter string) string {
	return "lint-" + linter
}

func (task *computeLintTask) Run() error {
	request := &rpc.GetApiSpecRequest{
		Name: task.specName,
	}
	spec, err := task.client.GetApiSpec(task.ctx, request)
	if err != nil {
		return err
	}
	data, err := core.GetBytesForSpec(task.ctx, task.client, spec)
	if err != nil {
		return err
	}
	var relation string
	var lint *rpc.Lint
	if core.IsOpenAPIv2(spec.GetMimeType()) || core.IsOpenAPIv3(spec.GetMimeType()) {
		// the default openapi linter is gnostic
		if task.linter == "" {
			task.linter = "gnostic"
		}
		relation = lintRelation(task.linter)
		log.Printf("computing %s/artifacts/%s", spec.Name, relation)
		lint, err = core.NewLintFromOpenAPI(spec.Name, data, task.linter)
		if err != nil {
			return fmt.Errorf("error processing OpenAPI: %s (%s)", spec.Name, err.Error())
		}
	} else if core.IsDiscovery(spec.GetMimeType()) {
		return fmt.Errorf("unsupported Discovery document: %s", spec.Name)
	} else if core.IsProto(spec.GetMimeType()) && core.IsZipArchive(spec.GetMimeType()) {
		// the default proto linter is the aip linter
		if task.linter == "" {
			task.linter = "aip"
		}
		relation = lintRelation(task.linter)
		log.Printf("computing %s/artifacts/%s", spec.Name, relation)
		lint, err = core.NewLintFromZippedProtos(spec.Name, data)
		if err != nil {
			return fmt.Errorf("error processing protos: %s (%s)", spec.Name, err.Error())
		}
	} else {
		return fmt.Errorf("we don't know how to lint %s", spec.Name)
	}
	subject := spec.GetName()
	messageData, err := proto.Marshal(lint)
	artifact := &rpc.Artifact{
		Name:     subject + "/artifacts/" + relation,
		MimeType: core.MimeTypeForMessageType("google.cloud.apigee.registry.applications.v1alpha1.Lint"),
		Contents: messageData,
	}
	err = core.SetArtifact(task.ctx, task.client, artifact)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main_test

import (
	"encoding/json"
	"fmt"
	"os"
	"path"
	"runtime"
	"testing"
	"time"

	"code.cloudfoundry.org/consuladapter/consulrunner"
	"code.cloudfoundry.org/diego-ssh/cmd/sshd/testrunner"
	"code.cloudfoundry.org/diego-ssh/keys"
	"code.cloudfoundry.org/inigo/helpers/portauthority"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

var (
	sshProxyPath string
	sshdPath     string
	sshdProcess  ifrit.Process

	sshdPort             uint16
	sshdTLSPort          uint16
	sshdContainerPort    uint16
	sshdContainerTLSPort uint16
	sshProxyPort         uint16
	healthCheckProxyPort uint16

	sshdAddress string

	hostKeyPem          string
	privateKeyPem       string
	publicAuthorizedKey string
	consulRunner        *consulrunner.ClusterRunner

	fixturesPath string

	portAllocator portauthority.PortAllocator
)

func TestSSHProxy(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "SSH Proxy Suite")
}

var _ = SynchronizedBeforeSuite(func() []byte {
	sshProxy, err := gexec.Build("code.cloudfoundry.org/diego-ssh/cmd/ssh-proxy", "-race")
	Expect(err).NotTo(HaveOccurred())

	sshd, err := gexec.Build("code.cloudfoundry.org/diego-ssh/cmd/sshd", "-race")
	Expect(err).NotTo(HaveOccurred())

	hostKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024)
	Expect(err).NotTo(HaveOccurred())

	privateKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024)
	Expect(err).NotTo(HaveOccurred())

	payload, err := json.Marshal(map[string]string{
		"ssh-proxy":      sshProxy,
		"sshd":           sshd,
		"host-key":       hostKey.PEMEncodedPrivateKey(),
		"private-key":    privateKey.PEMEncodedPrivateKey(),
		"authorized-key": privateKey.AuthorizedKey(),
	})

	Expect(err).NotTo(HaveOccurred())

	return payload
}, func(payload []byte) {
	context := map[string]string{}

	err := json.Unmarshal(payload, &context)
	Expect(err).NotTo(HaveOccurred())

	fixturesPath = path.Join(os.Getenv("GOPATH"), "src/code.cloudfoundry.org/diego-ssh/cmd/ssh-proxy/fixtures")

	hostKeyPem = context["host-key"]
	privateKeyPem = context["private-key"]
	publicAuthorizedKey = context["authorized-key"]

	node := GinkgoParallelNode()
	startPort := 1050 * node
	portRange := 1000
	endPort := startPort + portRange

	portAllocator, err = portauthority.New(startPort, endPort)
	Expect(err).NotTo(HaveOccurred())

	sshdPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	sshdContainerPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshdPath = context["sshd"]

	sshdTLSPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	sshdContainerTLSPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshdPath = context["sshd"]

	sshProxyPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshProxyPath = context["ssh-proxy"]

	healthCheckProxyPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	consulPort, err := portAllocator.ClaimPorts(consulrunner.PortOffsetLength)
	Expect(err).NotTo(HaveOccurred())
	consulRunner = consulrunner.NewClusterRunner(
		consulrunner.ClusterRunnerConfig{
			StartingPort: int(consulPort),
			NumNodes:     1,
			Scheme:       "http",
		},
	)

	consulRunner.Start()
	consulRunner.WaitUntilReady()
})

var _ = BeforeEach(func() {

	if runtime.GOOS == "windows" {
		Skip("SSH not supported on Windows, and SSH proxy never runs on Windows anyway")
	}

	err := consulRunner.Reset()
	Expect(err).NotTo(HaveOccurred())

	sshdAddress = fmt.Sprintf("127.0.0.1:%d", sshdPort)
	sshdArgs := testrunner.Args{
		Address:       sshdAddress,
		HostKey:       hostKeyPem,
		AuthorizedKey: publicAuthorizedKey,
	}

	runner := testrunner.New(sshdPath, sshdArgs)
	sshdProcess = ifrit.Invoke(runner)
})

var _ = AfterEach(func() {
	ginkgomon.Kill(sshdProcess, 5*time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	consulRunner.Stop()
}, func() {
	gexec.CleanupBuildArtifacts()
})
<commit_msg>Convert pathing for go mod'ed tests<commit_after>package main_test

import (
	"encoding/json"
	"fmt"
	"os"
	"path"
	"runtime"
	"testing"
	"time"

	"code.cloudfoundry.org/consuladapter/consulrunner"
	"code.cloudfoundry.org/diego-ssh/cmd/sshd/testrunner"
	"code.cloudfoundry.org/diego-ssh/keys"
	"code.cloudfoundry.org/inigo/helpers/portauthority"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

var (
	sshProxyPath string
	sshdPath     string
	sshdProcess  ifrit.Process

	sshdPort             uint16
	sshdTLSPort          uint16
	sshdContainerPort    uint16
	sshdContainerTLSPort uint16
	sshProxyPort         uint16
	healthCheckProxyPort uint16

	sshdAddress string

	hostKeyPem          string
	privateKeyPem       string
	publicAuthorizedKey string
	consulRunner        *consulrunner.ClusterRunner

	fixturesPath string

	portAllocator portauthority.PortAllocator
)

func TestSSHProxy(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "SSH Proxy Suite")
}

var _ = SynchronizedBeforeSuite(func() []byte {
	sshProxy, err := gexec.Build("code.cloudfoundry.org/diego-ssh/cmd/ssh-proxy", "-race")
	Expect(err).NotTo(HaveOccurred())

	sshd, err := gexec.Build("code.cloudfoundry.org/diego-ssh/cmd/sshd", "-race")
	Expect(err).NotTo(HaveOccurred())

	hostKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024)
	Expect(err).NotTo(HaveOccurred())

	privateKey, err := keys.RSAKeyPairFactory.NewKeyPair(1024)
	Expect(err).NotTo(HaveOccurred())

	payload, err := json.Marshal(map[string]string{
		"ssh-proxy":      sshProxy,
		"sshd":           sshd,
		"host-key":       hostKey.PEMEncodedPrivateKey(),
		"private-key":    privateKey.PEMEncodedPrivateKey(),
		"authorized-key": privateKey.AuthorizedKey(),
	})

	Expect(err).NotTo(HaveOccurred())

	return payload
}, func(payload []byte) {
	context := map[string]string{}

	err := json.Unmarshal(payload, &context)
	Expect(err).NotTo(HaveOccurred())

	fixturesPath = path.Join(os.Getenv("DIEGO_RELEASE_DIR"), "src/code.cloudfoundry.org/diego-ssh/cmd/ssh-proxy/fixtures")

	hostKeyPem = context["host-key"]
	privateKeyPem = context["private-key"]
	publicAuthorizedKey = context["authorized-key"]

	node := GinkgoParallelNode()
	startPort := 1050 * node
	portRange := 1000
	endPort := startPort + portRange

	portAllocator, err = portauthority.New(startPort, endPort)
	Expect(err).NotTo(HaveOccurred())

	sshdPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	sshdContainerPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshdPath = context["sshd"]

	sshdTLSPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	sshdContainerTLSPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshdPath = context["sshd"]

	sshProxyPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())
	sshProxyPath = context["ssh-proxy"]

	healthCheckProxyPort, err = portAllocator.ClaimPorts(1)
	Expect(err).NotTo(HaveOccurred())

	consulPort, err := portAllocator.ClaimPorts(consulrunner.PortOffsetLength)
	Expect(err).NotTo(HaveOccurred())
	consulRunner = consulrunner.NewClusterRunner(
		consulrunner.ClusterRunnerConfig{
			StartingPort: int(consulPort),
			NumNodes:     1,
			Scheme:       "http",
		},
	)

	consulRunner.Start()
	consulRunner.WaitUntilReady()
})

var _ = BeforeEach(func() {

	if runtime.GOOS == "windows" {
		Skip("SSH not supported on Windows, and SSH proxy never runs on Windows anyway")
	}

	err := consulRunner.Reset()
	Expect(err).NotTo(HaveOccurred())

	sshdAddress = fmt.Sprintf("127.0.0.1:%d", sshdPort)
	sshdArgs := testrunner.Args{
		Address:       sshdAddress,
		HostKey:       hostKeyPem,
		AuthorizedKey: publicAuthorizedKey,
	}

	runner := testrunner.New(sshdPath, sshdArgs)
	sshdProcess = ifrit.Invoke(runner)
})

var _ = AfterEach(func() {
	ginkgomon.Kill(sshdProcess, 5*time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	consulRunner.Stop()
}, func() {
	gexec.CleanupBuildArtifacts()
})
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package parallel

import (
	"fmt"
)

func ExampleFanOutIn() {
	data := []int{1, 20}
	err := FanOutIn(func(ch chan<- func() error) {
		for _, d := range data {
			d := d
			ch <- func() error {
				if d > 10 {
					return fmt.Errorf("%d is over 10", d)
				}
				return nil
			}
		}
	})

	fmt.Printf("got: %q", err)
	// Output: got: "20 is over 10"
}
<commit_msg>Add race test for parallel<commit_after>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package parallel

import (
	"fmt"
	"sync/atomic"
	"testing"
)

func ExampleFanOutIn() {
	data := []int{1, 20}
	err := FanOutIn(func(ch chan<- func() error) {
		for _, d := range data {
			d := d
			ch <- func() error {
				if d > 10 {
					return fmt.Errorf("%d is over 10", d)
				}
				return nil
			}
		}
	})

	fmt.Printf("got: %q", err)
	// Output: got: "20 is over 10"
}

func TestRaciness(t *testing.T) {
	t.Parallel()

	val := int32(0)

	for i := 0; i < 100; i++ {
		FanOutIn(func(ch chan<- func() error) {
			ch <- func() error { atomic.AddInt32(&val, 1); return nil }
		})
	}

	if val != 100 {
		t.Error("val != 100, was", val)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Tigera, Inc. 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 commands

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/glog"
	"github.com/tigera/libcalico-go/calicoctl/resourcemgr"
	"github.com/tigera/libcalico-go/lib/api"
	"github.com/tigera/libcalico-go/lib/api/unversioned"
	"github.com/tigera/libcalico-go/lib/client"
	"github.com/tigera/libcalico-go/lib/net"
	"github.com/tigera/libcalico-go/lib/scope"
)

// Convert loaded resources to a slice of resources for easier processing.
// The loaded resources may be a slice containing resources and resource lists, or
// may be a single resource or a single resource list.  This function handles the
// different possible options to convert to a single slice of resources.
func convertToSliceOfResources(loaded interface{}) []unversioned.Resource {
	r := []unversioned.Resource{}
	glog.V(2).Infof("Converting resource to slice: %v\n", loaded)

	switch reflect.TypeOf(loaded).Kind() {
	case reflect.Slice:
		// Recursively call this to add each resource in the supplied slice to
		// return slice.
		s := reflect.ValueOf(loaded)
		for i := 0; i < s.Len(); i++ {
			r = append(r, convertToSliceOfResources(s.Index(i).Interface())...)
		}
	case reflect.Struct:
		// This is a resource or resource list.  If a resource, add to our return
		// slice.  If a resource list, add each item to our return slice.
		lr := loaded.(unversioned.Resource)
		if strings.HasSuffix(lr.GetTypeMetadata().Kind, "List") {
			items := reflect.ValueOf(loaded).Elem().FieldByName("Items")
			for i := 0; i < items.Len(); i++ {
				r = append(r, items.Index(i).Interface().(unversioned.Resource))
			}
		} else {
			r = append(r, lr)
		}
	case reflect.Ptr:
		// This is a resource or resource list.  If a resource, add to our return
		// slice.  If a resource list, add each item to our return slice.
		lr := reflect.ValueOf(loaded).Elem().Interface().(unversioned.Resource)
		if strings.HasSuffix(lr.GetTypeMetadata().Kind, "List") {
			items := reflect.ValueOf(loaded).Elem().FieldByName("Items")
			for i := 0; i < items.Len(); i++ {
				r = append(r, items.Index(i).Interface().(unversioned.Resource))
			}
		} else {
			r = append(r, lr)
		}
	default:
		panic(errors.New(fmt.Sprintf("unhandled type %v converting to resource slice",
			reflect.TypeOf(loaded).Kind())))
	}

	glog.V(2).Infof("Returning slice: %v\n", r)
	return r
}

// Return a resource instance from the command line arguments.
func getResourceFromArguments(args map[string]interface{}) (unversioned.Resource, error) {
	kind := args["<KIND>"].(string)
	stringOrBlank := func(argName string) string {
		if args[argName] != nil {
			return args[argName].(string)
		}
		return ""
	}
	name := stringOrBlank("<NAME>")
	hostname := stringOrBlank("--hostname")
	resScope := stringOrBlank("--scope")
	switch kind {
	case "hostEndpoint":
		h := api.NewHostEndpoint()
		h.Metadata.Name = name
		h.Metadata.Hostname = hostname
		return *h, nil
	case "workloadEndpoint":
		h := api.NewWorkloadEndpoint() //TODO Need to add orchestrator ID and workload ID
		h.Metadata.Name = name
		h.Metadata.Hostname = hostname
		return *h, nil
	case "profile":
		p := api.NewProfile()
		p.Metadata.Name = name
		return *p, nil
	case "policy":
		p := api.NewPolicy()
		p.Metadata.Name = name
		return *p, nil
	case "pool":
		p := api.NewPool()
		if name != "" {
			_, cidr, err := net.ParseCIDR(name)
			if err != nil {
				return nil, err
			}
			p.Metadata.CIDR = *cidr
		}
		return *p, nil
	case "bgpPeer":
		p := api.NewBGPPeer()
		if name != "" {
			err := p.Metadata.PeerIP.UnmarshalText([]byte(name))
			if err != nil {
				return nil, err
			}
		}
		p.Metadata.Hostname = hostname
		switch resScope {
		case "node":
			p.Metadata.Scope = scope.Node
		case "global":
			p.Metadata.Scope = scope.Global
		case "":
			p.Metadata.Scope = scope.Undefined
		default:
			return nil, fmt.Errorf("Unrecognized scope '%s', must be one of: global, node", resScope)
		}
		return *p, nil

	default:
		return nil, fmt.Errorf("Resource type '%s' is not unsupported", kind)
	}
}

// Interface to execute a command for a specific resource type.
type commandInterface interface {
	execute(client *client.Client, resource unversioned.Resource) (unversioned.Resource, error)
}

// Results from executing a CLI command
type commandResults struct {
	// Whether the input file was invalid.
	fileInvalid bool

	// The number of resources that are being configured.
	numResources int

	// The number of resources that were actually configured.  This will
	// never be 0 without an associated error.
	numHandled int

	// The associated error.
	err error

	// The single type of resource that is being configured, or blank
	// if multiple resource types are being configured in a single shot.
	singleKind string

	// The results returned from each invocation
	resources []unversioned.Resource
}

// Common function for configuration commands apply, create, replace, get and delete.  All
// these commands:
// 	-  Load resources from file (or if not specified determine the resource from
// 	   the command line options).
// 	-  Convert the loaded resources into a list of resources (easier to handle)
// 	-  Process each resource individually, collate results and exit on the first error.
func executeConfigCommand(args map[string]interface{}, cmd commandInterface) commandResults {
	var r interface{}
	var err error
	var resources []unversioned.Resource

	glog.V(2).Info("Executing config command")

	if filename := args["--filename"]; filename != nil {
		// Filename is specified, load the resource from file and convert to a slice
		// of resources for easier handling.
		if r, err = resourcemgr.CreateResourcesFromFile(filename.(string)); err != nil {
			return commandResults{err: err, fileInvalid: true}
		}

		resources = convertToSliceOfResources(r)
	} else if r, err := getResourceFromArguments(args); err != nil {
		// Filename is not specific so extract the resource from the arguments.  This
		// is only useful for delete and get functions - but we don't need to check that
		// here since the command syntax requires a filename for the other resource
		// management commands.
		return commandResults{err: err}
	} else {
		// We extracted a single resource type with identifiers from the CLI, convert to
		// a list for simpler handling.
		resources = []unversioned.Resource{r}
	}

	if len(resources) == 0 {
		return commandResults{err: errors.New("no resources specified")}
	}

	if glog.V(2) {
		glog.Infof("Resources: %v\n", resources)
		d, err := yaml.Marshal(resources)
		if err != nil {
			return commandResults{err: err}
		}
		glog.Infof("Data: %s\n", string(d))
	}

	// Load the client config and connect.
	cf := args["--config"].(string)
	client, err := newClient(cf)
	if err != nil {
		return commandResults{err: err}
	}
	glog.V(2).Infof("Client: %v\n", client)

	// Initialise the command results with the number of resources and the name of the
	// kind of resource (if only dealing with a single resource).
	var results commandResults
	var kind string
	count := make(map[string]int)
	for _, r := range resources {
		kind = r.GetTypeMetadata().Kind
		count[kind] = count[kind] + 1
		results.numResources = results.numResources + 1
	}
	if len(count) == 1 {
		results.singleKind = kind
	}

	// Now execute the command on each resource in order, exiting as soon as we hit an
	// error.
	for _, r := range resources {
		r, err = cmd.execute(client, r)
		if err != nil {
			results.err = err
			break
		}
		results.resources = append(results.resources, r)
		results.numHandled = results.numHandled + 1
	}

	return results
}
<commit_msg>Allow plural and case-insensitive resource names on CLI<commit_after>// Copyright (c) 2016 Tigera, Inc. 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 commands

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/glog"
	"github.com/tigera/libcalico-go/calicoctl/resourcemgr"
	"github.com/tigera/libcalico-go/lib/api"
	"github.com/tigera/libcalico-go/lib/api/unversioned"
	"github.com/tigera/libcalico-go/lib/client"
	"github.com/tigera/libcalico-go/lib/net"
	"github.com/tigera/libcalico-go/lib/scope"
)

// Convert loaded resources to a slice of resources for easier processing.
// The loaded resources may be a slice containing resources and resource lists, or
// may be a single resource or a single resource list.  This function handles the
// different possible options to convert to a single slice of resources.
func convertToSliceOfResources(loaded interface{}) []unversioned.Resource {
	r := []unversioned.Resource{}
	glog.V(2).Infof("Converting resource to slice: %v\n", loaded)

	switch reflect.TypeOf(loaded).Kind() {
	case reflect.Slice:
		// Recursively call this to add each resource in the supplied slice to
		// return slice.
		s := reflect.ValueOf(loaded)
		for i := 0; i < s.Len(); i++ {
			r = append(r, convertToSliceOfResources(s.Index(i).Interface())...)
		}
	case reflect.Struct:
		// This is a resource or resource list.  If a resource, add to our return
		// slice.  If a resource list, add each item to our return slice.
		lr := loaded.(unversioned.Resource)
		if strings.HasSuffix(lr.GetTypeMetadata().Kind, "List") {
			items := reflect.ValueOf(loaded).Elem().FieldByName("Items")
			for i := 0; i < items.Len(); i++ {
				r = append(r, items.Index(i).Interface().(unversioned.Resource))
			}
		} else {
			r = append(r, lr)
		}
	case reflect.Ptr:
		// This is a resource or resource list.  If a resource, add to our return
		// slice.  If a resource list, add each item to our return slice.
		lr := reflect.ValueOf(loaded).Elem().Interface().(unversioned.Resource)
		if strings.HasSuffix(lr.GetTypeMetadata().Kind, "List") {
			items := reflect.ValueOf(loaded).Elem().FieldByName("Items")
			for i := 0; i < items.Len(); i++ {
				r = append(r, items.Index(i).Interface().(unversioned.Resource))
			}
		} else {
			r = append(r, lr)
		}
	default:
		panic(errors.New(fmt.Sprintf("unhandled type %v converting to resource slice",
			reflect.TypeOf(loaded).Kind())))
	}

	glog.V(2).Infof("Returning slice: %v\n", r)
	return r
}

// Return a resource instance from the command line arguments.
func getResourceFromArguments(args map[string]interface{}) (unversioned.Resource, error) {
	kind := args["<KIND>"].(string)
	stringOrBlank := func(argName string) string {
		if args[argName] != nil {
			return args[argName].(string)
		}
		return ""
	}
	name := stringOrBlank("<NAME>")
	hostname := stringOrBlank("--hostname")
	resScope := stringOrBlank("--scope")
	switch strings.ToLower(kind) {
	case "hostendpoints":
		fallthrough
	case "hostendpoint":
		h := api.NewHostEndpoint()
		h.Metadata.Name = name
		h.Metadata.Hostname = hostname
		return *h, nil
	case "profiles":
		fallthrough
	case "profile":
		p := api.NewProfile()
		p.Metadata.Name = name
		return *p, nil
	case "policies":
		fallthrough
	case "policy":
		p := api.NewPolicy()
		p.Metadata.Name = name
		return *p, nil
	case "pools":
		fallthrough
	case "pool":
		p := api.NewPool()
		if name != "" {
			_, cidr, err := net.ParseCIDR(name)
			if err != nil {
				return nil, err
			}
			p.Metadata.CIDR = *cidr
		}
		return *p, nil
	case "bgppeers":
		fallthrough
	case "bgppeer":
		p := api.NewBGPPeer()
		if name != "" {
			err := p.Metadata.PeerIP.UnmarshalText([]byte(name))
			if err != nil {
				return nil, err
			}
		}
		p.Metadata.Hostname = hostname
		switch resScope {
		case "node":
			p.Metadata.Scope = scope.Node
		case "global":
			p.Metadata.Scope = scope.Global
		case "":
			p.Metadata.Scope = scope.Undefined
		default:
			return nil, fmt.Errorf("Unrecognized scope '%s', must be one of: global, node", resScope)
		}
		return *p, nil

	default:
		return nil, fmt.Errorf("Resource type '%s' is not unsupported", kind)
	}
}

// Interface to execute a command for a specific resource type.
type commandInterface interface {
	execute(client *client.Client, resource unversioned.Resource) (unversioned.Resource, error)
}

// Results from executing a CLI command
type commandResults struct {
	// Whether the input file was invalid.
	fileInvalid bool

	// The number of resources that are being configured.
	numResources int

	// The number of resources that were actually configured.  This will
	// never be 0 without an associated error.
	numHandled int

	// The associated error.
	err error

	// The single type of resource that is being configured, or blank
	// if multiple resource types are being configured in a single shot.
	singleKind string

	// The results returned from each invocation
	resources []unversioned.Resource
}

// Common function for configuration commands apply, create, replace, get and delete.  All
// these commands:
// 	-  Load resources from file (or if not specified determine the resource from
// 	   the command line options).
// 	-  Convert the loaded resources into a list of resources (easier to handle)
// 	-  Process each resource individually, collate results and exit on the first error.
func executeConfigCommand(args map[string]interface{}, cmd commandInterface) commandResults {
	var r interface{}
	var err error
	var resources []unversioned.Resource

	glog.V(2).Info("Executing config command")

	if filename := args["--filename"]; filename != nil {
		// Filename is specified, load the resource from file and convert to a slice
		// of resources for easier handling.
		if r, err = resourcemgr.CreateResourcesFromFile(filename.(string)); err != nil {
			return commandResults{err: err, fileInvalid: true}
		}

		resources = convertToSliceOfResources(r)
	} else if r, err := getResourceFromArguments(args); err != nil {
		// Filename is not specific so extract the resource from the arguments.  This
		// is only useful for delete and get functions - but we don't need to check that
		// here since the command syntax requires a filename for the other resource
		// management commands.
		return commandResults{err: err}
	} else {
		// We extracted a single resource type with identifiers from the CLI, convert to
		// a list for simpler handling.
		resources = []unversioned.Resource{r}
	}

	if len(resources) == 0 {
		return commandResults{err: errors.New("no resources specified")}
	}

	if glog.V(2) {
		glog.Infof("Resources: %v\n", resources)
		d, err := yaml.Marshal(resources)
		if err != nil {
			return commandResults{err: err}
		}
		glog.Infof("Data: %s\n", string(d))
	}

	// Load the client config and connect.
	cf := args["--config"].(string)
	client, err := newClient(cf)
	if err != nil {
		return commandResults{err: err}
	}
	glog.V(2).Infof("Client: %v\n", client)

	// Initialise the command results with the number of resources and the name of the
	// kind of resource (if only dealing with a single resource).
	var results commandResults
	var kind string
	count := make(map[string]int)
	for _, r := range resources {
		kind = r.GetTypeMetadata().Kind
		count[kind] = count[kind] + 1
		results.numResources = results.numResources + 1
	}
	if len(count) == 1 {
		results.singleKind = kind
	}

	// Now execute the command on each resource in order, exiting as soon as we hit an
	// error.
	for _, r := range resources {
		r, err = cmd.execute(client, r)
		if err != nil {
			results.err = err
			break
		}
		results.resources = append(results.resources, r)
		results.numHandled = results.numHandled + 1
	}

	return results
}
<|endoftext|>