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

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

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

	"github.com/spf13/cobra"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
)

const Bootstrapper = "bootstrapper"

type setFn func(string, string) error

type Setting struct {
	name        string
	set         func(config.MinikubeConfig, string, string) error
	setMap      func(config.MinikubeConfig, string, map[string]interface{}) error
	validations []setFn
	callbacks   []setFn
}

// These are all the settings that are configurable
// and their validation and callback fn run on Set
var settings = []Setting{
	{
		name:        "vm-driver",
		set:         SetString,
		validations: []setFn{IsValidDriver},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "v",
		set:         SetInt,
		validations: []setFn{IsPositive},
	},
	{
		name:        "cpus",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "disk-size",
		set:         SetString,
		validations: []setFn{IsValidDiskSize},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "host-only-cidr",
		set:         SetString,
		validations: []setFn{IsValidCIDR},
	},
	{
		name:        "memory",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "log_dir",
		set:         SetString,
		validations: []setFn{IsValidPath},
	},
	{
		name: "kubernetes-version",
		set:  SetString,
	},
	{
		name:        "iso-url",
		set:         SetString,
		validations: []setFn{IsValidURL, IsURLExists},
	},
	{
		name: config.WantUpdateNotification,
		set:  SetBool,
	},
	{
		name: config.ReminderWaitPeriodInHours,
		set:  SetInt,
	},
	{
		name: config.WantReportError,
		set:  SetBool,
	},
	{
		name: config.WantReportErrorPrompt,
		set:  SetBool,
	},
	{
		name: config.WantKubectlDownloadMsg,
		set:  SetBool,
	},
	{
		name: config.WantNoneDriverWarning,
		set:  SetBool,
	},
	{
		name: config.MachineProfile,
		set:  SetString,
	},
	{
		name: Bootstrapper,
		set:  SetString, //TODO(r2d4): more validation here?
	},
	{
		name: config.ShowBootstrapperDeprecationNotification,
		set:  SetBool,
	},
	{
		name:        "dashboard",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "addon-manager",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "heapster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "efk",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "ingress",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry-creds",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "freshpod",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "storage-provisioner",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "storage-provisioner-gluster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "metrics-server",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-driver-installer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-gpu-device-plugin",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "logviewer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
	},
	{
		name:        "gvisor",
		set:         SetBool,
		validations: []setFn{IsValidAddon, IsContainerdRuntime},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name: "hyperv-virtual-switch",
		set:  SetString,
	},
	{
		name: "disable-driver-mounts",
		set:  SetBool,
	},
	{
		name:   "cache",
		set:    SetConfigMap,
		setMap: SetMap,
	},
	{
		name: "embed-certs",
		set:  SetBool,
	},
}

var ConfigCmd = &cobra.Command{
	Use:   "config SUBCOMMAND [flags]",
	Short: "Modify minikube config",
	Long: `config modifies minikube config files using subcommands like "minikube config set vm-driver kvm"
Configurable fields: ` + "\n\n" + configurableFields(),
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

func configurableFields() string {
	fields := []string{}
	for _, s := range settings {
		fields = append(fields, " * "+s.name)
	}
	return strings.Join(fields, "\n")
}

// ListConfigMap list entries from config file
func ListConfigMap(name string) ([]string, error) {
	configFile, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}
	var images []string
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			images = append(images, key)
		}
	}
	return images, nil
}

// AddToConfigMap adds entries to a map in the config file
func AddToConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	newImages := make(map[string]interface{})
	for _, image := range images {
		newImages[image] = nil
	}
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			newImages[key] = nil
		}
	}
	if err = s.setMap(configFile, name, newImages); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// DeleteFromConfigMap deletes entries from a map in the config file
func DeleteFromConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	values, ok := configFile[name]
	if !ok {
		return nil
	}
	for _, image := range images {
		delete(values.(map[string]interface{}), image)
	}
	if err = s.setMap(configFile, name, values.(map[string]interface{})); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// WriteConfig writes a minikube config to the JSON file
func WriteConfig(m config.MinikubeConfig) error {
	f, err := os.Create(constants.ConfigFile)
	if err != nil {
		return fmt.Errorf("Could not open file %s: %s", constants.ConfigFile, err)
	}
	defer f.Close()
	err = encode(f, m)
	if err != nil {
		return fmt.Errorf("Error encoding config %s: %s", constants.ConfigFile, err)
	}
	return nil
}

func encode(w io.Writer, m config.MinikubeConfig) error {
	b, err := json.MarshalIndent(m, "", "    ")
	if err != nil {
		return err
	}

	_, err = w.Write(b)

	return err
}
<commit_msg>[FEATURE] feature-gates via minikube config set<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

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

	"github.com/spf13/cobra"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
)

const Bootstrapper = "bootstrapper"

type setFn func(string, string) error

type Setting struct {
	name        string
	set         func(config.MinikubeConfig, string, string) error
	setMap      func(config.MinikubeConfig, string, map[string]interface{}) error
	validations []setFn
	callbacks   []setFn
}

// These are all the settings that are configurable
// and their validation and callback fn run on Set
var settings = []Setting{
	{
		name:        "vm-driver",
		set:         SetString,
		validations: []setFn{IsValidDriver},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:      "feature-gates",
		set:       SetString,
		callbacks: []setFn{RequiresRestartMsg},
	},
	{
		name:        "v",
		set:         SetInt,
		validations: []setFn{IsPositive},
	},
	{
		name:        "cpus",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "disk-size",
		set:         SetString,
		validations: []setFn{IsValidDiskSize},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "host-only-cidr",
		set:         SetString,
		validations: []setFn{IsValidCIDR},
	},
	{
		name:        "memory",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "log_dir",
		set:         SetString,
		validations: []setFn{IsValidPath},
	},
	{
		name: "kubernetes-version",
		set:  SetString,
	},
	{
		name:        "iso-url",
		set:         SetString,
		validations: []setFn{IsValidURL, IsURLExists},
	},
	{
		name: config.WantUpdateNotification,
		set:  SetBool,
	},
	{
		name: config.ReminderWaitPeriodInHours,
		set:  SetInt,
	},
	{
		name: config.WantReportError,
		set:  SetBool,
	},
	{
		name: config.WantReportErrorPrompt,
		set:  SetBool,
	},
	{
		name: config.WantKubectlDownloadMsg,
		set:  SetBool,
	},
	{
		name: config.WantNoneDriverWarning,
		set:  SetBool,
	},
	{
		name: config.MachineProfile,
		set:  SetString,
	},
	{
		name: Bootstrapper,
		set:  SetString, //TODO(r2d4): more validation here?
	},
	{
		name: config.ShowBootstrapperDeprecationNotification,
		set:  SetBool,
	},
	{
		name:        "dashboard",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "addon-manager",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "heapster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "efk",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "ingress",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry-creds",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "freshpod",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "storage-provisioner",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "storage-provisioner-gluster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "metrics-server",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-driver-installer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-gpu-device-plugin",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "logviewer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
	},
	{
		name:        "gvisor",
		set:         SetBool,
		validations: []setFn{IsValidAddon, IsContainerdRuntime},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name: "hyperv-virtual-switch",
		set:  SetString,
	},
	{
		name: "disable-driver-mounts",
		set:  SetBool,
	},
	{
		name:   "cache",
		set:    SetConfigMap,
		setMap: SetMap,
	},
	{
		name: "embed-certs",
		set:  SetBool,
	},
}

var ConfigCmd = &cobra.Command{
	Use:   "config SUBCOMMAND [flags]",
	Short: "Modify minikube config",
	Long: `config modifies minikube config files using subcommands like "minikube config set vm-driver kvm"
Configurable fields: ` + "\n\n" + configurableFields(),
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

func configurableFields() string {
	fields := []string{}
	for _, s := range settings {
		fields = append(fields, " * "+s.name)
	}
	return strings.Join(fields, "\n")
}

// ListConfigMap list entries from config file
func ListConfigMap(name string) ([]string, error) {
	configFile, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}
	var images []string
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			images = append(images, key)
		}
	}
	return images, nil
}

// AddToConfigMap adds entries to a map in the config file
func AddToConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	newImages := make(map[string]interface{})
	for _, image := range images {
		newImages[image] = nil
	}
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			newImages[key] = nil
		}
	}
	if err = s.setMap(configFile, name, newImages); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// DeleteFromConfigMap deletes entries from a map in the config file
func DeleteFromConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	values, ok := configFile[name]
	if !ok {
		return nil
	}
	for _, image := range images {
		delete(values.(map[string]interface{}), image)
	}
	if err = s.setMap(configFile, name, values.(map[string]interface{})); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// WriteConfig writes a minikube config to the JSON file
func WriteConfig(m config.MinikubeConfig) error {
	f, err := os.Create(constants.ConfigFile)
	if err != nil {
		return fmt.Errorf("Could not open file %s: %s", constants.ConfigFile, err)
	}
	defer f.Close()
	err = encode(f, m)
	if err != nil {
		return fmt.Errorf("Error encoding config %s: %s", constants.ConfigFile, err)
	}
	return nil
}

func encode(w io.Writer, m config.MinikubeConfig) error {
	b, err := json.MarshalIndent(m, "", "    ")
	if err != nil {
		return err
	}

	_, err = w.Write(b)

	return err
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp 2016 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 main

import (
	"errors"
	"fmt"
	"encoding/json"

	"github.com/hyperledger/fabric/core/chaincode/shim"

)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type Emp struct{
	empId string `json:"empId"`
	name string `json:"name"`
	title string `json:"title"`

}

// CountApplication is for storing retreived Application count
type Empppcounter struct{
	empCounter int `json:"count"`
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	  err := stub.PutState("table_ibminsert", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	// Check if table already exists
	_,  err = stub.GetTable("EmpTable")

	if err == nil {
		// Table already exists; do not recreate
		return nil, nil
	}
     fmt.Println("ready to create the table: ")
	// Create application Table
	err = stub.CreateTable("EmpTable", []*shim.ColumnDefinition{
		&shim.ColumnDefinition{Name: "empId", Type: shim.ColumnDefinition_STRING, Key: true},
		&shim.ColumnDefinition{Name: "name", Type: shim.ColumnDefinition_STRING, Key: false},
		&shim.ColumnDefinition{Name: "title", Type: shim.ColumnDefinition_STRING, Key: false},

	})
	if err != nil {
		return nil, errors.New("Failed creating ApplicationTable.")

	}

	return nil, nil
	}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	if function == "submitEmp" {
		if len(args) != 3 {
			return nil, fmt.Errorf("Incorrect number of arguments. Expecting 20. Got: %d.", len(args))
		}

		empId := args[0]
		name := args[1]
		title := args[2]
		fmt.Println("values Inserted in the table: empid"+empId)
		fmt.Println("values Inserted in the table: name"+name)
		fmt.Println("values Inserted in the table: empid"+title)

		//insert a row

		ok, err := stub.InsertRow("EmpTable", shim.Row{
		Columns: []*shim.Column{
				&shim.Column{Value: &shim.Column_String_{String_: empId}},
				&shim.Column{Value: &shim.Column_String_{String_: name}},
				&shim.Column{Value: &shim.Column_String_{String_: title}},
				}})

	if !ok && err == nil {
			return nil, errors.New("Row already exists.")
		}

	}
	fmt.Println("values Inserted in the table: ")

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting applicationid to query")
	}

	fmt.Println("came into read func and geting empid: ")

	empId := args[0]

fmt.Println("came into read func and geting empid: "+empId)

	var columns []shim.Column
	emplCounter := 0

	rows, err := stub.GetRows("EmpTable", columns)

	if err != nil {
		return nil, fmt.Errorf("Failed to retrieve row")
	}

	for row := range rows {
		if len(row.Columns) != 0 {
			emplCounter++
		}
	}

	res2E := Empppcounter{}
	res2E.empCounter = emplCounter
	mapB, _ := json.Marshal(res2E)
    fmt.Println(string(mapB))

	return mapB, nil

}
<commit_msg>addcode<commit_after>/*
Copyright IBM Corp 2016 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 main

import (
	"errors"
	"fmt"
	"encoding/json"

	"github.com/hyperledger/fabric/core/chaincode/shim"

)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type Emp struct{
	empId string `json:"empId"`
	name string `json:"name"`
	title string `json:"title"`

}

// CountApplication is for storing retreived Application count
type Empppcounter struct{
	empCounter int `json:"count"`
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	  err := stub.PutState("table_ibminsert", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	// Check if table already exists
	_,  err = stub.GetTable("EmpTable")

	if err == nil {
		// Table already exists; do not recreate
		return nil, nil
	}
     fmt.Println("ready to create the table: ")
	// Create application Table
	err = stub.CreateTable("EmpTable", []*shim.ColumnDefinition{
		&shim.ColumnDefinition{Name: "empId", Type: shim.ColumnDefinition_STRING, Key: true},
		&shim.ColumnDefinition{Name: "name", Type: shim.ColumnDefinition_STRING, Key: false},
		&shim.ColumnDefinition{Name: "title", Type: shim.ColumnDefinition_STRING, Key: false},

	})
	if err != nil {
		return nil, errors.New("Failed creating ApplicationTable.")

	}

	return nil, nil
	}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	if function == "submitEmp" {
		if len(args) != 3 {
			return nil, fmt.Errorf("Incorrect number of arguments. Expecting 20. Got: %d.", len(args))
		}

		empId := args[0]
		name := args[1]
		title := args[2]
		fmt.Println("values Inserted in the table: empid"+empId)
		fmt.Println("values Inserted in the table: name"+name)
		fmt.Println("values Inserted in the table: empid"+title)

		//insert a row

		ok, err := stub.InsertRow("EmpTable", shim.Row{
		Columns: []*shim.Column{
				&shim.Column{Value: &shim.Column_String_{String_: empId}},
				&shim.Column{Value: &shim.Column_String_{String_: name}},
				&shim.Column{Value: &shim.Column_String_{String_: title}},
				}})

	if !ok && err == nil {
			return nil, errors.New("Row already exists.")
		}

	}
	fmt.Println("values Inserted in the table: ")

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable

		t := SimpleChaincode{}
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting applicationid to query")
	}

	fmt.Println("came into read func and geting empid: ")

	empId := args[0]

fmt.Println("came into read func and geting empid: "+empId)

	var columns []shim.Column
	emplCounter := 0

	rows, err := stub.GetRows("EmpTable", columns)

	if err != nil {
		return nil, fmt.Errorf("Failed to retrieve row")
	}

	for row := range rows {
		if len(row.Columns) != 0 {
			emplCounter++
		}
	}

	res2E := Empppcounter{}
	res2E.empCounter = emplCounter
	mapB, _ := json.Marshal(res2E)
    fmt.Println(string(mapB))

	return mapB, nil

}
<|endoftext|>


<commit_before>// Copyright 2015 Christian Gärtner. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.

package compiler

import (
	"errors"
	"strings"
	builder "github.com/achtern/kluver/build"
	"github.com/achtern/kluver/lexer"
)

func New(path, includePath string,loader FileLoader) (*builder.Shader, error) {
	raw, err := loader.Get(path)
	if err != nil {
		return nil, err
	}

	// prepare includePath
	if !strings.HasSuffix(includePath, "/") {
		includePath = includePath + "/"
	}

	// initial
	tokens := make(chan lexer.Token)
	lexer.New(path, raw, tokens)

	buildStream := builder.New(tokens)

	for {
		select {
		case err := <-buildStream.Err:
			return nil, err
		case req := <-buildStream.Request:
			lib, err := loader.Get(includePath + req.Path)
			if err != nil {
				return nil, errors.New("Failed to lib <" + req.Path + ">")
			}
			lexer.New(req.Path, lib, req.Answer)
		case rep := <-buildStream.Response:
			return &rep, nil
		}
	}

}<commit_msg>compiler uses util package to add trailing slashes<commit_after>// Copyright 2015 Christian Gärtner. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.

package compiler

import (
	"errors"
	builder "github.com/achtern/kluver/build"
	"github.com/achtern/kluver/lexer"
	"github.com/achtern/kluver/util"
)

func New(path, includePath string,loader FileLoader) (*builder.Shader, error) {
	raw, err := loader.Get(path)
	if err != nil {
		return nil, err
	}

	// prepare includePath
	includePath = util.AddTrailingSlash(includePath)

	// initial
	tokens := make(chan lexer.Token)
	lexer.New(path, raw, tokens)

	buildStream := builder.New(tokens)

	for {
		select {
		case err := <-buildStream.Err:
			return nil, err
		case req := <-buildStream.Request:
			lib, err := loader.Get(includePath + req.Path)
			if err != nil {
				return nil, errors.New("Failed to lib <" + req.Path + ">")
			}
			lexer.New(req.Path, lib, req.Answer)
		case rep := <-buildStream.Response:
			return &rep, nil
		}
	}

}<|endoftext|>


<commit_before>package disruptor

type CompositeBarrier []*Sequence

func NewCompositeBarrier(sequences []*Sequence) CompositeBarrier { return sequences }

func (this CompositeBarrier) Load() int64 {
	var minimum = maxCursorSequenceValue

	for _, item := range this {
		if sequence := item.Load(); sequence < minimum {
			minimum = sequence
		}
	}

	return minimum
}

const maxCursorSequenceValue int64 = (1 << 63) - 1
<commit_msg>Using system constant.<commit_after>package disruptor

import "math"

type CompositeBarrier []*Sequence

func NewCompositeBarrier(sequences []*Sequence) CompositeBarrier { return sequences }

func (this CompositeBarrier) Load() int64 {
	var minimum int64 = math.MaxInt64

	for _, item := range this {
		if sequence := item.Load(); sequence < minimum {
			minimum = sequence
		}
	}

	return minimum
}
<|endoftext|>


<commit_before>package termt

import (
	"terminator"
	"time"
)

func Auto(term terminator.I, wait time.Duration) {
	defer close(term.Chan())
	time.Sleep(wait)
}
<commit_msg>fix import<commit_after>package termt

import (
	"time"

	"github.com/WhisperingChaos/terminator"
)

func Auto(term terminator.I, wait time.Duration) {
	defer close(term.Chan())
	time.Sleep(wait)
}
<|endoftext|>


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

// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/

package time

import (
	"once";
	"os"
)

const (
	MaxFileSize = 8192;	// actual files are closer to 1K
	HeaderSize = 4+16+4*7
)

export var (
	BadZoneinfo = os.NewError("time: malformed zoneinfo");
	NoZoneinfo = os.NewError("time: unknown time zone")
)

// Simple I/O interface to binary blob of data.
type Data struct {
	p *[]byte
}

func (d *Data) Read(n int) *[]byte {
	if d.p == nil || len(d.p) < n {
		d.p = nil
		return nil
	}
	p := d.p[0:n];
	d.p = d.p[n:len(d.p)]
	return p
}

func (d *Data) Big4() (n uint32, ok bool) {
	p := d.Read(4)
	if p == nil {
		return 0, false
	}
	return uint32(p[0]) << 24 | uint32(p[1]) << 16 | uint32(p[2]) << 8 | uint32(p[3]), true
}

func (d *Data) Byte() (n byte, ok bool) {
	p := d.Read(1)
	if p == nil {
		return 0, false
	}
	return p[0], true
}

// Make a string by stopping at the first NUL
func ByteString(p *[]byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

// Parsed representation
type Zone struct {
	utcoff int;
	isdst bool;
	name string
}

type Zonetime struct {
	time int32;		// transition time, in seconds since 1970 GMT
	zone *Zone;		// the zone that goes into effect at that time
	isstd, isutc bool;	// ignored - no idea what these mean
}

func ParseZoneinfo(bytes *[]byte) (zt *[]Zonetime, err *os.Error) {
	data1 := Data(bytes);
	data := &data1;

	// 4-byte magic "TZif"
	if magic := data.Read(4); magic == nil || string(magic) != "TZif" {
		return nil, BadZoneinfo
	}

	// 1-byte version, then 15 bytes of padding
	var p *[]byte
	if p = data.Read(16); p == nil || p[0] != 0 && p[0] != 2 {
		return nil, BadZoneinfo
	}
	vers := p[0]

	// six big-endian 32-bit integers:
	//	number of UTC/local indicators
	//	number of standard/wall indicators
	//	number of leap seconds
	//	number of transition times
	//	number of local time zones
	//	number of characters of time zone abbrev strings
	const (
		NUTCLocal = iota;
		NStdWall;
		NLeap;
		NTime;
		NZone;
		NChar
	)
	var n [6]int
	for i := 0; i < 6; i++ {
		nn, ok := data.Big4()
		if !ok {
			return nil, BadZoneinfo
		}
		n[i] = int(nn);
	}

	// Transition times.
	txtimes1 := Data(data.Read(n[NTime]*4));
	txtimes := &txtimes1;

	// Time zone indices for transition times.
	txzones := data.Read(n[NTime]);

	// Zone info structures
	zonedata1 := Data(data.Read(n[NZone]*6));
	zonedata := &zonedata1;

	// Time zone abbreviations.
	abbrev := data.Read(n[NChar]);

	// Leap-second time pairs
	leapdata1 := Data(data.Read(n[NLeap]*8));
	leapdata := &leapdata1;

	// Whether tx times associated with local time types
	// are specified as standard time or wall time.
	isstd := data.Read(n[NStdWall]);

	// Whether tx times associated with local time types
	// are specified as UTC or local time.
	isutc := data.Read(n[NUTCLocal]);

	if data.p == nil {	// ran out of data
		return nil, BadZoneinfo
	}

	// If version == 2, the entire file repeats, this time using
	// 8-byte ints for txtimes and leap seconds.
	// We won't need those until 2106.

	// Now we can build up a useful data structure.
	// First the zone information.
	//	utcoff[4] isdst[1] nameindex[1]
	zone := new([]Zone, n[NZone])
	for i := 0; i < len(zone); i++ {
		var ok bool;
		var n uint32
		if n, ok = zonedata.Big4(); !ok {
			return nil, BadZoneinfo
		}
		zone[i].utcoff = int(n)
		var b byte
		if b, ok = zonedata.Byte(); !ok {
			return nil, BadZoneinfo
		}
		zone[i].isdst = b != 0
		if b, ok = zonedata.Byte(); !ok || int(b) >= len(abbrev) {
			return nil, BadZoneinfo
		}
		zone[i].name = ByteString(abbrev[b:len(abbrev)])
	}

	// Now the transition time info.
	zt = new([]Zonetime, n[NTime])
	for i := 0; i < len(zt); i++ {
		var ok bool;
		var n uint32
		if n, ok = txtimes.Big4(); !ok {
			return nil, BadZoneinfo
		}
		zt[i].time = int32(n)
		if int(txzones[i]) >= len(zone) {
			return nil, BadZoneinfo
		}
		zt[i].zone = &zone[txzones[i]];
		if i < len(isstd) {
			zt[i].isstd = isstd[i] != 0
		}
		if i < len(isutc) {
			zt[i].isutc = isutc[i] != 0
		}
	}
	return zt, nil
}

func ReadFile(name string, max int) (p *[]byte, err *os.Error) {
	fd, e := os.Open(name, os.O_RDONLY, 0)
	if e != nil {
		return nil, e
	}
	p = new([]byte, max+1)[0:0];
	n := 0
	for len(p) < max {
		nn, e := fd.Read(p[n:cap(p)])
		if e != nil {
			fd.Close()
			return nil, e
		}
		if nn == 0 {
			fd.Close()
			return p, nil
		}
		p = p[0:n+nn]
	}
	fd.Close()
	return nil, BadZoneinfo	// too long
}

func ReadZoneinfoFile(name string) (tx *[]Zonetime, err *os.Error) {
	data, e := ReadFile(name, MaxFileSize)
	if e != nil {
		return nil, e
	}
	tx, err = ParseZoneinfo(data)
	return tx, err
}

var zones *[]Zonetime
var zoneerr *os.Error

func SetupZone() {
	// TODO: /etc/localtime is the default time zone info
	// for the system, but libc allows setting an environment
	// variable in order to direct reading a different file
	// (in /usr/share/zoneinfo).  We should check that
	// environment variable.
	zones, zoneerr = ReadZoneinfoFile("/etc/localtime");
}

export func LookupTimezone(sec int64) (zone string, offset int, err *os.Error) {
	once.Do(&SetupZone)
	if zoneerr != nil || zones == nil || len(zones) == 0 {
		return "GMT", 0, zoneerr
	}

	// Binary search for entry with largest time <= sec
	tz := zones;
	for len(tz) > 1 {
		m := len(tz)/2;
		if sec < int64(tz[m].time) {
			tz = tz[0:m]
		} else {
			tz = tz[m:len(tz)]
		}
	}
	z := tz[0].zone
	return z.name, z.utcoff, nil
}
<commit_msg>Correct zoneinfo version number: it's not 2, it's '2'.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/

package time

import (
	"once";
	"os"
)

const (
	MaxFileSize = 8192;	// actual files are closer to 1K
	HeaderSize = 4+16+4*7
)

export var (
	BadZoneinfo = os.NewError("time: malformed zoneinfo");
	NoZoneinfo = os.NewError("time: unknown time zone")
)

// Simple I/O interface to binary blob of data.
type Data struct {
	p *[]byte
}

func (d *Data) Read(n int) *[]byte {
	if d.p == nil || len(d.p) < n {
		d.p = nil
		return nil
	}
	p := d.p[0:n];
	d.p = d.p[n:len(d.p)]
	return p
}

func (d *Data) Big4() (n uint32, ok bool) {
	p := d.Read(4)
	if p == nil {
		return 0, false
	}
	return uint32(p[0]) << 24 | uint32(p[1]) << 16 | uint32(p[2]) << 8 | uint32(p[3]), true
}

func (d *Data) Byte() (n byte, ok bool) {
	p := d.Read(1)
	if p == nil {
		return 0, false
	}
	return p[0], true
}

// Make a string by stopping at the first NUL
func ByteString(p *[]byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

// Parsed representation
type Zone struct {
	utcoff int;
	isdst bool;
	name string
}

type Zonetime struct {
	time int32;		// transition time, in seconds since 1970 GMT
	zone *Zone;		// the zone that goes into effect at that time
	isstd, isutc bool;	// ignored - no idea what these mean
}

func ParseZoneinfo(bytes *[]byte) (zt *[]Zonetime, err *os.Error) {
	data1 := Data(bytes);
	data := &data1;

	// 4-byte magic "TZif"
	if magic := data.Read(4); magic == nil || string(magic) != "TZif" {
		return nil, BadZoneinfo
	}

	// 1-byte version, then 15 bytes of padding
	var p *[]byte
	if p = data.Read(16); p == nil || p[0] != 0 && p[0] != '2' {
		return nil, BadZoneinfo
	}
	vers := p[0]

	// six big-endian 32-bit integers:
	//	number of UTC/local indicators
	//	number of standard/wall indicators
	//	number of leap seconds
	//	number of transition times
	//	number of local time zones
	//	number of characters of time zone abbrev strings
	const (
		NUTCLocal = iota;
		NStdWall;
		NLeap;
		NTime;
		NZone;
		NChar
	)
	var n [6]int
	for i := 0; i < 6; i++ {
		nn, ok := data.Big4()
		if !ok {
			return nil, BadZoneinfo
		}
		n[i] = int(nn);
	}

	// Transition times.
	txtimes1 := Data(data.Read(n[NTime]*4));
	txtimes := &txtimes1;

	// Time zone indices for transition times.
	txzones := data.Read(n[NTime]);

	// Zone info structures
	zonedata1 := Data(data.Read(n[NZone]*6));
	zonedata := &zonedata1;

	// Time zone abbreviations.
	abbrev := data.Read(n[NChar]);

	// Leap-second time pairs
	leapdata1 := Data(data.Read(n[NLeap]*8));
	leapdata := &leapdata1;

	// Whether tx times associated with local time types
	// are specified as standard time or wall time.
	isstd := data.Read(n[NStdWall]);

	// Whether tx times associated with local time types
	// are specified as UTC or local time.
	isutc := data.Read(n[NUTCLocal]);

	if data.p == nil {	// ran out of data
		return nil, BadZoneinfo
	}

	// If version == 2, the entire file repeats, this time using
	// 8-byte ints for txtimes and leap seconds.
	// We won't need those until 2106.

	// Now we can build up a useful data structure.
	// First the zone information.
	//	utcoff[4] isdst[1] nameindex[1]
	zone := new([]Zone, n[NZone])
	for i := 0; i < len(zone); i++ {
		var ok bool;
		var n uint32
		if n, ok = zonedata.Big4(); !ok {
			return nil, BadZoneinfo
		}
		zone[i].utcoff = int(n)
		var b byte
		if b, ok = zonedata.Byte(); !ok {
			return nil, BadZoneinfo
		}
		zone[i].isdst = b != 0
		if b, ok = zonedata.Byte(); !ok || int(b) >= len(abbrev) {
			return nil, BadZoneinfo
		}
		zone[i].name = ByteString(abbrev[b:len(abbrev)])
	}

	// Now the transition time info.
	zt = new([]Zonetime, n[NTime])
	for i := 0; i < len(zt); i++ {
		var ok bool;
		var n uint32
		if n, ok = txtimes.Big4(); !ok {
			return nil, BadZoneinfo
		}
		zt[i].time = int32(n)
		if int(txzones[i]) >= len(zone) {
			return nil, BadZoneinfo
		}
		zt[i].zone = &zone[txzones[i]];
		if i < len(isstd) {
			zt[i].isstd = isstd[i] != 0
		}
		if i < len(isutc) {
			zt[i].isutc = isutc[i] != 0
		}
	}
	return zt, nil
}

func ReadFile(name string, max int) (p *[]byte, err *os.Error) {
	fd, e := os.Open(name, os.O_RDONLY, 0)
	if e != nil {
		return nil, e
	}
	p = new([]byte, max+1)[0:0];
	n := 0
	for len(p) < max {
		nn, e := fd.Read(p[n:cap(p)])
		if e != nil {
			fd.Close()
			return nil, e
		}
		if nn == 0 {
			fd.Close()
			return p, nil
		}
		p = p[0:n+nn]
	}
	fd.Close()
	return nil, BadZoneinfo	// too long
}

func ReadZoneinfoFile(name string) (tx *[]Zonetime, err *os.Error) {
	data, e := ReadFile(name, MaxFileSize)
	if e != nil {
		return nil, e
	}
	tx, err = ParseZoneinfo(data)
	return tx, err
}

var zones *[]Zonetime
var zoneerr *os.Error

func SetupZone() {
	// TODO: /etc/localtime is the default time zone info
	// for the system, but libc allows setting an environment
	// variable in order to direct reading a different file
	// (in /usr/share/zoneinfo).  We should check that
	// environment variable.
	zones, zoneerr = ReadZoneinfoFile("/etc/localtime");
}

export func LookupTimezone(sec int64) (zone string, offset int, err *os.Error) {
	once.Do(&SetupZone)
	if zoneerr != nil || zones == nil || len(zones) == 0 {
		return "GMT", 0, zoneerr
	}

	// Binary search for entry with largest time <= sec
	tz := zones;
	for len(tz) > 1 {
		m := len(tz)/2;
		if sec < int64(tz[m].time) {
			tz = tz[0:m]
		} else {
			tz = tz[m:len(tz)]
		}
	}
	z := tz[0].zone
	return z.name, z.utcoff, nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/fileio"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	flags "github.com/jessevdk/go-flags"
)

type logger interface {
	Println(string)
}

type StateBootstrap interface {
	GetState(string) (storage.State, error)
}

type migrator interface {
	Migrate(storage.State) (storage.State, error)
}

type fs interface {
	fileio.Stater
	fileio.TempFiler
	fileio.FileReader
	fileio.FileWriter
}

func NewConfig(bootstrap StateBootstrap, migrator migrator, logger logger, fs fs) Config {
	return Config{
		stateBootstrap: bootstrap,
		migrator:       migrator,
		logger:         logger,
		fs:             fs,
	}
}

type Config struct {
	stateBootstrap StateBootstrap
	migrator       migrator
	logger         logger
	fs             fs
}

func ParseArgs(args []string) (globalFlags, []string, error) {
	var globals globalFlags
	parser := flags.NewParser(&globals, flags.IgnoreUnknown)

	remainingArgs, err := parser.ParseArgs(args[1:])
	if err != nil {
		return globalFlags{}, remainingArgs, err
	}

	if !filepath.IsAbs(globals.StateDir) {
		workingDir, err := os.Getwd()
		if err != nil {
			return globalFlags{}, remainingArgs, err // not tested
		}
		globals.StateDir = filepath.Join(workingDir, globals.StateDir)
	}

	return globals, remainingArgs, nil
}

func (c Config) Bootstrap(args []string) (application.Configuration, error) {
	if len(args) == 1 {
		return application.Configuration{
			Command: "help",
		}, nil
	}

	globalFlags, remainingArgs, err := ParseArgs(args)
	if err != nil {
		return application.Configuration{}, err
	}

	var command string
	if len(remainingArgs) > 0 {
		command = remainingArgs[0]
	}

	if globalFlags.Version || command == "version" {
		command = "version"
		return application.Configuration{
			ShowCommandHelp: globalFlags.Help,
			Command:         command,
		}, nil
	}

	if len(remainingArgs) == 0 {
		return application.Configuration{
			Command: "help",
		}, nil
	}

	if len(remainingArgs) == 1 && command == "help" {
		return application.Configuration{
			Command: command,
		}, nil
	}

	if command == "help" {
		return application.Configuration{
			ShowCommandHelp: true,
			Command:         remainingArgs[1],
		}, nil
	}

	if globalFlags.Help {
		return application.Configuration{
			ShowCommandHelp: true,
			Command:         command,
		}, nil
	}

	state, err := c.stateBootstrap.GetState(globalFlags.StateDir)
	if err != nil {
		return application.Configuration{}, err
	}

	state, err = c.migrator.Migrate(state)
	if err != nil {
		return application.Configuration{}, err
	}

	state, err = c.updateIAASState(globalFlags, state)
	if err != nil {
		return application.Configuration{}, err
	}

	return application.Configuration{
		Global: application.GlobalConfiguration{
			Debug:    globalFlags.Debug,
			StateDir: globalFlags.StateDir,
		},
		State:           state,
		Command:         command,
		SubcommandFlags: remainingArgs[1:],
		ShowCommandHelp: globalFlags.Help,
	}, nil
}

func (c Config) updateIAASState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	if globalFlags.IAAS != "" {
		if state.IAAS != "" && globalFlags.IAAS != state.IAAS {
			return storage.State{}, fmt.Errorf("The iaas type cannot be changed for an existing environment. The current iaas type is %s.", state.IAAS)
		}
		state.IAAS = globalFlags.IAAS
	}

	switch state.IAAS {
	case "aws":
		return c.updateAWSState(globalFlags, state)
	case "azure":
		return c.updateAzureState(globalFlags, state)
	case "gcp":
		return c.updateGCPState(globalFlags, state)
	case "vsphere":
		return c.updateVSphereState(globalFlags, state)
	case "openstack":
		return c.updateOpenStackState(globalFlags, state)
	}

	return state, nil
}

func copyFlagToState(source string, sink *string) {
	if source != "" {
		*sink = source
	}
}

func (c Config) updateOpenStackState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.OpenStackInternalCidr, &state.OpenStack.InternalCidr)
	copyFlagToState(globalFlags.OpenStackExternalIP, &state.OpenStack.ExternalIP)
	copyFlagToState(globalFlags.OpenStackAuthURL, &state.OpenStack.AuthURL)
	copyFlagToState(globalFlags.OpenStackAZ, &state.OpenStack.AZ)
	copyFlagToState(globalFlags.OpenStackDefaultKeyName, &state.OpenStack.DefaultKeyName)
	copyFlagToState(globalFlags.OpenStackDefaultSecurityGroup, &state.OpenStack.DefaultSecurityGroup)
	copyFlagToState(globalFlags.OpenStackNetworkID, &state.OpenStack.NetworkID)
	copyFlagToState(globalFlags.OpenStackPassword, &state.OpenStack.Password)
	copyFlagToState(globalFlags.OpenStackUsername, &state.OpenStack.Username)
	copyFlagToState(globalFlags.OpenStackProject, &state.OpenStack.Project)
	copyFlagToState(globalFlags.OpenStackDomain, &state.OpenStack.Domain)
	copyFlagToState(globalFlags.OpenStackRegion, &state.OpenStack.Region)
	copyFlagToState(globalFlags.OpenStackRegion, &state.OpenStack.Region)

	if globalFlags.OpenStackPrivateKey != "" {
		_, key, err := c.readKey(globalFlags.OpenStackPrivateKey)
		if err != nil {
			return storage.State{}, err
		}

		state.OpenStack.PrivateKey = key
	}

	return state, nil
}

func (c Config) updateVSphereState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.VSphereVCenterUser, &state.VSphere.VCenterUser)
	copyFlagToState(globalFlags.VSphereVCenterPassword, &state.VSphere.VCenterPassword)
	copyFlagToState(globalFlags.VSphereVCenterIP, &state.VSphere.VCenterIP)
	copyFlagToState(globalFlags.VSphereVCenterDC, &state.VSphere.VCenterDC)
	copyFlagToState(globalFlags.VSphereVCenterRP, &state.VSphere.VCenterRP)
	copyFlagToState(globalFlags.VSphereCluster, &state.VSphere.Cluster)
	copyFlagToState(globalFlags.VSphereNetwork, &state.VSphere.Network)
	copyFlagToState(globalFlags.VSphereVCenterDS, &state.VSphere.VCenterDS)
	copyFlagToState(globalFlags.VSphereSubnet, &state.VSphere.Subnet)

	return state, nil
}

func (c Config) updateAWSState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.AWSAccessKeyID, &state.AWS.AccessKeyID)
	copyFlagToState(globalFlags.AWSSecretAccessKey, &state.AWS.SecretAccessKey)

	if globalFlags.AWSRegion != "" {
		if state.AWS.Region != "" && globalFlags.AWSRegion != state.AWS.Region {
			return storage.State{}, fmt.Errorf("The region cannot be changed for an existing environment. The current region is %s.", state.AWS.Region)
		}
		state.AWS.Region = globalFlags.AWSRegion
	}

	return state, nil
}

func (c Config) updateAzureState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.AzureClientID, &state.Azure.ClientID)
	copyFlagToState(globalFlags.AzureClientSecret, &state.Azure.ClientSecret)
	copyFlagToState(globalFlags.AzureRegion, &state.Azure.Region)
	copyFlagToState(globalFlags.AzureSubscriptionID, &state.Azure.SubscriptionID)
	copyFlagToState(globalFlags.AzureTenantID, &state.Azure.TenantID)

	return state, nil
}

func (c Config) updateGCPState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	if globalFlags.GCPServiceAccountKey != "" {
		path, key, err := c.getGCPServiceAccountKey(globalFlags.GCPServiceAccountKey)
		if err != nil {
			return storage.State{}, err
		}
		state.GCP.ServiceAccountKey = key
		state.GCP.ServiceAccountKeyPath = path

		id, err := c.getGCPProjectID(key)
		if err != nil {
			return storage.State{}, err
		}
		if state.GCP.ProjectID != "" && id != state.GCP.ProjectID {
			return storage.State{}, fmt.Errorf("The project ID cannot be changed for an existing environment. The current project ID is %s.", state.GCP.ProjectID)
		}
		state.GCP.ProjectID = id
	}

	if globalFlags.GCPRegion != "" {
		if state.GCP.Region != "" && globalFlags.GCPRegion != state.GCP.Region {
			return storage.State{}, fmt.Errorf("The region cannot be changed for an existing environment. The current region is %s.", state.GCP.Region)
		}
		state.GCP.Region = globalFlags.GCPRegion
	}

	return state, nil
}

func (c Config) getGCPServiceAccountKey(key string) (string, string, error) {
	if _, err := c.fs.Stat(key); err != nil {
		return c.writeGCPServiceAccountKey(key)
	}
	return c.readKey(key)
}

func (c Config) writeGCPServiceAccountKey(contents string) (string, string, error) {
	tempFile, err := c.fs.TempFile("", "gcpServiceAccountKey.json")
	if err != nil {
		return "", "", fmt.Errorf("Creating temp file for credentials: %s", err)
	}
	err = c.fs.WriteFile(tempFile.Name(), []byte(contents), storage.StateMode)
	if err != nil {
		return "", "", fmt.Errorf("Writing credentials to temp file: %s", err)
	}
	return tempFile.Name(), contents, nil
}

func (c Config) readKey(path string) (string, string, error) {
	keyBytes, err := c.fs.ReadFile(path)
	if err != nil {
		return "", "", fmt.Errorf("Reading key: %v", err)
	}
	return path, string(keyBytes), nil
}

func (c Config) getGCPProjectID(key string) (string, error) {
	p := struct {
		ProjectID string `json:"project_id"`
	}{}

	err := json.Unmarshal([]byte(key), &p)
	if err != nil {
		return "", fmt.Errorf("Unmarshalling service account key (must be valid json): %s", err)
	}

	if p.ProjectID == "" {
		return "", errors.New("Service account key is missing field `project_id`")
	}

	return p.ProjectID, nil
}

func ValidateIAAS(state storage.State) error {
	var err error
	switch state.IAAS {
	case "aws":
		creds := []string{state.AWS.AccessKeyID, state.AWS.SecretAccessKey, state.AWS.Region}
		err = validate("AWS", creds)

	case "azure":
		creds := []string{state.Azure.ClientID, state.Azure.ClientSecret, state.Azure.Region, state.Azure.SubscriptionID, state.Azure.TenantID}
		err = validate("Azure", creds)

	case "gcp":
		creds := []string{state.GCP.ServiceAccountKey, state.GCP.Region}
		err = validate("GCP", creds)

	case "vsphere":
		creds := []string{state.VSphere.VCenterUser, state.VSphere.VCenterPassword, state.VSphere.VCenterIP, state.VSphere.VCenterIP,
			state.VSphere.VCenterDC, state.VSphere.Cluster, state.VSphere.VCenterRP, state.VSphere.Network, state.VSphere.VCenterDS, state.VSphere.Subnet}
		err = validate("vSphere", creds)

	case "openstack":
		creds := []string{state.OpenStack.InternalCidr, state.OpenStack.ExternalIP, state.OpenStack.AuthURL, state.OpenStack.AZ, state.OpenStack.DefaultKeyName,
			state.OpenStack.DefaultSecurityGroup, state.OpenStack.NetworkID, state.OpenStack.Password, state.OpenStack.Username, state.OpenStack.Project,
			state.OpenStack.Domain, state.OpenStack.Region, state.OpenStack.PrivateKey}
		err = validate("OpenStack", creds)

	default:
		err = errors.New("--iaas [gcp, aws, azure, vsphere, openstack] must be provided or BBL_IAAS must be set")
	}

	if err != nil {
		return fmt.Errorf("\n\n%s\n", err)
	}

	return nil
}

func NeedsIAASCreds(command string) bool {
	_, ok := map[string]struct{}{
		"up":                struct{}{},
		"down":              struct{}{},
		"plan":              struct{}{},
		"destroy":           struct{}{},
		"leftovers":         struct{}{},
		"cleanup-leftovers": struct{}{},
		"rotate":            struct{}{},
	}[command]
	return ok
}

func validate(iaas string, creds []string) error {
	for _, s := range creds {
		if s == "" {
			return fmt.Errorf("There are %s credentials missing. To see all required credentials run `bbl plan --help`.", iaas)
		}
	}
	return nil
}
<commit_msg>Use absolute path for OpenStack private key<commit_after>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/fileio"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	flags "github.com/jessevdk/go-flags"
)

type logger interface {
	Println(string)
}

type StateBootstrap interface {
	GetState(string) (storage.State, error)
}

type migrator interface {
	Migrate(storage.State) (storage.State, error)
}

type fs interface {
	fileio.Stater
	fileio.TempFiler
	fileio.FileReader
	fileio.FileWriter
}

func NewConfig(bootstrap StateBootstrap, migrator migrator, logger logger, fs fs) Config {
	return Config{
		stateBootstrap: bootstrap,
		migrator:       migrator,
		logger:         logger,
		fs:             fs,
	}
}

type Config struct {
	stateBootstrap StateBootstrap
	migrator       migrator
	logger         logger
	fs             fs
}

func ParseArgs(args []string) (globalFlags, []string, error) {
	var globals globalFlags
	parser := flags.NewParser(&globals, flags.IgnoreUnknown)

	remainingArgs, err := parser.ParseArgs(args[1:])
	if err != nil {
		return globalFlags{}, remainingArgs, err
	}

	if !filepath.IsAbs(globals.StateDir) {
		workingDir, err := os.Getwd()
		if err != nil {
			return globalFlags{}, remainingArgs, err // not tested
		}
		globals.StateDir = filepath.Join(workingDir, globals.StateDir)
	}

	return globals, remainingArgs, nil
}

func (c Config) Bootstrap(args []string) (application.Configuration, error) {
	if len(args) == 1 {
		return application.Configuration{
			Command: "help",
		}, nil
	}

	globalFlags, remainingArgs, err := ParseArgs(args)
	if err != nil {
		return application.Configuration{}, err
	}

	var command string
	if len(remainingArgs) > 0 {
		command = remainingArgs[0]
	}

	if globalFlags.Version || command == "version" {
		command = "version"
		return application.Configuration{
			ShowCommandHelp: globalFlags.Help,
			Command:         command,
		}, nil
	}

	if len(remainingArgs) == 0 {
		return application.Configuration{
			Command: "help",
		}, nil
	}

	if len(remainingArgs) == 1 && command == "help" {
		return application.Configuration{
			Command: command,
		}, nil
	}

	if command == "help" {
		return application.Configuration{
			ShowCommandHelp: true,
			Command:         remainingArgs[1],
		}, nil
	}

	if globalFlags.Help {
		return application.Configuration{
			ShowCommandHelp: true,
			Command:         command,
		}, nil
	}

	state, err := c.stateBootstrap.GetState(globalFlags.StateDir)
	if err != nil {
		return application.Configuration{}, err
	}

	state, err = c.migrator.Migrate(state)
	if err != nil {
		return application.Configuration{}, err
	}

	state, err = c.updateIAASState(globalFlags, state)
	if err != nil {
		return application.Configuration{}, err
	}

	return application.Configuration{
		Global: application.GlobalConfiguration{
			Debug:    globalFlags.Debug,
			StateDir: globalFlags.StateDir,
		},
		State:           state,
		Command:         command,
		SubcommandFlags: remainingArgs[1:],
		ShowCommandHelp: globalFlags.Help,
	}, nil
}

func (c Config) updateIAASState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	if globalFlags.IAAS != "" {
		if state.IAAS != "" && globalFlags.IAAS != state.IAAS {
			return storage.State{}, fmt.Errorf("The iaas type cannot be changed for an existing environment. The current iaas type is %s.", state.IAAS)
		}
		state.IAAS = globalFlags.IAAS
	}

	switch state.IAAS {
	case "aws":
		return c.updateAWSState(globalFlags, state)
	case "azure":
		return c.updateAzureState(globalFlags, state)
	case "gcp":
		return c.updateGCPState(globalFlags, state)
	case "vsphere":
		return c.updateVSphereState(globalFlags, state)
	case "openstack":
		return c.updateOpenStackState(globalFlags, state)
	}

	return state, nil
}

func copyFlagToState(source string, sink *string) {
	if source != "" {
		*sink = source
	}
}

func (c Config) updateOpenStackState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.OpenStackInternalCidr, &state.OpenStack.InternalCidr)
	copyFlagToState(globalFlags.OpenStackExternalIP, &state.OpenStack.ExternalIP)
	copyFlagToState(globalFlags.OpenStackAuthURL, &state.OpenStack.AuthURL)
	copyFlagToState(globalFlags.OpenStackAZ, &state.OpenStack.AZ)
	copyFlagToState(globalFlags.OpenStackDefaultKeyName, &state.OpenStack.DefaultKeyName)
	copyFlagToState(globalFlags.OpenStackDefaultSecurityGroup, &state.OpenStack.DefaultSecurityGroup)
	copyFlagToState(globalFlags.OpenStackNetworkID, &state.OpenStack.NetworkID)
	copyFlagToState(globalFlags.OpenStackPassword, &state.OpenStack.Password)
	copyFlagToState(globalFlags.OpenStackUsername, &state.OpenStack.Username)
	copyFlagToState(globalFlags.OpenStackProject, &state.OpenStack.Project)
	copyFlagToState(globalFlags.OpenStackDomain, &state.OpenStack.Domain)
	copyFlagToState(globalFlags.OpenStackRegion, &state.OpenStack.Region)
	copyFlagToState(globalFlags.OpenStackRegion, &state.OpenStack.Region)

	if globalFlags.OpenStackPrivateKey != "" {
		absKeyPath, err := filepath.Abs(globalFlags.OpenStackPrivateKey)
		if err != nil {
			return storage.State{}, err
		}

		_, key, err := c.readKey(absKeyPath)
		if err != nil {
			return storage.State{}, err
		}

		state.OpenStack.PrivateKey = key
	}

	return state, nil
}

func (c Config) updateVSphereState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.VSphereVCenterUser, &state.VSphere.VCenterUser)
	copyFlagToState(globalFlags.VSphereVCenterPassword, &state.VSphere.VCenterPassword)
	copyFlagToState(globalFlags.VSphereVCenterIP, &state.VSphere.VCenterIP)
	copyFlagToState(globalFlags.VSphereVCenterDC, &state.VSphere.VCenterDC)
	copyFlagToState(globalFlags.VSphereVCenterRP, &state.VSphere.VCenterRP)
	copyFlagToState(globalFlags.VSphereCluster, &state.VSphere.Cluster)
	copyFlagToState(globalFlags.VSphereNetwork, &state.VSphere.Network)
	copyFlagToState(globalFlags.VSphereVCenterDS, &state.VSphere.VCenterDS)
	copyFlagToState(globalFlags.VSphereSubnet, &state.VSphere.Subnet)

	return state, nil
}

func (c Config) updateAWSState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.AWSAccessKeyID, &state.AWS.AccessKeyID)
	copyFlagToState(globalFlags.AWSSecretAccessKey, &state.AWS.SecretAccessKey)

	if globalFlags.AWSRegion != "" {
		if state.AWS.Region != "" && globalFlags.AWSRegion != state.AWS.Region {
			return storage.State{}, fmt.Errorf("The region cannot be changed for an existing environment. The current region is %s.", state.AWS.Region)
		}
		state.AWS.Region = globalFlags.AWSRegion
	}

	return state, nil
}

func (c Config) updateAzureState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	copyFlagToState(globalFlags.AzureClientID, &state.Azure.ClientID)
	copyFlagToState(globalFlags.AzureClientSecret, &state.Azure.ClientSecret)
	copyFlagToState(globalFlags.AzureRegion, &state.Azure.Region)
	copyFlagToState(globalFlags.AzureSubscriptionID, &state.Azure.SubscriptionID)
	copyFlagToState(globalFlags.AzureTenantID, &state.Azure.TenantID)

	return state, nil
}

func (c Config) updateGCPState(globalFlags globalFlags, state storage.State) (storage.State, error) {
	if globalFlags.GCPServiceAccountKey != "" {
		path, key, err := c.getGCPServiceAccountKey(globalFlags.GCPServiceAccountKey)
		if err != nil {
			return storage.State{}, err
		}
		state.GCP.ServiceAccountKey = key
		state.GCP.ServiceAccountKeyPath = path

		id, err := c.getGCPProjectID(key)
		if err != nil {
			return storage.State{}, err
		}
		if state.GCP.ProjectID != "" && id != state.GCP.ProjectID {
			return storage.State{}, fmt.Errorf("The project ID cannot be changed for an existing environment. The current project ID is %s.", state.GCP.ProjectID)
		}
		state.GCP.ProjectID = id
	}

	if globalFlags.GCPRegion != "" {
		if state.GCP.Region != "" && globalFlags.GCPRegion != state.GCP.Region {
			return storage.State{}, fmt.Errorf("The region cannot be changed for an existing environment. The current region is %s.", state.GCP.Region)
		}
		state.GCP.Region = globalFlags.GCPRegion
	}

	return state, nil
}

func (c Config) getGCPServiceAccountKey(key string) (string, string, error) {
	if _, err := c.fs.Stat(key); err != nil {
		return c.writeGCPServiceAccountKey(key)
	}
	return c.readKey(key)
}

func (c Config) writeGCPServiceAccountKey(contents string) (string, string, error) {
	tempFile, err := c.fs.TempFile("", "gcpServiceAccountKey.json")
	if err != nil {
		return "", "", fmt.Errorf("Creating temp file for credentials: %s", err)
	}
	err = c.fs.WriteFile(tempFile.Name(), []byte(contents), storage.StateMode)
	if err != nil {
		return "", "", fmt.Errorf("Writing credentials to temp file: %s", err)
	}
	return tempFile.Name(), contents, nil
}

func (c Config) readKey(path string) (string, string, error) {
	keyBytes, err := c.fs.ReadFile(path)
	if err != nil {
		return "", "", fmt.Errorf("Reading key: %v", err)
	}
	return path, string(keyBytes), nil
}

func (c Config) getGCPProjectID(key string) (string, error) {
	p := struct {
		ProjectID string `json:"project_id"`
	}{}

	err := json.Unmarshal([]byte(key), &p)
	if err != nil {
		return "", fmt.Errorf("Unmarshalling service account key (must be valid json): %s", err)
	}

	if p.ProjectID == "" {
		return "", errors.New("Service account key is missing field `project_id`")
	}

	return p.ProjectID, nil
}

func ValidateIAAS(state storage.State) error {
	var err error
	switch state.IAAS {
	case "aws":
		creds := []string{state.AWS.AccessKeyID, state.AWS.SecretAccessKey, state.AWS.Region}
		err = validate("AWS", creds)

	case "azure":
		creds := []string{state.Azure.ClientID, state.Azure.ClientSecret, state.Azure.Region, state.Azure.SubscriptionID, state.Azure.TenantID}
		err = validate("Azure", creds)

	case "gcp":
		creds := []string{state.GCP.ServiceAccountKey, state.GCP.Region}
		err = validate("GCP", creds)

	case "vsphere":
		creds := []string{state.VSphere.VCenterUser, state.VSphere.VCenterPassword, state.VSphere.VCenterIP, state.VSphere.VCenterIP,
			state.VSphere.VCenterDC, state.VSphere.Cluster, state.VSphere.VCenterRP, state.VSphere.Network, state.VSphere.VCenterDS, state.VSphere.Subnet}
		err = validate("vSphere", creds)

	case "openstack":
		creds := []string{state.OpenStack.InternalCidr, state.OpenStack.ExternalIP, state.OpenStack.AuthURL, state.OpenStack.AZ, state.OpenStack.DefaultKeyName,
			state.OpenStack.DefaultSecurityGroup, state.OpenStack.NetworkID, state.OpenStack.Password, state.OpenStack.Username, state.OpenStack.Project,
			state.OpenStack.Domain, state.OpenStack.Region, state.OpenStack.PrivateKey}
		err = validate("OpenStack", creds)

	default:
		err = errors.New("--iaas [gcp, aws, azure, vsphere, openstack] must be provided or BBL_IAAS must be set")
	}

	if err != nil {
		return fmt.Errorf("\n\n%s\n", err)
	}

	return nil
}

func NeedsIAASCreds(command string) bool {
	_, ok := map[string]struct{}{
		"up":                struct{}{},
		"down":              struct{}{},
		"plan":              struct{}{},
		"destroy":           struct{}{},
		"leftovers":         struct{}{},
		"cleanup-leftovers": struct{}{},
		"rotate":            struct{}{},
	}[command]
	return ok
}

func validate(iaas string, creds []string) error {
	for _, s := range creds {
		if s == "" {
			return fmt.Errorf("There are %s credentials missing. To see all required credentials run `bbl plan --help`.", iaas)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/storage"
	flags "github.com/jessevdk/go-flags"
)

type globalFlags struct {
	Help     bool   `short:"h" long:"help"`
	Debug    bool   `short:"d" long:"debug"         env:"BBL_DEBUG"`
	Version  bool   `short:"v" long:"version"`
	StateDir string `short:"s" long:"state-dir"`
	IAAS     string `long:"iaas"                    env:"BBL_IAAS"`

	AWSAccessKeyID     string `long:"aws-access-key-id"       env:"BBL_AWS_ACCESS_KEY_ID"`
	AWSSecretAccessKey string `long:"aws-secret-access-key"   env:"BBL_AWS_SECRET_ACCESS_KEY"`
	AWSRegion          string `long:"aws-region"              env:"BBL_AWS_REGION"`

	AzureSubscriptionID string `long:"azure-subscription-id"  env:"BBL_AZURE_SUBSCRIPTION_ID"`
	AzureTenantID       string `long:"azure-tenant-id"        env:"BBL_AZURE_TENANT_ID"`
	AzureClientID       string `long:"azure-client-id"        env:"BBL_AZURE_CLIENT_ID"`
	AzureClientSecret   string `long:"azure-client-secret"    env:"BBL_AZURE_CLIENT_SECRET"`

	GCPServiceAccountKey string `long:"gcp-service-account-key" env:"BBL_GCP_SERVICE_ACCOUNT_KEY"`
	GCPProjectID         string `long:"gcp-project-id"          env:"BBL_GCP_PROJECT_ID"`
	GCPZone              string `long:"gcp-zone"                env:"BBL_GCP_ZONE"`
	GCPRegion            string `long:"gcp-region"              env:"BBL_GCP_REGION"`
}

type ParsedFlags struct {
	State         storage.State
	RemainingArgs []string
	Help          bool
	Debug         bool
	Version       bool
	StateDir      string
}

func NewConfig(getState func(string) (storage.State, error)) Config {
	return Config{
		getState: getState,
	}
}

type Config struct {
	getState func(string) (storage.State, error)
}

func (c Config) Bootstrap(args []string) (ParsedFlags, error) {
	var globalFlags globalFlags

	parser := flags.NewParser(&globalFlags, flags.IgnoreUnknown)

	remainingArgs, err := parser.ParseArgs(args[1:])
	if err != nil {
		return ParsedFlags{}, err
	}

	nonStatefulCommand := len(remainingArgs) == 0 || globalFlags.Help || globalFlags.Version
	nonStatefulCommand = nonStatefulCommand || (remainingArgs[0] == "help" || remainingArgs[0] == "version")
	if nonStatefulCommand {
		return ParsedFlags{
			RemainingArgs: remainingArgs,
			Help:          globalFlags.Help,
			Debug:         globalFlags.Debug,
			Version:       globalFlags.Version,
			StateDir:      globalFlags.StateDir,
		}, nil
	}

	stateDir := globalFlags.StateDir
	if stateDir == "" {
		stateDir, err = os.Getwd()
		if err != nil {
			// not tested
			return ParsedFlags{}, err
		}
	}

	state, err := c.getState(stateDir)
	if err != nil {
		return ParsedFlags{}, err
	}

	if globalFlags.IAAS != "" {
		if state.IAAS != "" && globalFlags.IAAS != state.IAAS {
			iaasMismatch := fmt.Sprintf("The iaas type cannot be changed for an existing environment. The current iaas type is %s.", state.IAAS)
			return ParsedFlags{}, errors.New(iaasMismatch)
		}
		state.IAAS = globalFlags.IAAS
	}

	if globalFlags.AWSAccessKeyID != "" {
		state.AWS.AccessKeyID = globalFlags.AWSAccessKeyID
	}
	if globalFlags.AWSSecretAccessKey != "" {
		state.AWS.SecretAccessKey = globalFlags.AWSSecretAccessKey
	}
	if globalFlags.AWSRegion != "" {
		if state.AWS.Region != "" && globalFlags.AWSRegion != state.AWS.Region {
			regionMismatch := fmt.Sprintf("The region cannot be changed for an existing environment. The current region is %s.", state.AWS.Region)
			return ParsedFlags{}, errors.New(regionMismatch)
		}
		state.AWS.Region = globalFlags.AWSRegion
	}

	if globalFlags.GCPServiceAccountKey != "" {
		serviceAccountKey, err := parseServiceAccountKey(globalFlags.GCPServiceAccountKey)
		if err != nil {
			return ParsedFlags{}, err
		}
		state.GCP.ServiceAccountKey = serviceAccountKey
	}
	if globalFlags.GCPProjectID != "" {
		state.GCP.ProjectID = globalFlags.GCPProjectID
	}
	if globalFlags.GCPZone != "" {
		if state.GCP.Zone != "" && globalFlags.GCPZone != state.GCP.Zone {
			zoneMismatch := fmt.Sprintf("The zone cannot be changed for an existing environment. The current zone is %s.", state.GCP.Zone)
			return ParsedFlags{}, errors.New(zoneMismatch)
		}
		state.GCP.Zone = globalFlags.GCPZone
	}
	if globalFlags.GCPRegion != "" {
		if state.GCP.Region != "" && globalFlags.GCPRegion != state.GCP.Region {
			regionMismatch := fmt.Sprintf("The region cannot be changed for an existing environment. The current region is %s.", state.GCP.Region)
			return ParsedFlags{}, errors.New(regionMismatch)
		}
		state.GCP.Region = globalFlags.GCPRegion
	}
	if globalFlags.AzureSubscriptionID != "" {
		state.Azure.SubscriptionID = globalFlags.AzureSubscriptionID
	}
	if globalFlags.AzureTenantID != "" {
		state.Azure.TenantID = globalFlags.AzureTenantID
	}
	if globalFlags.AzureClientID != "" {
		state.Azure.ClientID = globalFlags.AzureClientID
	}
	if globalFlags.AzureClientSecret != "" {
		state.Azure.ClientSecret = globalFlags.AzureClientSecret
	}

	return ParsedFlags{State: state, RemainingArgs: remainingArgs, Help: globalFlags.Help, Debug: globalFlags.Debug, Version: globalFlags.Version, StateDir: globalFlags.StateDir}, nil
}

func ValidateIAAS(state storage.State, command string) error {
	if state.IAAS == "" || (state.IAAS != "gcp" && state.IAAS != "aws" && state.IAAS != "azure") {
		return errors.New("--iaas [gcp, aws, azure] must be provided or BBL_IAAS must be set")
	}
	if state.IAAS == "aws" {
		err := validateAWS(state.AWS)
		if err != nil {
			return err
		}
	}
	if state.IAAS == "gcp" {
		err := validateGCP(state.GCP)
		if err != nil {
			return err
		}
	}
	if state.IAAS == "azure" {
		err := validateAzure(state.Azure)
		if err != nil {
			return err
		}
	}
	return nil
}

func NeedsIAASConfig(command string) bool {
	_, ok := map[string]struct{}{
		"up":         struct{}{},
		"down":       struct{}{},
		"destroy":    struct{}{},
		"create-lbs": struct{}{},
		"delete-lbs": struct{}{},
		"update-lbs": struct{}{},
	}[command]
	return ok
}

func validateAWS(aws storage.AWS) error {
	if aws.AccessKeyID == "" {
		return errors.New("AWS access key ID must be provided")
	}
	if aws.SecretAccessKey == "" {
		return errors.New("AWS secret access key must be provided")
	}
	if aws.Region == "" {
		return errors.New("AWS region must be provided")
	}
	return nil
}

func validateGCP(gcp storage.GCP) error {
	if gcp.ServiceAccountKey == "" {
		return errors.New("GCP service account key must be provided")
	}
	if gcp.ProjectID == "" {
		return errors.New("GCP project ID must be provided")
	}
	if gcp.Zone == "" {
		return errors.New("GCP zone must be provided")
	}
	if gcp.Region == "" {
		return errors.New("GCP region must be provided")
	}
	return nil
}

func validateAzure(azure storage.Azure) error {
	if azure.SubscriptionID == "" {
		return errors.New("Azure subscription id must be provided")
	}
	if azure.TenantID == "" {
		return errors.New("Azure tenant id must be provided")
	}
	if azure.ClientID == "" {
		return errors.New("Azure client id must be provided")
	}
	if azure.ClientSecret == "" {
		return errors.New("Azure client secret must be provided")
	}
	return nil
}

func parseServiceAccountKey(serviceAccountKey string) (string, error) {
	var key string

	if _, err := os.Stat(serviceAccountKey); err != nil {
		key = serviceAccountKey
	} else {
		rawServiceAccountKey, err := ioutil.ReadFile(serviceAccountKey)
		if err != nil {
			return "", fmt.Errorf("error reading service account key from file: %v", err)
		}

		key = string(rawServiceAccountKey)
	}

	var tmp interface{}
	err := json.Unmarshal([]byte(key), &tmp)
	if err != nil {
		return "", fmt.Errorf("error unmarshalling service account key (must be valid json): %v", err)
	}

	return key, err
}
<commit_msg>bbl rotate requires IAAS keys.<commit_after>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/storage"
	flags "github.com/jessevdk/go-flags"
)

type globalFlags struct {
	Help     bool   `short:"h" long:"help"`
	Debug    bool   `short:"d" long:"debug"         env:"BBL_DEBUG"`
	Version  bool   `short:"v" long:"version"`
	StateDir string `short:"s" long:"state-dir"`
	IAAS     string `long:"iaas"                    env:"BBL_IAAS"`

	AWSAccessKeyID     string `long:"aws-access-key-id"       env:"BBL_AWS_ACCESS_KEY_ID"`
	AWSSecretAccessKey string `long:"aws-secret-access-key"   env:"BBL_AWS_SECRET_ACCESS_KEY"`
	AWSRegion          string `long:"aws-region"              env:"BBL_AWS_REGION"`

	AzureSubscriptionID string `long:"azure-subscription-id"  env:"BBL_AZURE_SUBSCRIPTION_ID"`
	AzureTenantID       string `long:"azure-tenant-id"        env:"BBL_AZURE_TENANT_ID"`
	AzureClientID       string `long:"azure-client-id"        env:"BBL_AZURE_CLIENT_ID"`
	AzureClientSecret   string `long:"azure-client-secret"    env:"BBL_AZURE_CLIENT_SECRET"`

	GCPServiceAccountKey string `long:"gcp-service-account-key" env:"BBL_GCP_SERVICE_ACCOUNT_KEY"`
	GCPProjectID         string `long:"gcp-project-id"          env:"BBL_GCP_PROJECT_ID"`
	GCPZone              string `long:"gcp-zone"                env:"BBL_GCP_ZONE"`
	GCPRegion            string `long:"gcp-region"              env:"BBL_GCP_REGION"`
}

type ParsedFlags struct {
	State         storage.State
	RemainingArgs []string
	Help          bool
	Debug         bool
	Version       bool
	StateDir      string
}

func NewConfig(getState func(string) (storage.State, error)) Config {
	return Config{
		getState: getState,
	}
}

type Config struct {
	getState func(string) (storage.State, error)
}

func (c Config) Bootstrap(args []string) (ParsedFlags, error) {
	var globalFlags globalFlags

	parser := flags.NewParser(&globalFlags, flags.IgnoreUnknown)

	remainingArgs, err := parser.ParseArgs(args[1:])
	if err != nil {
		return ParsedFlags{}, err
	}

	nonStatefulCommand := len(remainingArgs) == 0 || globalFlags.Help || globalFlags.Version
	nonStatefulCommand = nonStatefulCommand || (remainingArgs[0] == "help" || remainingArgs[0] == "version")
	if nonStatefulCommand {
		return ParsedFlags{
			RemainingArgs: remainingArgs,
			Help:          globalFlags.Help,
			Debug:         globalFlags.Debug,
			Version:       globalFlags.Version,
			StateDir:      globalFlags.StateDir,
		}, nil
	}

	stateDir := globalFlags.StateDir
	if stateDir == "" {
		stateDir, err = os.Getwd()
		if err != nil {
			// not tested
			return ParsedFlags{}, err
		}
	}

	state, err := c.getState(stateDir)
	if err != nil {
		return ParsedFlags{}, err
	}

	if globalFlags.IAAS != "" {
		if state.IAAS != "" && globalFlags.IAAS != state.IAAS {
			iaasMismatch := fmt.Sprintf("The iaas type cannot be changed for an existing environment. The current iaas type is %s.", state.IAAS)
			return ParsedFlags{}, errors.New(iaasMismatch)
		}
		state.IAAS = globalFlags.IAAS
	}

	if globalFlags.AWSAccessKeyID != "" {
		state.AWS.AccessKeyID = globalFlags.AWSAccessKeyID
	}
	if globalFlags.AWSSecretAccessKey != "" {
		state.AWS.SecretAccessKey = globalFlags.AWSSecretAccessKey
	}
	if globalFlags.AWSRegion != "" {
		if state.AWS.Region != "" && globalFlags.AWSRegion != state.AWS.Region {
			regionMismatch := fmt.Sprintf("The region cannot be changed for an existing environment. The current region is %s.", state.AWS.Region)
			return ParsedFlags{}, errors.New(regionMismatch)
		}
		state.AWS.Region = globalFlags.AWSRegion
	}

	if globalFlags.GCPServiceAccountKey != "" {
		serviceAccountKey, err := parseServiceAccountKey(globalFlags.GCPServiceAccountKey)
		if err != nil {
			return ParsedFlags{}, err
		}
		state.GCP.ServiceAccountKey = serviceAccountKey
	}
	if globalFlags.GCPProjectID != "" {
		state.GCP.ProjectID = globalFlags.GCPProjectID
	}
	if globalFlags.GCPZone != "" {
		if state.GCP.Zone != "" && globalFlags.GCPZone != state.GCP.Zone {
			zoneMismatch := fmt.Sprintf("The zone cannot be changed for an existing environment. The current zone is %s.", state.GCP.Zone)
			return ParsedFlags{}, errors.New(zoneMismatch)
		}
		state.GCP.Zone = globalFlags.GCPZone
	}
	if globalFlags.GCPRegion != "" {
		if state.GCP.Region != "" && globalFlags.GCPRegion != state.GCP.Region {
			regionMismatch := fmt.Sprintf("The region cannot be changed for an existing environment. The current region is %s.", state.GCP.Region)
			return ParsedFlags{}, errors.New(regionMismatch)
		}
		state.GCP.Region = globalFlags.GCPRegion
	}
	if globalFlags.AzureSubscriptionID != "" {
		state.Azure.SubscriptionID = globalFlags.AzureSubscriptionID
	}
	if globalFlags.AzureTenantID != "" {
		state.Azure.TenantID = globalFlags.AzureTenantID
	}
	if globalFlags.AzureClientID != "" {
		state.Azure.ClientID = globalFlags.AzureClientID
	}
	if globalFlags.AzureClientSecret != "" {
		state.Azure.ClientSecret = globalFlags.AzureClientSecret
	}

	return ParsedFlags{State: state, RemainingArgs: remainingArgs, Help: globalFlags.Help, Debug: globalFlags.Debug, Version: globalFlags.Version, StateDir: globalFlags.StateDir}, nil
}

func ValidateIAAS(state storage.State, command string) error {
	if state.IAAS == "" || (state.IAAS != "gcp" && state.IAAS != "aws" && state.IAAS != "azure") {
		return errors.New("--iaas [gcp, aws, azure] must be provided or BBL_IAAS must be set")
	}
	if state.IAAS == "aws" {
		err := validateAWS(state.AWS)
		if err != nil {
			return err
		}
	}
	if state.IAAS == "gcp" {
		err := validateGCP(state.GCP)
		if err != nil {
			return err
		}
	}
	if state.IAAS == "azure" {
		err := validateAzure(state.Azure)
		if err != nil {
			return err
		}
	}
	return nil
}

func NeedsIAASConfig(command string) bool {
	_, ok := map[string]struct{}{
		"up":         struct{}{},
		"down":       struct{}{},
		"destroy":    struct{}{},
		"create-lbs": struct{}{},
		"delete-lbs": struct{}{},
		"update-lbs": struct{}{},
		"rotate":     struct{}{},
	}[command]
	return ok
}

func validateAWS(aws storage.AWS) error {
	if aws.AccessKeyID == "" {
		return errors.New("AWS access key ID must be provided")
	}
	if aws.SecretAccessKey == "" {
		return errors.New("AWS secret access key must be provided")
	}
	if aws.Region == "" {
		return errors.New("AWS region must be provided")
	}
	return nil
}

func validateGCP(gcp storage.GCP) error {
	if gcp.ServiceAccountKey == "" {
		return errors.New("GCP service account key must be provided")
	}
	if gcp.ProjectID == "" {
		return errors.New("GCP project ID must be provided")
	}
	if gcp.Zone == "" {
		return errors.New("GCP zone must be provided")
	}
	if gcp.Region == "" {
		return errors.New("GCP region must be provided")
	}
	return nil
}

func validateAzure(azure storage.Azure) error {
	if azure.SubscriptionID == "" {
		return errors.New("Azure subscription id must be provided")
	}
	if azure.TenantID == "" {
		return errors.New("Azure tenant id must be provided")
	}
	if azure.ClientID == "" {
		return errors.New("Azure client id must be provided")
	}
	if azure.ClientSecret == "" {
		return errors.New("Azure client secret must be provided")
	}
	return nil
}

func parseServiceAccountKey(serviceAccountKey string) (string, error) {
	var key string

	if _, err := os.Stat(serviceAccountKey); err != nil {
		key = serviceAccountKey
	} else {
		rawServiceAccountKey, err := ioutil.ReadFile(serviceAccountKey)
		if err != nil {
			return "", fmt.Errorf("error reading service account key from file: %v", err)
		}

		key = string(rawServiceAccountKey)
	}

	var tmp interface{}
	err := json.Unmarshal([]byte(key), &tmp)
	if err != nil {
		return "", fmt.Errorf("error unmarshalling service account key (must be valid json): %v", err)
	}

	return key, err
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-qemu 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 hypervisor provides management facilities for one or more QEMU
// virtual machines on a hypervisor.
package hypervisor

import (
	"fmt"
	"sync"

	"github.com/digitalocean/go-qemu"
	"github.com/digitalocean/go-qemu/qmp"
)

// A Hypervisor enables access to all virtual machines on a QEMU hypervisor.
type Hypervisor struct {
	// Currently connected monitor sockets
	mu        sync.Mutex
	connected []qmp.Monitor

	// Driver used to communicate with domains
	driver Driver
}

// New creates a new Hypervisor using the input Driver.
func New(driver Driver) *Hypervisor {
	return &Hypervisor{
		connected: make([]qmp.Monitor, 0),
		driver:    driver,
	}
}

// A Driver is a QEMU QMP monitor driver that a Hypervisor can use to perform
// actions on groups of virtual machines.
type Driver interface {
	NewMonitor(domain string) (qmp.Monitor, error)
	DomainNames() ([]string, error)
}

// Domains retrieves all virtual machines which reside on a given hypervisor,
// connecting to the monitor socket of each machine.
//
// Each Domain's Close method must be called to clean up its resources when
// they are no longer needed.
//
// The Disconnect method must be called to clean up monitor socket connections
// once the virtual machines are no longer needed.
func (hv *Hypervisor) Domains() ([]*qemu.Domain, error) {
	domains, err := hv.driver.DomainNames()
	if err != nil {
		return nil, err
	}

	ds := make([]*qemu.Domain, 0, len(domains))
	for _, d := range domains {
		domain, err := hv.connectDomain(d)
		if err != nil {
			return nil, err
		}

		ds = append(ds, domain)
	}

	return ds, nil
}

// Domain retrieves a single virtual machine from a hypervisor, connecting to
// its monitor socket.  If no virtual machine exists with the given name, an
// error is returned.
//
// The Domain's Close method must be called to clean up its resources when
// they are no longer needed.
//
// The Disconnect method must be called to clean up the monitor socket
// connection once the virtual machine is no longer needed.
func (hv *Hypervisor) Domain(name string) (*qemu.Domain, error) {
	domains, err := hv.driver.DomainNames()
	if err != nil {
		return nil, err
	}

	for _, d := range domains {
		if d == name {
			return hv.connectDomain(name)
		}
	}

	return nil, fmt.Errorf("domain %q not found", name)
}

// DomainNames retrieves the names of all virtual machines which reside on
// a given hypervisor, so that individual connections can be opened using
// the Domain method, as needed.
func (hv *Hypervisor) DomainNames() ([]string, error) {
	return hv.driver.DomainNames()
}

// Disconnect cleans up monitor socket connections for all virtual
// machines.
func (hv *Hypervisor) Disconnect() error {
	hv.mu.Lock()
	defer hv.mu.Unlock()

	for _, mon := range hv.connected {
		if err := mon.Disconnect(); err != nil {
			return err
		}
	}

	hv.connected = make([]qmp.Monitor, 0)
	return nil
}

// connectDomain opens a monitor socket connection and creates a virtual
// machine for the machine with the specified name.
func (hv *Hypervisor) connectDomain(name string) (*qemu.Domain, error) {
	mon, err := hv.driver.NewMonitor(name)
	if err != nil {
		return nil, err
	}

	dom, err := qemu.NewDomain(mon, name)
	if err != nil {
		return nil, err
	}

	hv.mu.Lock()
	hv.connected = append(hv.connected, mon)
	hv.mu.Unlock()

	return dom, nil
}
<commit_msg>hypervisor: call qmp.Monitor.Connect where needed<commit_after>// Copyright 2016 The go-qemu 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 hypervisor provides management facilities for one or more QEMU
// virtual machines on a hypervisor.
package hypervisor

import (
	"fmt"
	"sync"

	"github.com/digitalocean/go-qemu"
	"github.com/digitalocean/go-qemu/qmp"
)

// A Hypervisor enables access to all virtual machines on a QEMU hypervisor.
type Hypervisor struct {
	// Currently connected monitor sockets
	mu        sync.Mutex
	connected []qmp.Monitor

	// Driver used to communicate with domains
	driver Driver
}

// New creates a new Hypervisor using the input Driver.
func New(driver Driver) *Hypervisor {
	return &Hypervisor{
		connected: make([]qmp.Monitor, 0),
		driver:    driver,
	}
}

// A Driver is a QEMU QMP monitor driver that a Hypervisor can use to perform
// actions on groups of virtual machines.
type Driver interface {
	NewMonitor(domain string) (qmp.Monitor, error)
	DomainNames() ([]string, error)
}

// Domains retrieves all virtual machines which reside on a given hypervisor,
// connecting to the monitor socket of each machine.
//
// Each Domain's Close method must be called to clean up its resources when
// they are no longer needed.
//
// The Disconnect method must be called to clean up monitor socket connections
// once the virtual machines are no longer needed.
func (hv *Hypervisor) Domains() ([]*qemu.Domain, error) {
	domains, err := hv.driver.DomainNames()
	if err != nil {
		return nil, err
	}

	ds := make([]*qemu.Domain, 0, len(domains))
	for _, d := range domains {
		domain, err := hv.connectDomain(d)
		if err != nil {
			return nil, err
		}

		ds = append(ds, domain)
	}

	return ds, nil
}

// Domain retrieves a single virtual machine from a hypervisor, connecting to
// its monitor socket.  If no virtual machine exists with the given name, an
// error is returned.
//
// The Domain's Close method must be called to clean up its resources when
// they are no longer needed.
//
// The Disconnect method must be called to clean up the monitor socket
// connection once the virtual machine is no longer needed.
func (hv *Hypervisor) Domain(name string) (*qemu.Domain, error) {
	domains, err := hv.driver.DomainNames()
	if err != nil {
		return nil, err
	}

	for _, d := range domains {
		if d == name {
			return hv.connectDomain(name)
		}
	}

	return nil, fmt.Errorf("domain %q not found", name)
}

// DomainNames retrieves the names of all virtual machines which reside on
// a given hypervisor, so that individual connections can be opened using
// the Domain method, as needed.
func (hv *Hypervisor) DomainNames() ([]string, error) {
	return hv.driver.DomainNames()
}

// Disconnect cleans up monitor socket connections for all virtual
// machines.
func (hv *Hypervisor) Disconnect() error {
	hv.mu.Lock()
	defer hv.mu.Unlock()

	for _, mon := range hv.connected {
		if err := mon.Disconnect(); err != nil {
			return err
		}
	}

	hv.connected = make([]qmp.Monitor, 0)
	return nil
}

// connectDomain opens a monitor socket connection and creates a virtual
// machine for the machine with the specified name.
func (hv *Hypervisor) connectDomain(name string) (*qemu.Domain, error) {
	mon, err := hv.driver.NewMonitor(name)
	if err != nil {
		return nil, err
	}

	if err := mon.Connect(); err != nil {
		return nil, err
	}

	dom, err := qemu.NewDomain(mon, name)
	if err != nil {
		return nil, err
	}

	hv.mu.Lock()
	hv.connected = append(hv.connected, mon)
	hv.mu.Unlock()

	return dom, nil
}
<|endoftext|>


<commit_before>package storage

import (
	"net"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/chihaya/chihaya/bittorrent"
)

// PeerEqualityFunc is the boolean function to use to check two Peers for equality.
var PeerEqualityFunc = func(p1, p2 bittorrent.Peer) bool { return p1.Equal(p2) }

// TestPeerStore tests a PeerStore implementation against the interface.
func TestPeerStore(t *testing.T, p PeerStore) {
	testData := []struct {
		ih   bittorrent.InfoHash
		peer bittorrent.Peer
	}{
		{
			bittorrent.InfoHashFromString("00000000000000000001"),
			bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000001"), Port: 1, IP: bittorrent.IP{IP: net.ParseIP("1.1.1.1").To4(), AddressFamily: bittorrent.IPv4}},
		},
		{
			bittorrent.InfoHashFromString("00000000000000000002"),
			bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000002"), Port: 2, IP: bittorrent.IP{IP: net.ParseIP("abab::0001"), AddressFamily: bittorrent.IPv6}},
		},
	}

	v4Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999994"), IP: bittorrent.IP{IP: net.ParseIP("99.99.99.99").To4(), AddressFamily: bittorrent.IPv4}, Port: 9994}
	v6Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999996"), IP: bittorrent.IP{IP: net.ParseIP("fc00::0001"), AddressFamily: bittorrent.IPv6}, Port: 9996}

	for _, c := range testData {
		peer := v4Peer
		if c.peer.IP.AddressFamily == bittorrent.IPv6 {
			peer = v6Peer
		}

		// Test ErrDNE for non-existent swarms.
		err := p.DeleteLeecher(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		_, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Test empty scrape response for non-existent swarms.
		scrape := p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(0), scrape.Complete)
		require.Equal(t, uint32(0), scrape.Incomplete)
		require.Equal(t, uint32(0), scrape.Snatches)

		// Insert dummy Peer to keep swarm active
		// Has the same address family as c.peer
		err = p.PutLeecher(c.ih, peer)
		require.Nil(t, err)

		// Test ErrDNE for non-existent seeder.
		err = p.DeleteSeeder(c.ih, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Test PutLeecher -> Announce -> DeleteLeecher -> Announce

		err = p.PutLeecher(c.ih, c.peer)
		require.Nil(t, err)

		peers, err := p.AnnouncePeers(c.ih, true, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// non-seeder announce should still return the leecher
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		scrape = p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(2), scrape.Incomplete)
		require.Equal(t, uint32(0), scrape.Complete)

		err = p.DeleteLeecher(c.ih, c.peer)
		require.Nil(t, err)

		peers, err = p.AnnouncePeers(c.ih, true, 50, peer)
		require.Nil(t, err)
		require.False(t, containsPeer(peers, c.peer))

		// Test PutSeeder -> Announce -> DeleteSeeder -> Announce

		err = p.PutSeeder(c.ih, c.peer)
		require.Nil(t, err)

		// Should be leecher to see the seeder
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		scrape = p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(1), scrape.Incomplete)
		require.Equal(t, uint32(1), scrape.Complete)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Nil(t, err)

		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.False(t, containsPeer(peers, c.peer))

		// Test PutLeecher -> Graduate -> Announce -> DeleteLeecher -> Announce

		err = p.PutLeecher(c.ih, c.peer)
		require.Nil(t, err)

		err = p.GraduateLeecher(c.ih, c.peer)
		require.Nil(t, err)

		// Has to be leecher to see the graduated seeder
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// Deleting the Peer as a Leecher should have no effect
		err = p.DeleteLeecher(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Verify it's still there
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// Clean up

		err = p.DeleteLeecher(c.ih, peer)
		require.Nil(t, err)

		// Test ErrDNE for missing leecher
		err = p.DeleteLeecher(c.ih, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Nil(t, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)
	}

}

func containsPeer(peers []bittorrent.Peer, p bittorrent.Peer) bool {
	for _, peer := range peers {
		if PeerEqualityFunc(peer, p) {
			return true
		}
	}
	return false
}
<commit_msg>storage: stop peer store after tests<commit_after>package storage

import (
	"net"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/chihaya/chihaya/bittorrent"
)

// PeerEqualityFunc is the boolean function to use to check two Peers for equality.
var PeerEqualityFunc = func(p1, p2 bittorrent.Peer) bool { return p1.Equal(p2) }

// TestPeerStore tests a PeerStore implementation against the interface.
func TestPeerStore(t *testing.T, p PeerStore) {
	testData := []struct {
		ih   bittorrent.InfoHash
		peer bittorrent.Peer
	}{
		{
			bittorrent.InfoHashFromString("00000000000000000001"),
			bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000001"), Port: 1, IP: bittorrent.IP{IP: net.ParseIP("1.1.1.1").To4(), AddressFamily: bittorrent.IPv4}},
		},
		{
			bittorrent.InfoHashFromString("00000000000000000002"),
			bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000002"), Port: 2, IP: bittorrent.IP{IP: net.ParseIP("abab::0001"), AddressFamily: bittorrent.IPv6}},
		},
	}

	v4Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999994"), IP: bittorrent.IP{IP: net.ParseIP("99.99.99.99").To4(), AddressFamily: bittorrent.IPv4}, Port: 9994}
	v6Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999996"), IP: bittorrent.IP{IP: net.ParseIP("fc00::0001"), AddressFamily: bittorrent.IPv6}, Port: 9996}

	for _, c := range testData {
		peer := v4Peer
		if c.peer.IP.AddressFamily == bittorrent.IPv6 {
			peer = v6Peer
		}

		// Test ErrDNE for non-existent swarms.
		err := p.DeleteLeecher(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		_, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Test empty scrape response for non-existent swarms.
		scrape := p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(0), scrape.Complete)
		require.Equal(t, uint32(0), scrape.Incomplete)
		require.Equal(t, uint32(0), scrape.Snatches)

		// Insert dummy Peer to keep swarm active
		// Has the same address family as c.peer
		err = p.PutLeecher(c.ih, peer)
		require.Nil(t, err)

		// Test ErrDNE for non-existent seeder.
		err = p.DeleteSeeder(c.ih, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Test PutLeecher -> Announce -> DeleteLeecher -> Announce

		err = p.PutLeecher(c.ih, c.peer)
		require.Nil(t, err)

		peers, err := p.AnnouncePeers(c.ih, true, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// non-seeder announce should still return the leecher
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		scrape = p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(2), scrape.Incomplete)
		require.Equal(t, uint32(0), scrape.Complete)

		err = p.DeleteLeecher(c.ih, c.peer)
		require.Nil(t, err)

		peers, err = p.AnnouncePeers(c.ih, true, 50, peer)
		require.Nil(t, err)
		require.False(t, containsPeer(peers, c.peer))

		// Test PutSeeder -> Announce -> DeleteSeeder -> Announce

		err = p.PutSeeder(c.ih, c.peer)
		require.Nil(t, err)

		// Should be leecher to see the seeder
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		scrape = p.ScrapeSwarm(c.ih, c.peer.IP.AddressFamily)
		require.Equal(t, uint32(1), scrape.Incomplete)
		require.Equal(t, uint32(1), scrape.Complete)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Nil(t, err)

		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.False(t, containsPeer(peers, c.peer))

		// Test PutLeecher -> Graduate -> Announce -> DeleteLeecher -> Announce

		err = p.PutLeecher(c.ih, c.peer)
		require.Nil(t, err)

		err = p.GraduateLeecher(c.ih, c.peer)
		require.Nil(t, err)

		// Has to be leecher to see the graduated seeder
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// Deleting the Peer as a Leecher should have no effect
		err = p.DeleteLeecher(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		// Verify it's still there
		peers, err = p.AnnouncePeers(c.ih, false, 50, peer)
		require.Nil(t, err)
		require.True(t, containsPeer(peers, c.peer))

		// Clean up

		err = p.DeleteLeecher(c.ih, peer)
		require.Nil(t, err)

		// Test ErrDNE for missing leecher
		err = p.DeleteLeecher(c.ih, peer)
		require.Equal(t, ErrResourceDoesNotExist, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Nil(t, err)

		err = p.DeleteSeeder(c.ih, c.peer)
		require.Equal(t, ErrResourceDoesNotExist, err)
	}

	e := p.Stop()
	require.Nil(t, <-e)
}

func containsPeer(peers []bittorrent.Peer, p bittorrent.Peer) bool {
	for _, peer := range peers {
		if PeerEqualityFunc(peer, p) {
			return true
		}
	}
	return false
}
<|endoftext|>