<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 vsphere

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/url"
	"os/exec"
	"strings"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
	"gopkg.in/gcfg.v1"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

const ProviderName = "vsphere"
const ActivePowerState = "poweredOn"

// VSphere is an implementation of cloud provider Interface for VSphere.
type VSphere struct {
	cfg *VSphereConfig
	// InstanceID of the server where this VSphere object is instantiated.
	localInstanceID string
}

type VSphereConfig struct {
	Global struct {
		User         string `gcfg:"user"`
		Password     string `gcfg:"password"`
		VCenterIP    string `gcfg:"server"`
		VCenterPort  string `gcfg:"port"`
		InsecureFlag bool   `gcfg:"insecure-flag"`
		Datacenter   string `gcfg:"datacenter"`
		Datastore    string `gcfg:"datastore"`
	}

	Network struct {
		PublicNetwork string `gcfg:"public-network"`
	}
}

func readConfig(config io.Reader) (VSphereConfig, error) {
	if config == nil {
		err := fmt.Errorf("no vSphere cloud provider config file given")
		return VSphereConfig{}, err
	}

	var cfg VSphereConfig
	err := gcfg.ReadInto(&cfg, config)
	return cfg, err
}

func init() {
	cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
		cfg, err := readConfig(config)
		if err != nil {
			return nil, err
		}
		return newVSphere(cfg)
	})
}

func readInstanceID(cfg *VSphereConfig) (string, error) {
	cmd := exec.Command("bash", "-c", `dmidecode -t 1 | grep UUID | tr -d ' ' | cut -f 2 -d ':'`)
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}

	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	// Create a new finder
	f := find.NewFinder(c.Client, true)

	// Fetch and set data center
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return "", err
	}
	f.SetDatacenter(dc)

	s := object.NewSearchIndex(c.Client)

	svm, err := s.FindByUuid(ctx, dc, strings.ToLower(strings.TrimSpace(out.String())), true, nil)
	var vm mo.VirtualMachine
	err = s.Properties(ctx, svm.Reference(), []string{"name"}, &vm)
	if err != nil {
		return "", err
	}
	return vm.Name, nil
}

func newVSphere(cfg VSphereConfig) (*VSphere, error) {
	id, err := readInstanceID(&cfg)
	if err != nil {
		return nil, err
	}

	vs := VSphere{
		cfg:             &cfg,
		localInstanceID: id,
	}
	return &vs, nil
}

func vsphereLogin(cfg *VSphereConfig, ctx context.Context) (*govmomi.Client, error) {

	// Parse URL from string
	u, err := url.Parse(fmt.Sprintf("https://%s:%s/sdk", cfg.Global.VCenterIP, cfg.Global.VCenterPort))
	if err != nil {
		return nil, err
	}
	// set username and password for the URL
	u.User = url.UserPassword(cfg.Global.User, cfg.Global.Password)

	// Connect and log in to ESX or vCenter
	c, err := govmomi.NewClient(ctx, u, cfg.Global.InsecureFlag)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func getVirtualMachineByName(cfg *VSphereConfig, ctx context.Context, c *govmomi.Client, name string) (*object.VirtualMachine, error) {
	// Create a new finder
	f := find.NewFinder(c.Client, true)

	// Fetch and set data center
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return nil, err
	}
	f.SetDatacenter(dc)

	// Retrieve vm by name
	//TODO: also look for vm inside subfolders
	vm, err := f.VirtualMachine(ctx, name)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

func getVirtualMachineManagedObjectReference(ctx context.Context, c *govmomi.Client, vm *object.VirtualMachine, field string, dst interface{}) error {
	collector := property.DefaultCollector(c.Client)

	// Retrieve required field from VM object
	err := collector.RetrieveOne(ctx, vm.Reference(), []string{field}, dst)
	if err != nil {
		return err
	}
	return nil
}

func getInstances(cfg *VSphereConfig, ctx context.Context, c *govmomi.Client, filter string) ([]string, error) {
	f := find.NewFinder(c.Client, true)
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return nil, err
	}

	f.SetDatacenter(dc)

	//TODO: get all vms inside subfolders
	vms, err := f.VirtualMachineList(ctx, filter)
	if err != nil {
		return nil, err
	}

	var vmRef []types.ManagedObjectReference
	for _, vm := range vms {
		vmRef = append(vmRef, vm.Reference())
	}

	pc := property.DefaultCollector(c.Client)

	var vmt []mo.VirtualMachine
	err = pc.Retrieve(ctx, vmRef, []string{"name", "summary"}, &vmt)
	if err != nil {
		return nil, err
	}

	var vmList []string
	for _, vm := range vmt {
		if vm.Summary.Runtime.PowerState == ActivePowerState {
			vmList = append(vmList, vm.Name)
		} else if vm.Summary.Config.Template == false {
			glog.Warningf("VM %s, is not in %s state", vm.Name, ActivePowerState)
		}
	}
	return vmList, nil
}

type Instances struct {
	cfg             *VSphereConfig
	localInstanceID string
}

// Instances returns an implementation of Instances for vSphere.
func (vs *VSphere) Instances() (cloudprovider.Instances, bool) {
	return &Instances{vs.cfg, vs.localInstanceID}, true
}

// List is an implementation of Instances.List.
func (i *Instances) List(filter string) ([]string, error) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return nil, err
	}
	defer c.Logout(ctx)

	vmList, err := getInstances(i.cfg, ctx, c, filter)
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %s instances matching %s: %s",
		len(vmList), filter, vmList)

	return vmList, nil
}

// NodeAddresses is an implementation of Instances.NodeAddresses.
func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) {
	addrs := []api.NodeAddress{}

	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return nil, err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)
	if err != nil {
		return nil, err
	}

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "guest.net", &mvm)
	if err != nil {
		return nil, err
	}

	// retrieve VM's ip(s)
	for _, v := range mvm.Guest.Net {
		var addressType api.NodeAddressType
		if i.cfg.Network.PublicNetwork == v.Network {
			addressType = api.NodeExternalIP
		} else {
			addressType = api.NodeInternalIP
		}
		for _, ip := range v.IpAddress {
			api.AddToNodeAddresses(&addrs,
				api.NodeAddress{
					Type:    addressType,
					Address: ip,
				},
			)
		}
	}
	return addrs, nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return errors.New("unimplemented")
}

func (i *Instances) CurrentNodeName(hostname string) (string, error) {
	return i.localInstanceID, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name string) (string, error) {
	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)
	if err != nil {
		return "", err
	}

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "summary", &mvm)
	if err != nil {
		return "", err
	}

	if mvm.Summary.Runtime.PowerState == ActivePowerState {
		return vm.InventoryPath, nil
	}

	if mvm.Summary.Config.Template == false {
		glog.Warningf("VM %s, is not in %s state", name, ActivePowerState)
	} else {
		glog.Warningf("VM %s, is a template", name)
	}

	return "", cloudprovider.InstanceNotFound
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name string) (string, error) {
	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "summary", &mvm)
	if err != nil {
		return "", err
	}

	if mvm.Summary.Runtime.PowerState == ActivePowerState {
		return "/" + vm.InventoryPath, nil
	}

	if mvm.Summary.Config.Template == false {
		glog.Warning("VM %s, is not in %s state", name, ActivePowerState)
	} else {
		glog.Warning("VM %s, is a template", name)
	}

	return "", cloudprovider.InstanceNotFound
}

func (i *Instances) InstanceType(name string) (string, error) {
	return "", nil
}

func (vs *VSphere) Clusters() (cloudprovider.Clusters, bool) {
	return nil, true
}

// ProviderName returns the cloud provider ID.
func (vs *VSphere) ProviderName() string {
	return ProviderName
}

// LoadBalancer returns an implementation of LoadBalancer for vSphere.
func (vs *VSphere) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
	return nil, false
}

// Zones returns an implementation of Zones for Google vSphere.
func (vs *VSphere) Zones() (cloudprovider.Zones, bool) {
	glog.V(4).Info("Claiming to support Zones")

	return vs, true
}

func (vs *VSphere) GetZone() (cloudprovider.Zone, error) {
	glog.V(4).Infof("Current zone is %v", vs.cfg.Global.Datacenter)

	return cloudprovider.Zone{Region: vs.cfg.Global.Datacenter}, nil
}

// Routes returns an implementation of Routes for vSphere.
func (vs *VSphere) Routes() (cloudprovider.Routes, bool) {
	return nil, true
}

// ScrubDNS filters DNS settings for pods.
func (vs *VSphere) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
	return nameservers, searches
}
<commit_msg>Adding error check when read instance id failed<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 vsphere

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/url"
	"os/exec"
	"strings"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
	"gopkg.in/gcfg.v1"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

const ProviderName = "vsphere"
const ActivePowerState = "poweredOn"

// VSphere is an implementation of cloud provider Interface for VSphere.
type VSphere struct {
	cfg *VSphereConfig
	// InstanceID of the server where this VSphere object is instantiated.
	localInstanceID string
}

type VSphereConfig struct {
	Global struct {
		User         string `gcfg:"user"`
		Password     string `gcfg:"password"`
		VCenterIP    string `gcfg:"server"`
		VCenterPort  string `gcfg:"port"`
		InsecureFlag bool   `gcfg:"insecure-flag"`
		Datacenter   string `gcfg:"datacenter"`
		Datastore    string `gcfg:"datastore"`
	}

	Network struct {
		PublicNetwork string `gcfg:"public-network"`
	}
}

func readConfig(config io.Reader) (VSphereConfig, error) {
	if config == nil {
		err := fmt.Errorf("no vSphere cloud provider config file given")
		return VSphereConfig{}, err
	}

	var cfg VSphereConfig
	err := gcfg.ReadInto(&cfg, config)
	return cfg, err
}

func init() {
	cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
		cfg, err := readConfig(config)
		if err != nil {
			return nil, err
		}
		return newVSphere(cfg)
	})
}

func readInstanceID(cfg *VSphereConfig) (string, error) {
	cmd := exec.Command("bash", "-c", `dmidecode -t 1 | grep UUID | tr -d ' ' | cut -f 2 -d ':'`)
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}
	if out.Len() == 0 {
		return "", fmt.Errorf("unable to retrieve Instance ID")
	}

	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	// Create a new finder
	f := find.NewFinder(c.Client, true)

	// Fetch and set data center
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return "", err
	}
	f.SetDatacenter(dc)

	s := object.NewSearchIndex(c.Client)

	svm, err := s.FindByUuid(ctx, dc, strings.ToLower(strings.TrimSpace(out.String())), true, nil)
	var vm mo.VirtualMachine
	err = s.Properties(ctx, svm.Reference(), []string{"name"}, &vm)
	if err != nil {
		return "", err
	}
	return vm.Name, nil
}

func newVSphere(cfg VSphereConfig) (*VSphere, error) {
	id, err := readInstanceID(&cfg)
	if err != nil {
		return nil, err
	}

	vs := VSphere{
		cfg:             &cfg,
		localInstanceID: id,
	}
	return &vs, nil
}

func vsphereLogin(cfg *VSphereConfig, ctx context.Context) (*govmomi.Client, error) {

	// Parse URL from string
	u, err := url.Parse(fmt.Sprintf("https://%s:%s/sdk", cfg.Global.VCenterIP, cfg.Global.VCenterPort))
	if err != nil {
		return nil, err
	}
	// set username and password for the URL
	u.User = url.UserPassword(cfg.Global.User, cfg.Global.Password)

	// Connect and log in to ESX or vCenter
	c, err := govmomi.NewClient(ctx, u, cfg.Global.InsecureFlag)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func getVirtualMachineByName(cfg *VSphereConfig, ctx context.Context, c *govmomi.Client, name string) (*object.VirtualMachine, error) {
	// Create a new finder
	f := find.NewFinder(c.Client, true)

	// Fetch and set data center
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return nil, err
	}
	f.SetDatacenter(dc)

	// Retrieve vm by name
	//TODO: also look for vm inside subfolders
	vm, err := f.VirtualMachine(ctx, name)
	if err != nil {
		return nil, err
	}

	return vm, nil
}

func getVirtualMachineManagedObjectReference(ctx context.Context, c *govmomi.Client, vm *object.VirtualMachine, field string, dst interface{}) error {
	collector := property.DefaultCollector(c.Client)

	// Retrieve required field from VM object
	err := collector.RetrieveOne(ctx, vm.Reference(), []string{field}, dst)
	if err != nil {
		return err
	}
	return nil
}

func getInstances(cfg *VSphereConfig, ctx context.Context, c *govmomi.Client, filter string) ([]string, error) {
	f := find.NewFinder(c.Client, true)
	dc, err := f.Datacenter(ctx, cfg.Global.Datacenter)
	if err != nil {
		return nil, err
	}

	f.SetDatacenter(dc)

	//TODO: get all vms inside subfolders
	vms, err := f.VirtualMachineList(ctx, filter)
	if err != nil {
		return nil, err
	}

	var vmRef []types.ManagedObjectReference
	for _, vm := range vms {
		vmRef = append(vmRef, vm.Reference())
	}

	pc := property.DefaultCollector(c.Client)

	var vmt []mo.VirtualMachine
	err = pc.Retrieve(ctx, vmRef, []string{"name", "summary"}, &vmt)
	if err != nil {
		return nil, err
	}

	var vmList []string
	for _, vm := range vmt {
		if vm.Summary.Runtime.PowerState == ActivePowerState {
			vmList = append(vmList, vm.Name)
		} else if vm.Summary.Config.Template == false {
			glog.Warningf("VM %s, is not in %s state", vm.Name, ActivePowerState)
		}
	}
	return vmList, nil
}

type Instances struct {
	cfg             *VSphereConfig
	localInstanceID string
}

// Instances returns an implementation of Instances for vSphere.
func (vs *VSphere) Instances() (cloudprovider.Instances, bool) {
	return &Instances{vs.cfg, vs.localInstanceID}, true
}

// List is an implementation of Instances.List.
func (i *Instances) List(filter string) ([]string, error) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return nil, err
	}
	defer c.Logout(ctx)

	vmList, err := getInstances(i.cfg, ctx, c, filter)
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %s instances matching %s: %s",
		len(vmList), filter, vmList)

	return vmList, nil
}

// NodeAddresses is an implementation of Instances.NodeAddresses.
func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) {
	addrs := []api.NodeAddress{}

	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return nil, err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)
	if err != nil {
		return nil, err
	}

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "guest.net", &mvm)
	if err != nil {
		return nil, err
	}

	// retrieve VM's ip(s)
	for _, v := range mvm.Guest.Net {
		var addressType api.NodeAddressType
		if i.cfg.Network.PublicNetwork == v.Network {
			addressType = api.NodeExternalIP
		} else {
			addressType = api.NodeInternalIP
		}
		for _, ip := range v.IpAddress {
			api.AddToNodeAddresses(&addrs,
				api.NodeAddress{
					Type:    addressType,
					Address: ip,
				},
			)
		}
	}
	return addrs, nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return errors.New("unimplemented")
}

func (i *Instances) CurrentNodeName(hostname string) (string, error) {
	return i.localInstanceID, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name string) (string, error) {
	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)
	if err != nil {
		return "", err
	}

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "summary", &mvm)
	if err != nil {
		return "", err
	}

	if mvm.Summary.Runtime.PowerState == ActivePowerState {
		return vm.InventoryPath, nil
	}

	if mvm.Summary.Config.Template == false {
		glog.Warningf("VM %s, is not in %s state", name, ActivePowerState)
	} else {
		glog.Warningf("VM %s, is a template", name)
	}

	return "", cloudprovider.InstanceNotFound
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name string) (string, error) {
	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create vSphere client
	c, err := vsphereLogin(i.cfg, ctx)
	if err != nil {
		return "", err
	}
	defer c.Logout(ctx)

	vm, err := getVirtualMachineByName(i.cfg, ctx, c, name)

	var mvm mo.VirtualMachine
	err = getVirtualMachineManagedObjectReference(ctx, c, vm, "summary", &mvm)
	if err != nil {
		return "", err
	}

	if mvm.Summary.Runtime.PowerState == ActivePowerState {
		return "/" + vm.InventoryPath, nil
	}

	if mvm.Summary.Config.Template == false {
		glog.Warning("VM %s, is not in %s state", name, ActivePowerState)
	} else {
		glog.Warning("VM %s, is a template", name)
	}

	return "", cloudprovider.InstanceNotFound
}

func (i *Instances) InstanceType(name string) (string, error) {
	return "", nil
}

func (vs *VSphere) Clusters() (cloudprovider.Clusters, bool) {
	return nil, true
}

// ProviderName returns the cloud provider ID.
func (vs *VSphere) ProviderName() string {
	return ProviderName
}

// LoadBalancer returns an implementation of LoadBalancer for vSphere.
func (vs *VSphere) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
	return nil, false
}

// Zones returns an implementation of Zones for Google vSphere.
func (vs *VSphere) Zones() (cloudprovider.Zones, bool) {
	glog.V(4).Info("Claiming to support Zones")

	return vs, true
}

func (vs *VSphere) GetZone() (cloudprovider.Zone, error) {
	glog.V(4).Infof("Current zone is %v", vs.cfg.Global.Datacenter)

	return cloudprovider.Zone{Region: vs.cfg.Global.Datacenter}, nil
}

// Routes returns an implementation of Routes for vSphere.
func (vs *VSphere) Routes() (cloudprovider.Routes, bool) {
	return nil, true
}

// ScrubDNS filters DNS settings for pods.
func (vs *VSphere) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
	return nameservers, searches
}
<|endoftext|>


<commit_before>package nodepool

import (
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes"
	appslisters "k8s.io/client-go/listers/apps/v1beta1"
	"k8s.io/client-go/tools/record"

	v1alpha1 "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1"
)

type Interface interface {
	Sync(*v1alpha1.CassandraCluster) error
}

type defaultCassandraClusterNodepoolControl struct {
	kubeClient        kubernetes.Interface
	statefulSetLister appslisters.StatefulSetLister
	recorder          record.EventRecorder
}

var _ Interface = &defaultCassandraClusterNodepoolControl{}

func NewControl(
	kubeClient kubernetes.Interface,
	statefulSetLister appslisters.StatefulSetLister,
	recorder record.EventRecorder,
) Interface {
	return &defaultCassandraClusterNodepoolControl{
		kubeClient:        kubeClient,
		statefulSetLister: statefulSetLister,
		recorder:          recorder,
	}
}

func (e *defaultCassandraClusterNodepoolControl) createStatefulSet(
	cluster *v1alpha1.CassandraCluster,
	nodePool *v1alpha1.CassandraClusterNodePool,
) error {
	desiredSet := StatefulSetForCluster(cluster, nodePool)
	client := e.kubeClient.AppsV1beta1().StatefulSets(cluster.Namespace)
	lister := e.statefulSetLister.StatefulSets(desiredSet.Namespace)
	_, err := lister.Get(desiredSet.Name)
	// StatefulSet already exists
	if err == nil {
		return nil
	}
	if !k8sErrors.IsNotFound(err) {
		return err
	}
	_, err = client.Create(desiredSet)
	if k8sErrors.IsAlreadyExists(err) {
		return nil
	}
	return err
}

func (e *defaultCassandraClusterNodepoolControl) Sync(cluster *v1alpha1.CassandraCluster) error {
	for _, pool := range cluster.Spec.NodePools {
		err := e.createStatefulSet(cluster, &pool)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Add back a temporary hack to enable scale out so that E2E tests continue to pass.<commit_after>package nodepool

import (
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes"
	appslisters "k8s.io/client-go/listers/apps/v1beta1"
	"k8s.io/client-go/tools/record"

	v1alpha1 "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1"
)

type Interface interface {
	Sync(*v1alpha1.CassandraCluster) error
}

type defaultCassandraClusterNodepoolControl struct {
	kubeClient        kubernetes.Interface
	statefulSetLister appslisters.StatefulSetLister
	recorder          record.EventRecorder
}

var _ Interface = &defaultCassandraClusterNodepoolControl{}

func NewControl(
	kubeClient kubernetes.Interface,
	statefulSetLister appslisters.StatefulSetLister,
	recorder record.EventRecorder,
) Interface {
	return &defaultCassandraClusterNodepoolControl{
		kubeClient:        kubeClient,
		statefulSetLister: statefulSetLister,
		recorder:          recorder,
	}
}

func (e *defaultCassandraClusterNodepoolControl) createStatefulSet(
	cluster *v1alpha1.CassandraCluster,
	nodePool *v1alpha1.CassandraClusterNodePool,
) error {
	desiredSet := StatefulSetForCluster(cluster, nodePool)
	client := e.kubeClient.AppsV1beta1().StatefulSets(cluster.Namespace)
	lister := e.statefulSetLister.StatefulSets(desiredSet.Namespace)
	existingSet, err := lister.Get(desiredSet.Name)
	// StatefulSet already exists
	if err == nil {
		// XXX Temporary hack to enable scale out until we implement ScaleOut action.
		if *existingSet.Spec.Replicas < *desiredSet.Spec.Replicas {
			existingSet = existingSet.DeepCopy()
			existingSet.Spec.Replicas = desiredSet.Spec.Replicas
			_, err = client.Update(existingSet)
			return err
		}
		return nil
	}
	if !k8sErrors.IsNotFound(err) {
		return err
	}
	_, err = client.Create(desiredSet)
	if k8sErrors.IsAlreadyExists(err) {
		return nil
	}
	return err
}

func (e *defaultCassandraClusterNodepoolControl) Sync(cluster *v1alpha1.CassandraCluster) error {
	for _, pool := range cluster.Spec.NodePools {
		err := e.createStatefulSet(cluster, &pool)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"strings"
)

type proxyHost struct {
	Proto   string
	Address string
}

func NewProxy(localAddr, remoteAddr string) error {
	var (
		local  proxyHost
		remote proxyHost
	)
	local.Proto, local.Address = splitURI(localAddr)
	remote.Proto, remote.Address = splitURI(remoteAddr)
	fmt.Println(local, remote)

	listener, err := net.Listen(local.Proto, local.Address)
	if err != nil {
		return err
	}

	fmt.Println(listener)

	pending, complete := make(chan net.Conn), make(chan net.Conn)
	go closeConn(complete)

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		go func() {
			go handleConn(pending, complete, remote)
			pending <- conn
		}()
	}

	return nil
}

func closeConn(in <-chan net.Conn) {
	for conn := range in {
		conn.Close()
	}
}

func handleConn(in <-chan net.Conn, out chan<- net.Conn, remote proxyHost) {
	for conn := range in {
		proxyConn(remote, conn)
		out <- conn
	}
}

func proxyConn(host proxyHost, conn net.Conn) {
	rConn, err := net.Dial(host.Proto, host.Address)
	if err != nil {
		panic(err)
	}
	defer rConn.Close()

	buf := &bytes.Buffer{}
	for {
		data := make([]byte, 256)
		n, err := conn.Read(data)
		if err != nil {
			if err != io.EOF {
				panic(err)
			}
		}
		buf.Write(data[:n])
		if (data[len(data[:n])-2] == '\r' && data[len(data[:n])-1] == '\n') || data[0] == 0 {
			break
		}
	}

	if _, err := rConn.Write(buf.Bytes()); err != nil {
		panic(err)
	}

	data := make([]byte, 1024)
	n, err := rConn.Read(data)
	if err != nil {
		if err != io.EOF {
			panic(err)
		}
	}
	fmt.Println(n)
}

func splitURI(uri string) (string, string) {
	arr := strings.Split(uri, "://")

	if len(arr) == 1 {
		return "unix", arr[0]
	}

	proto := arr[0]
	if proto == "http" {
		proto = "tcp"
	}

	return proto, arr[1]
}

func main() {
	local := "tcp://localhost:3000"
	remote := "tcp://google.com:80"

	err := NewProxy(local, remote)
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>Fix read/write issues<commit_after>package main

import (
	"fmt"
	"net"
	"os"
	"strings"
)

type host struct {
	Proto   string
	Address string
	Port    int
}

func NewProxy(fromUrl, toUrl string) error {
	var (
		from host
		to   host
	)

	from.Proto, from.Address = parseURL(fromUrl)
	to.Proto, to.Address = parseURL(toUrl)

	waiting, complete := make(chan net.Conn), make(chan net.Conn)

	server, err := net.Listen(from.Proto, from.Address)
	if err != nil {
		return err
	}

	for {
		conn, err := server.Accept()
		if err != nil {
			return err
		}
		go func() {
			go handleConn(waiting, complete, to)
			waiting <- conn
		}()
	}

	return nil
}

func closeConn(in chan net.Conn) {
	for conn := range in {
		conn.Close()
	}
}

func handleConn(waiting chan net.Conn, complete chan net.Conn, remote host) {
	for conn := range waiting {
		proxyConn(remote, conn)
		complete <- conn
	}
}

func proxyConn(toHost host, from net.Conn) {
	defer from.Close()

	to, err := net.Dial(toHost.Proto, toHost.Address)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	defer to.Close()

	complete := make(chan bool)

	go copyContent(from, to, complete)
	go copyContent(to, from, complete)
	<-complete
}

func copyContent(from, to net.Conn, complete chan bool) {
	var (
		err   error  = nil
		bytes []byte = make([]byte, 256)
		read  int    = 0
	)

	for {
		read, err = from.Read(bytes)
		if err != nil {
			complete <- true
			break
		}
		_, err = to.Write(bytes[:read])
		if err != nil {
			complete <- true
			break
		}
	}
}

func parseURL(uri string) (string, string) {
	arr := strings.Split(uri, "://")

	if len(arr) == 1 {
		return "unix", arr[0] //, 0
	}

	proto := arr[0]
	if proto == "http" {
		proto = "tcp"
	}

	return proto, arr[1]
}

func main() {
	from := os.Args[1]
	to := os.Args[2]

	err := NewProxy(from, to)
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package resourcecollector

import (
	"fmt"

	"github.com/libopenstorage/stork/drivers/volume"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
)

func (r *ResourceCollector) pvcToBeCollected(
	object runtime.Unstructured,
	namespace string,
	allDrivers bool,
) (bool, error) {
	metadata, err := meta.Accessor(object)
	if err != nil {
		return false, err
	}

	pvcName := metadata.GetName()
	pvc, err := r.coreOps.GetPersistentVolumeClaim(pvcName, namespace)
	if err != nil {
		return false, err
	}

	// Only collect Bound PVCs that aren't being deleted
	if pvc.Status.Phase != v1.ClaimBound || pvc.DeletionTimestamp != nil {
		return false, nil
	}

	// Don't collect PVCs not owned by the driver if collecting for a specific
	// driver
	if !allDrivers && !r.Driver.OwnsPVC(pvc) {
		return false, nil
	}
	// Else collect PVCs for all supported drivers
	_, err = volume.GetPVCDriver(pvc)
	if err != nil {
		return false, nil
	}

	return true, nil
}

// Updates the PVC by pointing to the new PV that it should refer to
// pvNameMappings has the map of the original PV name to the new PV name
func (r *ResourceCollector) preparePVCResourceForApply(
	object runtime.Unstructured,
	allObjects []runtime.Unstructured,
	pvNameMappings map[string]string,
) (bool, error) {
	var pvc v1.PersistentVolumeClaim
	var updatedName string
	var present bool

	metadata, err := meta.Accessor(object)
	if err != nil {
		return false, err
	}

	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.UnstructuredContent(), &pvc); err != nil {
		return false, fmt.Errorf("error converting PVC object: %v: %v", object, err)
	}
	pv, err := getCSIPV(&pvc, allObjects)
	if err != nil {
		return false, fmt.Errorf("failed to get CSI PV for a given PVC %s: %v", pvc.Name, err)
	}
	isCSIPVC, err := isCSIPersistentVolume(pv)
	if err != nil {
		return false, fmt.Errorf("failed to check if PVC is for a CSI driver: %v", err)
	}
	if isCSIPVC {
		logrus.Debugf("skipping CSI PVC in pre-restore: %s", metadata.GetName())
		return true, nil
	}

	if updatedName, present = pvNameMappings[pvc.Spec.VolumeName]; !present {
		return false, fmt.Errorf("PV name mapping not found for %v", metadata.GetName())
	}
	pvc.Spec.VolumeName = updatedName
	o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&pvc)
	if err != nil {
		return false, err
	}
	object.SetUnstructuredContent(o)
	return false, nil
}
<commit_msg>Fix for non-CSI PVC restore not erroring out<commit_after>package resourcecollector

import (
	"fmt"

	"github.com/libopenstorage/stork/drivers/volume"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
)

func (r *ResourceCollector) pvcToBeCollected(
	object runtime.Unstructured,
	namespace string,
	allDrivers bool,
) (bool, error) {
	metadata, err := meta.Accessor(object)
	if err != nil {
		return false, err
	}

	pvcName := metadata.GetName()
	pvc, err := r.coreOps.GetPersistentVolumeClaim(pvcName, namespace)
	if err != nil {
		return false, err
	}

	// Only collect Bound PVCs that aren't being deleted
	if pvc.Status.Phase != v1.ClaimBound || pvc.DeletionTimestamp != nil {
		return false, nil
	}

	// Don't collect PVCs not owned by the driver if collecting for a specific
	// driver
	if !allDrivers && !r.Driver.OwnsPVC(pvc) {
		return false, nil
	}
	// Else collect PVCs for all supported drivers
	_, err = volume.GetPVCDriver(pvc)
	if err != nil {
		return false, nil
	}

	return true, nil
}

// Updates the PVC by pointing to the new PV that it should refer to
// pvNameMappings has the map of the original PV name to the new PV name
func (r *ResourceCollector) preparePVCResourceForApply(
	object runtime.Unstructured,
	allObjects []runtime.Unstructured,
	pvNameMappings map[string]string,
) (bool, error) {
	var pvc v1.PersistentVolumeClaim
	var updatedName string
	var present bool

	metadata, err := meta.Accessor(object)
	if err != nil {
		return false, err
	}

	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.UnstructuredContent(), &pvc); err != nil {
		return false, fmt.Errorf("error converting PVC object: %v: %v", object, err)
	}
	pv, err := getCSIPV(&pvc, allObjects)
	if err != nil {
		// if not a CSI PVC, this may fail. Do not return error to allow for non-CSI PVCs
		return false, nil
	}
	isCSIPVC, err := isCSIPersistentVolume(pv)
	if err != nil {
		return false, fmt.Errorf("failed to check if PVC is for a CSI driver: %v", err)
	}
	if isCSIPVC {
		logrus.Debugf("skipping CSI PVC in pre-restore: %s", metadata.GetName())
		return true, nil
	}

	if updatedName, present = pvNameMappings[pvc.Spec.VolumeName]; !present {
		return false, fmt.Errorf("PV name mapping not found for %v", metadata.GetName())
	}
	pvc.Spec.VolumeName = updatedName
	o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&pvc)
	if err != nil {
		return false, err
	}
	object.SetUnstructuredContent(o)
	return false, nil
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"net/http"

	"ratings/responses"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

type (
	app struct {
		Key     string `json:"key" validate:"required,len=32,alphanum,excludesall= " conform:"lower,trim"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"lower,trim"`
	}

	user struct {
		Name   string `json:"name" validate:"omitempty,gte=3,lte=70" conform:"name,trim"`
		Email  string `json:"email" validate:"omitempty,email,gte=3,lte=100,excludesall= " conform:"email,trim"`
		MiBAID string `json:"mibaId" validate:"omitempty,alphanum,gte=1,excludesall= " conform:"lower,trim"`
	}

	platform struct {
		Key     string `json:"key" validate:"required,len=32,alphanum,excludesall= " conform:"lower,trim"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"lower,trim"`
	}

	device struct {
		Name   string  `json:"name" validate:"required,gte=1,lte=30" conform:"trim"`
		Brand  *string `json:"brand" validate:"omitempty,gte=1,lte=30" conform:"trim"`
		Screen screen  `json:"screen" validate:"required"`
	}

	screen struct {
		Width  int  `json:"width" validate:"required,gt=0"`
		Height int  `json:"height" validate:"required,gt=0"`
		PPI    *int `json:"ppi" validate:"omitempty,gt=0"`
	}

	browser struct {
		Name    string `json:"name" validate:"required,gte=1,lte=15" conform:"trim"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"lower,trim"`
	}

	// Request holds the mapped fields from the request's JSON body
	Request struct {
		Rating      int8     `json:"rating" validate:"min=-127,max=127"`
		Description string   `json:"description" validate:"omitempty,gte=3,lte=30" conform:"trim,title"`
		Comment     string   `json:"comment" validate:"omitempty,gte=3,lte=1000" conform:"trim,ucfirst"`
		Range       string   `json:"range" validate:"required,len=32,alphanum,excludesall= " conform:"lower,trim"`
		App         app      `json:"app" validate:"required"`
		Platform    platform `json:"platform" validate:"required"`
		Device      device   `json:"device" validate:"required"`
		User        *user    `json:"user" validate:"omitempty"`
		Browser     *browser `json:"browser" validate:"omitempty"`
	}
)

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		context.Logger().Error("Error binding request: " + err.Error())

		return request, err
	}

	if err := validate(request, context); err != nil {
		context.Logger().Error("Error validating request: " + err.Error())

		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorMessage := fmt.Sprintf("Error parsing request: %s", err.Error())
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error(errorMessage)

		return responses.ErrorResponse(errorCode, errorMessage, context)
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			return responses.ErrorResponse(http.StatusUnprocessableEntity, errs.Error(), context)
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorMessage := fmt.Sprintf("Error validating request: %s", err.(error).Error())
			errorList = append(errorList, errorMessage)

			context.Logger().Error(errorMessage)
		}

		return responses.ErrorsResponse(http.StatusUnprocessableEntity, errorList, context)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Comment = sanitizer.Sanitize(request.Comment)
	request.Description = sanitizer.Sanitize(request.Description)
}

func RegisterCustomValidators(validate *validator.Validate) {
	validate.RegisterStructValidation(UserCustomValidator, user{})
}

func UserCustomValidator(sl validator.StructLevel) {
	item := sl.Current().Interface().(user)

	if len(item.Email) == 0 && len(item.MiBAID) == 0 {
		sl.ReportError(item.Email, "Email", "email", "email/mibaid", "")
		sl.ReportError(item.MiBAID, "MiBAID", "baid", "email/mibaid", "")
	}
}
<commit_msg>Reordered operations<commit_after>package parser

import (
	"fmt"
	"net/http"

	"ratings/responses"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

type (
	app struct {
		Key     string `json:"key" validate:"required,len=32,alphanum,excludesall= " conform:"trim,lower"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"trim,lower"`
	}

	user struct {
		Name   string `json:"name" validate:"omitempty,gte=3,lte=70" conform:"trim,name"`
		Email  string `json:"email" validate:"omitempty,email,gte=3,lte=100,excludesall= " conform:"trim,email"`
		MiBAID string `json:"mibaId" validate:"omitempty,alphanum,gte=1,excludesall= " conform:"trim,lower"`
	}

	platform struct {
		Key     string `json:"key" validate:"required,len=32,alphanum,excludesall= " conform:"trim,lower"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"trim,lower"`
	}

	device struct {
		Name   string  `json:"name" validate:"required,gte=1,lte=30" conform:"trim"`
		Brand  *string `json:"brand" validate:"omitempty,gte=1,lte=30" conform:"trim"`
		Screen screen  `json:"screen" validate:"required"`
	}

	screen struct {
		Width  int  `json:"width" validate:"required,gt=0"`
		Height int  `json:"height" validate:"required,gt=0"`
		PPI    *int `json:"ppi" validate:"omitempty,gt=0"`
	}

	browser struct {
		Name    string `json:"name" validate:"required,gte=1,lte=15" conform:"trim"`
		Version string `json:"version" validate:"required,gte=1,lte=15,excludesall= " conform:"trim,lower"`
	}

	// Request holds the mapped fields from the request's JSON body
	Request struct {
		Rating      int8     `json:"rating" validate:"min=-127,max=127"`
		Description string   `json:"description" validate:"omitempty,gte=3,lte=30" conform:"trim,title"`
		Comment     string   `json:"comment" validate:"omitempty,gte=3,lte=1000" conform:"trim,ucfirst"`
		Range       string   `json:"range" validate:"required,len=32,alphanum,excludesall= " conform:"trim,lower"`
		App         app      `json:"app" validate:"required"`
		Platform    platform `json:"platform" validate:"required"`
		Device      device   `json:"device" validate:"required"`
		User        *user    `json:"user" validate:"omitempty"`
		Browser     *browser `json:"browser" validate:"omitempty"`
	}
)

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		context.Logger().Error("Error binding request: " + err.Error())

		return request, err
	}

	if err := validate(request, context); err != nil {
		context.Logger().Error("Error validating request: " + err.Error())

		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorMessage := fmt.Sprintf("Error parsing request: %s", err.Error())
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error(errorMessage)

		return responses.ErrorResponse(errorCode, errorMessage, context)
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			return responses.ErrorResponse(http.StatusUnprocessableEntity, errs.Error(), context)
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorMessage := fmt.Sprintf("Error validating request: %s", err.(error).Error())
			errorList = append(errorList, errorMessage)

			context.Logger().Error(errorMessage)
		}

		return responses.ErrorsResponse(http.StatusUnprocessableEntity, errorList, context)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Comment = sanitizer.Sanitize(request.Comment)
	request.Description = sanitizer.Sanitize(request.Description)
}

func RegisterCustomValidators(validate *validator.Validate) {
	validate.RegisterStructValidation(UserCustomValidator, user{})
}

func UserCustomValidator(sl validator.StructLevel) {
	item := sl.Current().Interface().(user)

	if len(item.Email) == 0 && len(item.MiBAID) == 0 {
		sl.ReportError(item.Email, "Email", "email", "email/mibaid", "")
		sl.ReportError(item.MiBAID, "MiBAID", "baid", "email/mibaid", "")
	}
}
<|endoftext|>


<commit_before>package httpcontext

import (
	"errors"
	"io"
	"net/http"
)

// ContextReadCloser implements the io.ReadCloser interface
// with two additional methods: Context() and SetContext().
type ContextReadCloser interface {
	io.ReadCloser
	Context() interface{}
	SetContext(interface{})
}

type contextReadCloser struct {
	io.ReadCloser
	context interface{}
}

func (crc *contextReadCloser) Context() interface{} {
	return crc.context
}

func (crc *contextReadCloser) SetContext(context interface{}) {
	crc.context = context
}

// Set sets a context value on req.
// It accomplishes this by replacing the http.Request.Body with
// a ContextReadCloser. See Invasion of the Body Snatchers.
func Set(req *http.Request, context interface{}) {
	crc, ok := req.Body.(ContextReadCloser)
	if !ok {
		crc = &contextReadCloser{ReadCloser: req.Body}
		req.Body = crc
	}
	crc.SetContext(context)
}

// Set gets a context value from req.
// Set will return an error if req.Body was not previously
// replaced with a ContextReadCloser.
func Get(req *http.Request) (interface{}, error) {
	crc, ok := req.Body.(ContextReadCloser)
	if !ok {
		return nil, errors.New("Unable to convert http.Request.Body to ContextReadCloser")
	}
	return crc.Context(), nil
}
<commit_msg>Re-implement ContextReadCloser as a map[interface{}]interface{}. This implements the Gorilla context interface at https://github.com/gorilla/context<commit_after>package httpcontext

import (
	"io"
	"net/http"
)

// Set sets a context value on req.
// It currently accomplishes this by replacing the http.Request’s Body with
// a ContextReadCloser, which wraps the original io.ReadCloser.
// See “Invasion of the Body Snatchers.”
func Set(req *http.Request, key interface{}, value interface{}) {
	crc := getContextReadCloser(req)
	crc.Context()[key] = value
}

// Get gets a context value from req.
// Returns nil if key is not present in the request context.
func Get(req *http.Request, key interface{}) interface{} {
	crc := getContextReadCloser(req)
	if value, ok := crc.Context()[key]; ok {
		return value
	}
	return nil
}

// GetOk gets a context value from req.
// Returns (nil, false) if key is not present in the request context.
func GetOk(req *http.Request, key interface{}) (interface{}, bool) {
	crc := getContextReadCloser(req)
	if value, ok := crc.Context()[key]; ok {
		return value, ok
	}
	return nil, false
}

// ContextReadCloser implements the io.ReadCloser interface
// with two additional methods: Context() and SetContext().
type ContextReadCloser interface {
	io.ReadCloser
	Context() map[interface{}]interface{}
}

type contextReadCloser struct {
	io.ReadCloser
	context map[interface{}]interface{}
}

func (crc *contextReadCloser) Context() map[interface{}]interface{} {
	return crc.context
}

func getContextReadCloser(req *http.Request) ContextReadCloser {
	crc, ok := req.Body.(ContextReadCloser)
	if !ok {
		crc = &contextReadCloser{
			ReadCloser: req.Body,
			context:    make(map[interface{}]interface{}),
		}
		req.Body = crc
	}
	return crc
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
)

type errorJSON struct {
	Error string `json:'error'`
}

type httphandler struct {
}

func errorjson(w http.ResponseWriter, code int, errString string) {
	w.WriteHeader(code)
	ej := &errorJSON{
		Error: errString,
	}
	out, err := json.Marshal(ej)
	if err != nil {
		w.Write([]byte("How did this happen?"))
		return
	}
	w.Write(out)
}

func (h httphandler) ServeHTTP(w http.ResponseWriter, request *http.Request) {
	fmt.Println(request.URL.Path)
	parts := strings.Split(request.URL.Path, "/")
	if parts[1] == "jq" {
		if len(parts) < 5 {
			errorjson(w, 404, "Expected url in format /jq/urlencode(jq filter)/to/urlencode(path)")
			return
		}
		JqHandler(w,
			request,
			parts[2],
			request.URL.Path[len(parts[2])+8:],
		)
	}
}
<commit_msg>Fix struct tag format<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
)

type errorJSON struct {
	Error string `json:"error"`
}

type httphandler struct {
}

func errorjson(w http.ResponseWriter, code int, errString string) {
	w.WriteHeader(code)
	ej := &errorJSON{
		Error: errString,
	}
	out, err := json.Marshal(ej)
	if err != nil {
		w.Write([]byte("How did this happen?"))
		return
	}
	w.Write(out)
}

func (h httphandler) ServeHTTP(w http.ResponseWriter, request *http.Request) {
	fmt.Println(request.URL.Path)
	parts := strings.Split(request.URL.Path, "/")
	if parts[1] == "jq" {
		if len(parts) < 5 {
			errorjson(w, 404, "Expected url in format /jq/urlencode(jq filter)/to/urlencode(path)")
			return
		}
		JqHandler(w,
			request,
			parts[2],
			request.URL.Path[len(parts[2])+8:],
		)
	}
}
<|endoftext|>


<commit_before>package httprequest

import (
	"os"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"
	"mime/multipart"
	"bytes"
	"strings"
	"net/url"
	"github.com/mreiferson/go-httpclient"
)

type HttpRequest struct {
	Transport *httpclient.Transport
	Client *http.Client
	ProxyFlag bool
	BasicAuthFlag bool
	Username string
	Password string
	ProxyUrl string
}

func NewWithDefaults() (*HttpRequest) {
	timeout := 10000 * time.Millisecond
	return New(timeout, timeout, timeout, true, true)
}

func New(connection_timeout time.Duration, response_header_timeout time.Duration, request_timeout time.Duration, keep_alive_flag bool, compression_flag bool) (*HttpRequest) {
	transport := &httpclient.Transport {
		DisableKeepAlives: !keep_alive_flag,
		DisableCompression: !compression_flag,
		ConnectTimeout: connection_timeout,
		ResponseHeaderTimeout: response_header_timeout,
		RequestTimeout: request_timeout,
	}
	defer transport.Close()
	client := &http.Client{ Transport: transport }
	r := HttpRequest{ Transport: transport, Client: client, ProxyFlag: false, BasicAuthFlag: false, Username: "", Password: "", ProxyUrl: "" }
	return &r
}

func (r *HttpRequest) Dump() {
	fmt.Println("HttpRequest")
	fmt.Println("-----------")
	fmt.Println("\tProxyFlag:      ", r.ProxyFlag)
	fmt.Println("\tBasicAuthFlag:  ", r.BasicAuthFlag)
	fmt.Println("\tUsername:       ", r.Username)
	fmt.Println("\tPassword:       ", r.Password)
	fmt.Println("\tProxyUrl:       ", r.ProxyUrl)
}

func (r *HttpRequest) UseBasicAuth(flag bool) {
	r.BasicAuthFlag = flag
}

func (r *HttpRequest) UseProxy(flag bool) {
	r.ProxyFlag = flag
}

func (r *HttpRequest) Get(url string, headers map[string]string) ([]byte, int, error) {
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) PostUrlEncoded(url string, headers map[string]string, data url.Values) ([]byte, int, error) {
	body := strings.NewReader(data.Encode())
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) PostMultipart(url string, headers map[string]string, multipart_params map[string]string) ([]byte, int, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	for key, val := range multipart_params {
		_ = writer.WriteField(key, val)
	}
	err := writer.Close()
	if err != nil {
		return nil, -1, err
	}
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) Put(url string, headers map[string]string, data string) ([]byte, int, error) {
	body := &bytes.Buffer{}
	_, err := body.WriteString(data)
	if err != nil {
		return nil, -1, err
	}
	request, err := http.NewRequest("PUT", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}
<commit_msg>Added TLS Verify Flag.<commit_after>package httprequest

import (
	"os"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"
	"mime/multipart"
	"bytes"
	"strings"
	"net/url"
	"github.com/mreiferson/go-httpclient"
	"crypto/tls"
)

type HttpRequest struct {
	Transport *httpclient.Transport
	Client *http.Client
	ProxyFlag bool
	BasicAuthFlag bool
	Username string
	Password string
	ProxyUrl string
}

func NewWithDefaults() (*HttpRequest) {
	timeout := 10000 * time.Millisecond
	return New(timeout, timeout, timeout, true, true, false)
}

func New(connection_timeout time.Duration, response_header_timeout time.Duration, request_timeout time.Duration, keep_alive_flag bool, compression_flag bool, skip_tls_verify bool) (*HttpRequest) {
	transport := &httpclient.Transport {
		DisableKeepAlives: !keep_alive_flag,
		DisableCompression: !compression_flag,
		ConnectTimeout: connection_timeout,
		ResponseHeaderTimeout: response_header_timeout,
		RequestTimeout: request_timeout,
		TLSClientConfig: &tls.Config{InsecureSkipVerify: skip_tls_verify},
	}

	defer transport.Close()
	client := &http.Client{ Transport: transport }
	r := HttpRequest{ Transport: transport, Client: client, ProxyFlag: false, BasicAuthFlag: false, Username: "", Password: "", ProxyUrl: "" }
	return &r
}

func (r *HttpRequest) Dump() {
	fmt.Println("HttpRequest")
	fmt.Println("-----------")
	fmt.Println("\tProxyFlag:      ", r.ProxyFlag)
	fmt.Println("\tBasicAuthFlag:  ", r.BasicAuthFlag)
	fmt.Println("\tUsername:       ", r.Username)
	fmt.Println("\tPassword:       ", r.Password)
	fmt.Println("\tProxyUrl:       ", r.ProxyUrl)
}

func (r *HttpRequest) UseBasicAuth(flag bool) {
	r.BasicAuthFlag = flag
}

func (r *HttpRequest) UseProxy(flag bool) {
	r.ProxyFlag = flag
}

func (r *HttpRequest) Get(url string, headers map[string]string) ([]byte, int, error) {
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) PostUrlEncoded(url string, headers map[string]string, data url.Values) ([]byte, int, error) {
	body := strings.NewReader(data.Encode())
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) PostMultipart(url string, headers map[string]string, multipart_params map[string]string) ([]byte, int, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	for key, val := range multipart_params {
		_ = writer.WriteField(key, val)
	}
	err := writer.Close()
	if err != nil {
		return nil, -1, err
	}
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}

func (r *HttpRequest) Put(url string, headers map[string]string, data string) ([]byte, int, error) {
	body := &bytes.Buffer{}
	_, err := body.WriteString(data)
	if err != nil {
		return nil, -1, err
	}
	request, err := http.NewRequest("PUT", url, body)
	if err != nil {
		return nil, -1, err
	}
	if r.ProxyFlag {
		os.Setenv("HTTP_PROXY", r.ProxyUrl)
	}
	if r.BasicAuthFlag {
		request.SetBasicAuth(r.Username, r.Password)
	}
	if headers != nil {
		for key, val := range headers {
			request.Header.Set(key, val)
		}
	}
	response, err := r.Client.Do(request)
	if err != nil {
		return nil, -1, err
	}
	defer response.Body.Close()
	status_code := response.StatusCode
	contents, err := ioutil.ReadAll(response.Body)
	neterr, ok := err.(net.Error)
	if ok && neterr.Timeout() {
		err = nil
	}
	if err != nil {
		return nil, status_code, err
	}
	return contents, status_code, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"os/user"
	"path/filepath"
	//"fmt"
	"code.google.com/p/gcfg"
	"github.com/op/go-logging"
)

const (
	GPS_VERSION = "0.2.0"
)

// Command ling flags
var verboseFlag = flag.Bool("v", false, "Show verbose debug information")
var configFlag = flag.String("c", "", "Use alternative config file")
var groupFlag = flag.String("g", "", "Newsgroup(s) to post to - separate multiple with a comma \",\"")
var subjectFlag = flag.String("s", "", "Subject to use")
var dirSubjectFlag = flag.Bool("d", false, "Use directory names as subjects")

// Logger
var log = logging.MustGetLogger("gopoststuff")

// Config
var Config struct {
	Global struct {
		From         string
		DefaultGroup string
		ArticleSize  int64
		SubjectPrefix string
	}

	Server map[string]*struct {
		Address     string
		Port        int
		Username    string
		Password    string
		Connections int
		TLS         bool
		InsecureSSL bool
	}
}

func main() {
	// Parse command line flags
	flag.Parse()

	// Set up logging
	var format = logging.MustStringFormatter(" %{level: -8s}  %{message}")
	logging.SetFormatter(format)
	if *verboseFlag {
		logging.SetLevel(logging.DEBUG, "gopoststuff")
	} else {
		logging.SetLevel(logging.INFO, "gopoststuff")
	}

	log.Info("gopoststuff starting...")

	// Make sure -d or -s was specified
	if len(*subjectFlag) == 0 && !*dirSubjectFlag {
		log.Fatal("Need to specify -d or -s option, try gopoststuff --help")
	}

	// Check arguments
	if len(flag.Args()) == 0 {
		log.Fatal("No filenames provided")
	}

	// Check that all supplied arguments exist
	for _, arg := range flag.Args() {
		st, err := os.Stat(arg)
		if err != nil {
			log.Fatalf("stat %s: %s", arg, err)
		}

		// If -d was specified, make sure that it's a directory
		if *dirSubjectFlag && !st.IsDir() {
			log.Fatalf("-d option used but not a directory: %s", arg)
		}
	}

	// Load config file
	var cfgFile string
	if len(*configFlag) > 0 {
		cfgFile = *configFlag
	} else {
		// Default to user homedir for config file
		u, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		cfgFile = filepath.Join(u.HomeDir, ".gopoststuff.conf")
	}

	log.Debug("Reading config from %s", cfgFile)

	err := gcfg.ReadFileInto(&Config, cfgFile)
	if err != nil {
		log.Fatal(err)
	}

	// Start the magical spawner
	Spawner(flag.Args())
}
<commit_msg>Update version string to differentiate between in progress and final versions<commit_after>package main

import (
	"flag"
	"os"
	"os/user"
	"path/filepath"
	//"fmt"
	"code.google.com/p/gcfg"
	"github.com/op/go-logging"
)

const (
	GPS_VERSION = "0.2.0-git"
)

// Command ling flags
var verboseFlag = flag.Bool("v", false, "Show verbose debug information")
var configFlag = flag.String("c", "", "Use alternative config file")
var groupFlag = flag.String("g", "", "Newsgroup(s) to post to - separate multiple with a comma \",\"")
var subjectFlag = flag.String("s", "", "Subject to use")
var dirSubjectFlag = flag.Bool("d", false, "Use directory names as subjects")

// Logger
var log = logging.MustGetLogger("gopoststuff")

// Config
var Config struct {
	Global struct {
		From         string
		DefaultGroup string
		ArticleSize  int64
		SubjectPrefix string
	}

	Server map[string]*struct {
		Address     string
		Port        int
		Username    string
		Password    string
		Connections int
		TLS         bool
		InsecureSSL bool
	}
}

func main() {
	// Parse command line flags
	flag.Parse()

	// Set up logging
	var format = logging.MustStringFormatter(" %{level: -8s}  %{message}")
	logging.SetFormatter(format)
	if *verboseFlag {
		logging.SetLevel(logging.DEBUG, "gopoststuff")
	} else {
		logging.SetLevel(logging.INFO, "gopoststuff")
	}

	log.Info("gopoststuff starting...")

	// Make sure -d or -s was specified
	if len(*subjectFlag) == 0 && !*dirSubjectFlag {
		log.Fatal("Need to specify -d or -s option, try gopoststuff --help")
	}

	// Check arguments
	if len(flag.Args()) == 0 {
		log.Fatal("No filenames provided")
	}

	// Check that all supplied arguments exist
	for _, arg := range flag.Args() {
		st, err := os.Stat(arg)
		if err != nil {
			log.Fatalf("stat %s: %s", arg, err)
		}

		// If -d was specified, make sure that it's a directory
		if *dirSubjectFlag && !st.IsDir() {
			log.Fatalf("-d option used but not a directory: %s", arg)
		}
	}

	// Load config file
	var cfgFile string
	if len(*configFlag) > 0 {
		cfgFile = *configFlag
	} else {
		// Default to user homedir for config file
		u, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		cfgFile = filepath.Join(u.HomeDir, ".gopoststuff.conf")
	}

	log.Debug("Reading config from %s", cfgFile)

	err := gcfg.ReadFileInto(&Config, cfgFile)
	if err != nil {
		log.Fatal(err)
	}

	// Start the magical spawner
	Spawner(flag.Args())
}
<|endoftext|>


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

package httputil

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"golang.org/x/net/context"
)

// An interface for transports that support the signature of
// http.Transport.CancelRequest.
type canceller interface {
	CancelRequest(*http.Request)
}

// Wait until the context is cancelled, then cancel the supplied HTTP request.
// If the caller closes finished before this happens, return early.
//
// HACK(jacobsa): The http package's design for cancellation is flawed: the
// canceller must naturally race with the transport receiving the request --
// CancelRequest may be called before RoundTrip, and in this case the
// cancellation will be lost. This is especially a problem with wrapper
// transports like the oauth2 one, which may do extra work before calling
// through to http.Transport. Work around this by repeatedly cancelling until
// the finished channel is closed.
func waitForCancellation(
	ctx context.Context,
	c canceller,
	finished chan struct{},
	req *http.Request) {
	// If there is no done channel, there's nothing we can do.
	cancelChan := ctx.Done()
	if cancelChan == nil {
		return
	}

	// Wait for the context to be cancelled or for the caller to say they no
	// longer care because the HTTP request has been completed.
	select {
	case <-cancelChan:

	case <-finished:
		return
	}

	// Keep cancelling the HTTP request until the finished channel is closed, as
	// discussed above.
	for {
		c.CancelRequest(req)

		select {
		case <-finished:
			return

		case <-time.After(10 * time.Millisecond):
		}
	}
}

// Call client.Do with the supplied request, cancelling the request if the
// context is cancelled. Return an error if the client does not support
// cancellation.
func Do(
	ctx context.Context,
	client *http.Client,
	req *http.Request) (resp *http.Response, err error) {
	// Make sure the transport supports cancellation.
	c, ok := client.Transport.(canceller)
	if !ok {
		err = fmt.Errorf(
			"Transport of type %v doesn't support cancellation",
			reflect.TypeOf(client.Transport))
		return
	}

	// Wait for cancellation in the background, returning early if this function
	// returns.
	finished := make(chan struct{})
	defer close(finished)
	go waitForCancellation(ctx, c, finished, req)

	// Call through.
	resp, err = client.Do(req)

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

package httputil

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"golang.org/x/net/context"
)

// An interface for transports that support the signature of
// http.Transport.CancelRequest.
type canceller interface {
	CancelRequest(*http.Request)
}

// Wait for the allDone channel to be closed. If the context is cancelled
// before then, cancel the HTTP request via the canceller repeatedly until
// allDone is closed.
//
// We must cancel repeatedly because the http package's design for cancellation
// is flawed: the canceller must naturally race with the transport receiving
// the request (CancelRequest may be called before RoundTrip, and in this case
// the cancellation will be lost). This is especially a problem with wrapper
// transports like the oauth2 one, which may do extra work before calling
// through to http.Transport.
func waitForCancellation(
	ctx context.Context,
	allDone chan struct{},
	c canceller,
	req *http.Request) {
	// If the context is not cancellable, there is nothing interesting we can do.
	cancelChan := ctx.Done()
	if cancelChan == nil {
		return
	}

	// If the user closes allDone before the context is cancelled, we can return
	// immediately.
	select {
	case <-allDone:
		return

	case <-cancelChan:
	}

	// The context has been cancelled. Repeatedly cancel the HTTP request as
	// described above until the user closes allDone.
	for {
		c.CancelRequest(req)

		select {
		case <-allDone:
			return

		case <-time.After(10 * time.Millisecond):
		}
	}
}

// Call client.Do with the supplied request, cancelling the request if the
// context is cancelled. Return an error if the client does not support
// cancellation.
func Do(
	ctx context.Context,
	client *http.Client,
	req *http.Request) (resp *http.Response, err error) {
	// Make sure the transport supports cancellation.
	c, ok := client.Transport.(canceller)
	if !ok {
		err = fmt.Errorf(
			"Transport of type %v doesn't support cancellation",
			reflect.TypeOf(client.Transport))
		return
	}

	// Wait for cancellation in the background, returning early if this function
	// returns.
	finished := make(chan struct{})
	defer close(finished)
	go waitForCancellation(ctx, c, finished, req)

	// Call through.
	resp, err = client.Do(req)

	return
}
<|endoftext|>


<commit_before>// Package hyperloglog implements the HyperLogLog algorithm for
// cardinality estimation. In English: it counts things. It counts
// things using very small amounts of memory compared to the number of
// objects it is counting.
//
// For a full description of the algorithm, see the paper HyperLogLog:
// the analysis of a near-optimal cardinality estimation algorithm by
// Flajolet, et. al.
package hyperloglog

import (
	"fmt"
	"math"
)

type HyperLogLog struct {
	m         uint    // Number of registers
	b         uint32  // Number of bits used to determine register index
	alpha     float64 // Bias correction constant
	registers []uint8
}

// Compute bias correction alpha_m.
func get_alpha(m uint) (result float64) {
	switch m {
	case 16:
		result = 0.673
	case 32:
		result = 0.697
	case 64:
		result = 0.709
	default:
		result = 0.7213 / (1.0 + 1.079/float64(m))
	}
	return result
}

// Return a new HyperLogLog with the given number of registers. More
// registers leads to lower error in your estimated count, at the
// expense of memory.
//
// Choose a power of two number of registers, depending on the amount
// of memory you're willing to use and the error you're willing to
// tolerate. Each register uses one byte of memory.
//
// Approximate error will be:
//     1.04 / sqrt(registers)
//
func New(registers uint) (*HyperLogLog, error) {
	if registers%2 != 0 {
		return nil, fmt.Errorf("number of registers %d not a power of two", registers)
	}
	h := &HyperLogLog{}
	h.m = registers
	h.b = uint32(math.Ceil(math.Log2(float64(registers))))
	h.alpha = get_alpha(registers)
	h.Reset()
	return h, nil
}

// Reset all internal variables and set the count to zero.
func (h *HyperLogLog) Reset() {
	h.registers = make([]uint8, h.m)
}

// Calculate the position of the leftmost 1-bit.
func rho(val uint32, max uint32) uint8 {
	r := uint32(1)
	for val&0x80000000 == 0 && r <= max {
		r++
		val <<= 1
	}
	return uint8(r)
}

// Add to the count. val should be a 32 bit unsigned integer from a
// good hash function.
func (h *HyperLogLog) Add(val uint32) {
	k := 32 - h.b
	r := rho(val<<h.b, k)
	j := val >> uint(k)
	if r > h.registers[j] {
		h.registers[j] = r
	}
}

// Get the estimated count.
func (h *HyperLogLog) Count() uint64 {
	sum := 0.0
	for _, val := range h.registers {
		sum += 1 / math.Pow(2, float64(val))
	}
	estimate := h.alpha * float64(h.m*h.m) / sum
	pow_2_32 := math.Pow(2, 32)
	if estimate <= 5.0/2.0*float64(h.m) {
		// Small range correction
		v := 0
		for _, r := range h.registers {
			if r == 0 {
				v++
			}
		}
		if v > 0 {
			estimate = float64(h.m) * math.Log(float64(h.m)/float64(v))
		}
	} else if estimate > 1.0/30.0*pow_2_32 {
		// Large range correction
		estimate = -pow_2_32 * math.Log(1-estimate/pow_2_32)
	}
	return uint64(estimate)
}
<commit_msg>Ensure all division of floats is actually dividing floats<commit_after>// Package hyperloglog implements the HyperLogLog algorithm for
// cardinality estimation. In English: it counts things. It counts
// things using very small amounts of memory compared to the number of
// objects it is counting.
//
// For a full description of the algorithm, see the paper HyperLogLog:
// the analysis of a near-optimal cardinality estimation algorithm by
// Flajolet, et. al.
package hyperloglog

import (
	"fmt"
	"math"
)

const (
	pow_2_32     = 4294967296.0
	neg_pow_2_32 = -4294967296.0
)

type HyperLogLog struct {
	m         uint    // Number of registers
	b         uint32  // Number of bits used to determine register index
	alpha     float64 // Bias correction constant
	registers []uint8
}

// Compute bias correction alpha_m.
func get_alpha(m uint) (result float64) {
	switch m {
	case 16:
		result = 0.673
	case 32:
		result = 0.697
	case 64:
		result = 0.709
	default:
		result = 0.7213 / (1.0 + 1.079/float64(m))
	}
	return result
}

// Return a new HyperLogLog with the given number of registers. More
// registers leads to lower error in your estimated count, at the
// expense of memory.
//
// Choose a power of two number of registers, depending on the amount
// of memory you're willing to use and the error you're willing to
// tolerate. Each register uses one byte of memory.
//
// Approximate error will be:
//     1.04 / sqrt(registers)
//
func New(registers uint) (*HyperLogLog, error) {
	if registers%2 != 0 {
		return nil, fmt.Errorf("number of registers %d not a power of two", registers)
	}
	h := &HyperLogLog{}
	h.m = registers
	h.b = uint32(math.Ceil(math.Log2(float64(registers))))
	h.alpha = get_alpha(registers)
	h.Reset()
	return h, nil
}

// Reset all internal variables and set the count to zero.
func (h *HyperLogLog) Reset() {
	h.registers = make([]uint8, h.m)
}

// Calculate the position of the leftmost 1-bit.
func rho(val uint32, max uint32) uint8 {
	r := uint32(1)
	for val&0x80000000 == 0 && r <= max {
		r++
		val <<= 1
	}
	return uint8(r)
}

// Add to the count. val should be a 32 bit unsigned integer from a
// good hash function.
func (h *HyperLogLog) Add(val uint32) {
	k := 32 - h.b
	r := rho(val<<h.b, k)
	j := val >> uint(k)
	if r > h.registers[j] {
		h.registers[j] = r
	}
}

// Get the estimated count.
func (h *HyperLogLog) Count() uint64 {
	sum := 0.0
	for _, val := range h.registers {
		sum += 1.0 / math.Pow(2.0, float64(val))
	}
	estimate := h.alpha * float64(h.m*h.m) / sum
	if estimate <= 5.0/2.0*float64(h.m) {
		// Small range correction
		v := 0
		for _, r := range h.registers {
			if r == 0 {
				v++
			}
		}
		if v > 0 {
			estimate = float64(h.m) * math.Log(float64(h.m)/float64(v))
		}
	} else if estimate > 1.0/30.0*pow_2_32 {
		// Large range correction
		estimate = neg_pow_2_32 * math.Log(1-estimate/pow_2_32)
	}
	return uint64(estimate)
}
<|endoftext|>


<commit_before>package auth

import (
  "github.com/stretchr/testify/assert"
  "testing"
)

func TestLogin(t *testing.T) {
  htpasswdProvider := &HtpasswdProvider{
    File:NewHtpasswdFile("../test/htpasswd.test"),
  }
  token, err := htpasswdProvider.Login("foo", "bar")
  assert.Nil(t, err)
  assert.NotNil(t, token)
}

func TestLoginInvalid(t *testing.T) {
  htpasswdProvider := &HtpasswdProvider{
    File:NewHtpasswdFile("../test/htpasswd.test"),
  }
  token, err := htpasswdProvider.Login("bar", "foo")
  assert.Error(t, err)
  assert.Equal(t, token, "")
}
<commit_msg>Also test NewHtpasswdProvider<commit_after>package auth

import (
  "github.com/stretchr/testify/assert"
  "testing"
)

func TestLogin(t *testing.T) {
  htpasswdProvider := NewHtpasswdProvider("../test/htpasswd.test")
  token, err := htpasswdProvider.Login("foo", "bar")
  assert.Nil(t, err)
  assert.NotNil(t, token)
}

func TestLoginInvalid(t *testing.T) {
  htpasswdProvider := &HtpasswdProvider{
    File:NewHtpasswdFile("../test/htpasswd.test"),
  }
  token, err := htpasswdProvider.Login("bar", "foo")
  assert.Error(t, err)
  assert.Equal(t, token, "")
}
<|endoftext|>


<commit_before>// config helper for cache, mq, and worker
package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

// Contains the configuration for an iron.io service.
// An empty instance is not usable
type Settings struct {
	Token      string `json:"token,omitempty"`
	ProjectId  string `json:"project_id,omitempty"`
	Host       string `json:"host,omitempty"`
	Scheme     string `json:"scheme,omitempty"`
	Port       uint16 `json:"port,omitempty"`
	ApiVersion string `json:"api_version,omitempty"`
	UserAgent  string `json:"user_agent,omitempty"`
}

var (
	debug     = false
	goVersion = runtime.Version()
	Presets   = map[string]Settings{
		"worker": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "2",
			Host:       "worker-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/worker 2.0 (Go " + goVersion + ")",
		},
		"mq": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       "mq-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/mq 1.0 (Go " + goVersion + ")",
		},
		"cache": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       "cache-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/cache 1.0 (Go " + goVersion + ")",
		},
	}
)

func dbg(v ...interface{}) {
	if debug {
		fmt.Fprintln(os.Stderr, v...)
	}
}

// ManualConfig gathers configuration from env variables, json config files
// and finally overwrites it with specified instance of Settings.
func ManualConfig(fullProduct string, configuration *Settings) (settings Settings) {
	return config(fullProduct, "", configuration)
}

// Config gathers configuration from env variables and json config files.
// Examples of fullProduct are "iron_worker", "iron_cache", "iron_mq".
func Config(fullProduct string) (settings Settings) {
	return config(fullProduct, "", nil)
}

// Like Config, but useful for keeping multiple dev environment information in
// one iron.json config file. If env="", works same as Config.
//
// e.g.
//    {
//        "production": {
//            "token": ...,
//            "project_id": ...
//        },
//        "test": {
//            ...
//        }
//    }
func ConfigWithEnv(fullProduct, env string) (settings Settings) {
	return config(fullProduct, env, nil)
}

func config(fullProduct, env string, configuration *Settings) Settings {
	if os.Getenv("IRON_CONFIG_DEBUG") != "" {
		debug = true
		dbg("debugging of config enabled")
	}
	pair := strings.SplitN(fullProduct, "_", 2)
	if len(pair) != 2 {
		panic("Invalid product name, has to use prefix.")
	}
	family, product := pair[0], pair[1]

	base, found := Presets[product]

	if !found {
		base = Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       product + "-aws-us-east-1.iron.io",
			UserAgent:  "iron_go",
		}
	}

	base.globalConfig(family, product, env)
	base.globalEnv(family, product)
	base.productEnv(family, product)
	base.localConfig(family, product, env)
	base.manualConfig(configuration)

	return base
}

func (s *Settings) globalConfig(family, product, env string) {
	u, err := user.Current()
	if err != nil {
		fmt.Println("Error getting user.Current():", err)
		return

	}
	//	fmt.Println("Homedir:", u.HomeDir)
	path := filepath.Join(u.HomeDir, ".iron.json")
	s.UseConfigFile(family, product, path, env)
}

// The environment variables the scheme looks for are all of the same formula:
// the camel-cased product name is switched to an underscore (“IronWorker”
// becomes “iron_worker”) and converted to be all capital letters. For the
// global environment variables, “IRON” is used by itself. The value being
// loaded is then joined by an underscore to the name, and again capitalised.
// For example, to retrieve the OAuth token, the client looks for “IRON_TOKEN”.
func (s *Settings) globalEnv(family, product string) {
	eFamily := strings.ToUpper(family) + "_"
	s.commonEnv(eFamily)
}

// In the case of product-specific variables (which override global variables),
// it would be “IRON_WORKER_TOKEN” (for IronWorker).
func (s *Settings) productEnv(family, product string) {
	eProduct := strings.ToUpper(family) + "_" + strings.ToUpper(product) + "_"
	s.commonEnv(eProduct)
}

func (s *Settings) localConfig(family, product, env string) {
	s.UseConfigFile(family, product, "iron.json", env)
}

func (s *Settings) manualConfig(settings *Settings) {
	if settings != nil {
		s.UseSettings(settings)
	}
}

func (s *Settings) commonEnv(prefix string) {
	if token := os.Getenv(prefix + "TOKEN"); token != "" {
		s.Token = token
		dbg("env has TOKEN:", s.Token)
	}
	if pid := os.Getenv(prefix + "PROJECT_ID"); pid != "" {
		s.ProjectId = pid
		dbg("env has PROJECT_ID:", s.ProjectId)
	}
	if host := os.Getenv(prefix + "HOST"); host != "" {
		s.Host = host
		dbg("env has HOST:", s.Host)
	}
	if scheme := os.Getenv(prefix + "SCHEME"); scheme != "" {
		s.Scheme = scheme
		dbg("env has SCHEME:", s.Scheme)
	}
	if port := os.Getenv(prefix + "PORT"); port != "" {
		n, err := strconv.ParseUint(port, 10, 16)
		if err != nil {
			panic(err)
		}
		s.Port = uint16(n)
		dbg("env has PORT:", s.Port)
	}
	if vers := os.Getenv(prefix + "API_VERSION"); vers != "" {
		s.ApiVersion = vers
		dbg("env has API_VERSION:", s.ApiVersion)
	}
}

// Load and merge the given JSON config file.
func (s *Settings) UseConfigFile(family, product, path, env string) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		dbg("tried to", err, ": skipping")
		return
	}

	data := map[string]interface{}{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		panic("Invalid JSON in " + path + ": " + err.Error())
	}

	dbg("config in", path, "found")

	if env != "" {
		data = data[env].(map[string]interface{})
	}
	s.UseConfigMap(data)

	ipData, found := data[family+"_"+product]
	if found {
		pData := ipData.(map[string]interface{})
		s.UseConfigMap(pData)
	}
}

// Merge the given data into the settings.
func (s *Settings) UseConfigMap(data map[string]interface{}) {
	if token, found := data["token"]; found {
		s.Token = token.(string)
		dbg("config has token:", s.Token)
	}
	if projectId, found := data["project_id"]; found {
		s.ProjectId = projectId.(string)
		dbg("config has project_id:", s.ProjectId)
	}
	if host, found := data["host"]; found {
		s.Host = host.(string)
		dbg("config has host:", s.Host)
	}
	if prot, found := data["scheme"]; found {
		s.Scheme = prot.(string)
		dbg("config has scheme:", s.Scheme)
	}
	if port, found := data["port"]; found {
		s.Port = uint16(port.(float64))
		dbg("config has port:", s.Port)
	}
	if vers, found := data["api_version"]; found {
		s.ApiVersion = vers.(string)
		dbg("config has api_version:", s.ApiVersion)
	}
	if agent, found := data["user_agent"]; found {
		s.UserAgent = agent.(string)
		dbg("config has user_agent:", s.UserAgent)
	}
}

// Merge the given instance into the settings.
func (s *Settings) UseSettings(settings *Settings) {
	if settings.Token != "" {
		s.Token = settings.Token
	}
	if settings.ProjectId != "" {
		s.ProjectId = settings.ProjectId
	}
	if settings.Host != "" {
		s.Host = settings.Host
	}
	if settings.Scheme != "" {
		s.Scheme = settings.Scheme
	}
	if settings.ApiVersion != "" {
		s.ApiVersion = settings.ApiVersion
	}
	if settings.UserAgent != "" {
		s.UserAgent = settings.UserAgent
	}
	if settings.Port > 0 {
		s.Port = settings.Port
	}
}
<commit_msg>fail more gracefully if env specified but not found<commit_after>// config helper for cache, mq, and worker
package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

// Contains the configuration for an iron.io service.
// An empty instance is not usable
type Settings struct {
	Token      string `json:"token,omitempty"`
	ProjectId  string `json:"project_id,omitempty"`
	Host       string `json:"host,omitempty"`
	Scheme     string `json:"scheme,omitempty"`
	Port       uint16 `json:"port,omitempty"`
	ApiVersion string `json:"api_version,omitempty"`
	UserAgent  string `json:"user_agent,omitempty"`
}

var (
	debug     = false
	goVersion = runtime.Version()
	Presets   = map[string]Settings{
		"worker": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "2",
			Host:       "worker-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/worker 2.0 (Go " + goVersion + ")",
		},
		"mq": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       "mq-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/mq 1.0 (Go " + goVersion + ")",
		},
		"cache": Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       "cache-aws-us-east-1.iron.io",
			UserAgent:  "iron_go/cache 1.0 (Go " + goVersion + ")",
		},
	}
)

func dbg(v ...interface{}) {
	if debug {
		fmt.Fprintln(os.Stderr, v...)
	}
}

// ManualConfig gathers configuration from env variables, json config files
// and finally overwrites it with specified instance of Settings.
func ManualConfig(fullProduct string, configuration *Settings) (settings Settings) {
	return config(fullProduct, "", configuration)
}

// Config gathers configuration from env variables and json config files.
// Examples of fullProduct are "iron_worker", "iron_cache", "iron_mq".
func Config(fullProduct string) (settings Settings) {
	return config(fullProduct, "", nil)
}

// Like Config, but useful for keeping multiple dev environment information in
// one iron.json config file. If env="", works same as Config.
//
// e.g.
//    {
//        "production": {
//            "token": ...,
//            "project_id": ...
//        },
//        "test": {
//            ...
//        }
//    }
func ConfigWithEnv(fullProduct, env string) (settings Settings) {
	return config(fullProduct, env, nil)
}

func config(fullProduct, env string, configuration *Settings) Settings {
	if os.Getenv("IRON_CONFIG_DEBUG") != "" {
		debug = true
		dbg("debugging of config enabled")
	}
	pair := strings.SplitN(fullProduct, "_", 2)
	if len(pair) != 2 {
		panic("Invalid product name, has to use prefix.")
	}
	family, product := pair[0], pair[1]

	base, found := Presets[product]

	if !found {
		base = Settings{
			Scheme:     "https",
			Port:       443,
			ApiVersion: "1",
			Host:       product + "-aws-us-east-1.iron.io",
			UserAgent:  "iron_go",
		}
	}

	base.globalConfig(family, product, env)
	base.globalEnv(family, product)
	base.productEnv(family, product)
	base.localConfig(family, product, env)
	base.manualConfig(configuration)

	return base
}

func (s *Settings) globalConfig(family, product, env string) {
	u, err := user.Current()
	if err != nil {
		fmt.Println("Error getting user.Current():", err)
		return

	}
	//	fmt.Println("Homedir:", u.HomeDir)
	path := filepath.Join(u.HomeDir, ".iron.json")
	s.UseConfigFile(family, product, path, env)
}

// The environment variables the scheme looks for are all of the same formula:
// the camel-cased product name is switched to an underscore (“IronWorker”
// becomes “iron_worker”) and converted to be all capital letters. For the
// global environment variables, “IRON” is used by itself. The value being
// loaded is then joined by an underscore to the name, and again capitalised.
// For example, to retrieve the OAuth token, the client looks for “IRON_TOKEN”.
func (s *Settings) globalEnv(family, product string) {
	eFamily := strings.ToUpper(family) + "_"
	s.commonEnv(eFamily)
}

// In the case of product-specific variables (which override global variables),
// it would be “IRON_WORKER_TOKEN” (for IronWorker).
func (s *Settings) productEnv(family, product string) {
	eProduct := strings.ToUpper(family) + "_" + strings.ToUpper(product) + "_"
	s.commonEnv(eProduct)
}

func (s *Settings) localConfig(family, product, env string) {
	s.UseConfigFile(family, product, "iron.json", env)
}

func (s *Settings) manualConfig(settings *Settings) {
	if settings != nil {
		s.UseSettings(settings)
	}
}

func (s *Settings) commonEnv(prefix string) {
	if token := os.Getenv(prefix + "TOKEN"); token != "" {
		s.Token = token
		dbg("env has TOKEN:", s.Token)
	}
	if pid := os.Getenv(prefix + "PROJECT_ID"); pid != "" {
		s.ProjectId = pid
		dbg("env has PROJECT_ID:", s.ProjectId)
	}
	if host := os.Getenv(prefix + "HOST"); host != "" {
		s.Host = host
		dbg("env has HOST:", s.Host)
	}
	if scheme := os.Getenv(prefix + "SCHEME"); scheme != "" {
		s.Scheme = scheme
		dbg("env has SCHEME:", s.Scheme)
	}
	if port := os.Getenv(prefix + "PORT"); port != "" {
		n, err := strconv.ParseUint(port, 10, 16)
		if err != nil {
			panic(err)
		}
		s.Port = uint16(n)
		dbg("env has PORT:", s.Port)
	}
	if vers := os.Getenv(prefix + "API_VERSION"); vers != "" {
		s.ApiVersion = vers
		dbg("env has API_VERSION:", s.ApiVersion)
	}
}

// Load and merge the given JSON config file.
func (s *Settings) UseConfigFile(family, product, path, env string) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		dbg("tried to", err, ": skipping")
		return
	}

	data := map[string]interface{}{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		panic("Invalid JSON in " + path + ": " + err.Error())
	}

	dbg("config in", path, "found")

	if env != "" {
		envdata, ok := data[env].(map[string]interface{})
		if !ok {
			return // bail, they specified an env but we couldn't find one, so error out.
		}
		data = envdata
	}
	s.UseConfigMap(data)

	ipData, found := data[family+"_"+product]
	if found {
		pData := ipData.(map[string]interface{})
		s.UseConfigMap(pData)
	}
}

// Merge the given data into the settings.
func (s *Settings) UseConfigMap(data map[string]interface{}) {
	if token, found := data["token"]; found {
		s.Token = token.(string)
		dbg("config has token:", s.Token)
	}
	if projectId, found := data["project_id"]; found {
		s.ProjectId = projectId.(string)
		dbg("config has project_id:", s.ProjectId)
	}
	if host, found := data["host"]; found {
		s.Host = host.(string)
		dbg("config has host:", s.Host)
	}
	if prot, found := data["scheme"]; found {
		s.Scheme = prot.(string)
		dbg("config has scheme:", s.Scheme)
	}
	if port, found := data["port"]; found {
		s.Port = uint16(port.(float64))
		dbg("config has port:", s.Port)
	}
	if vers, found := data["api_version"]; found {
		s.ApiVersion = vers.(string)
		dbg("config has api_version:", s.ApiVersion)
	}
	if agent, found := data["user_agent"]; found {
		s.UserAgent = agent.(string)
		dbg("config has user_agent:", s.UserAgent)
	}
}

// Merge the given instance into the settings.
func (s *Settings) UseSettings(settings *Settings) {
	if settings.Token != "" {
		s.Token = settings.Token
	}
	if settings.ProjectId != "" {
		s.ProjectId = settings.ProjectId
	}
	if settings.Host != "" {
		s.Host = settings.Host
	}
	if settings.Scheme != "" {
		s.Scheme = settings.Scheme
	}
	if settings.ApiVersion != "" {
		s.ApiVersion = settings.ApiVersion
	}
	if settings.UserAgent != "" {
		s.UserAgent = settings.UserAgent
	}
	if settings.Port > 0 {
		s.Port = settings.Port
	}
}
<|endoftext|>