<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package openstack

import (
	"errors"

	"github.com/golang/glog"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack"
	"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/types"
)

type Instances struct {
	compute            *gophercloud.ServiceClient
	flavor_to_resource map[string]*api.NodeResources // keyed by flavor id
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
		Region: os.region,
	})
	if err != nil {
		glog.Warningf("Failed to find compute endpoint: %v", err)
		return nil, false
	}

	pager := flavors.ListDetail(compute, nil)

	flavor_to_resource := make(map[string]*api.NodeResources)
	err = pager.EachPage(func(page pagination.Page) (bool, error) {
		flavorList, err := flavors.ExtractFlavors(page)
		if err != nil {
			return false, err
		}
		for _, flavor := range flavorList {
			rsrc := api.NodeResources{
				Capacity: api.ResourceList{
					api.ResourceCPU:            *resource.NewQuantity(int64(flavor.VCPUs), resource.DecimalSI),
					api.ResourceMemory:         *resource.NewQuantity(int64(flavor.RAM)*MiB, resource.BinarySI),
					"openstack.org/disk":       *resource.NewQuantity(int64(flavor.Disk)*GB, resource.DecimalSI),
					"openstack.org/rxTxFactor": *resource.NewMilliQuantity(int64(flavor.RxTxFactor)*1000, resource.DecimalSI),
					"openstack.org/swap":       *resource.NewQuantity(int64(flavor.Swap)*MiB, resource.BinarySI),
				},
			}
			flavor_to_resource[flavor.ID] = &rsrc
		}
		return true, nil
	})
	if err != nil {
		glog.Warningf("Failed to find compute flavors: %v", err)
		return nil, false
	}

	glog.V(3).Infof("Found %v compute flavors", len(flavor_to_resource))
	glog.V(1).Info("Claiming to support Instances")

	return &Instances{compute, flavor_to_resource}, true
}

func (i *Instances) List(name_filter string) ([]types.NodeName, error) {
	glog.V(4).Infof("openstack List(%v) called", name_filter)

	opts := servers.ListOpts{
		Name:   name_filter,
		Status: "ACTIVE",
	}
	pager := servers.List(i.compute, opts)

	ret := make([]types.NodeName, 0)
	err := pager.EachPage(func(page pagination.Page) (bool, error) {
		sList, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		for i := range sList {
			ret = append(ret, mapServerToNodeName(&sList[i]))
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %v instances matching %v: %v",
		len(ret), name_filter, ret)

	return ret, nil
}

// Implementation of Instances.CurrentNodeName
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	return types.NodeName(hostname), nil
}

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

func (i *Instances) NodeAddresses(name types.NodeName) ([]api.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	return "", nil
}
<commit_msg>openstack: Return instance name in CurrentNodeName<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package openstack

import (
	"errors"

	"github.com/golang/glog"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack"
	"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/types"
)

type Instances struct {
	compute            *gophercloud.ServiceClient
	flavor_to_resource map[string]*api.NodeResources // keyed by flavor id
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
		Region: os.region,
	})
	if err != nil {
		glog.Warningf("Failed to find compute endpoint: %v", err)
		return nil, false
	}

	pager := flavors.ListDetail(compute, nil)

	flavor_to_resource := make(map[string]*api.NodeResources)
	err = pager.EachPage(func(page pagination.Page) (bool, error) {
		flavorList, err := flavors.ExtractFlavors(page)
		if err != nil {
			return false, err
		}
		for _, flavor := range flavorList {
			rsrc := api.NodeResources{
				Capacity: api.ResourceList{
					api.ResourceCPU:            *resource.NewQuantity(int64(flavor.VCPUs), resource.DecimalSI),
					api.ResourceMemory:         *resource.NewQuantity(int64(flavor.RAM)*MiB, resource.BinarySI),
					"openstack.org/disk":       *resource.NewQuantity(int64(flavor.Disk)*GB, resource.DecimalSI),
					"openstack.org/rxTxFactor": *resource.NewMilliQuantity(int64(flavor.RxTxFactor)*1000, resource.DecimalSI),
					"openstack.org/swap":       *resource.NewQuantity(int64(flavor.Swap)*MiB, resource.BinarySI),
				},
			}
			flavor_to_resource[flavor.ID] = &rsrc
		}
		return true, nil
	})
	if err != nil {
		glog.Warningf("Failed to find compute flavors: %v", err)
		return nil, false
	}

	glog.V(3).Infof("Found %v compute flavors", len(flavor_to_resource))
	glog.V(1).Info("Claiming to support Instances")

	return &Instances{compute, flavor_to_resource}, true
}

func (i *Instances) List(name_filter string) ([]types.NodeName, error) {
	glog.V(4).Infof("openstack List(%v) called", name_filter)

	opts := servers.ListOpts{
		Name:   name_filter,
		Status: "ACTIVE",
	}
	pager := servers.List(i.compute, opts)

	ret := make([]types.NodeName, 0)
	err := pager.EachPage(func(page pagination.Page) (bool, error) {
		sList, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		for i := range sList {
			ret = append(ret, mapServerToNodeName(&sList[i]))
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %v instances matching %v: %v",
		len(ret), name_filter, ret)

	return ret, nil
}

// Implementation of Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	md, err := getMetadata()
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Name), nil
}

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

func (i *Instances) NodeAddresses(name types.NodeName) ([]api.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	return "", nil
}
<|endoftext|>


<commit_before>package atlas

import (
	"errors"
	"github.com/bndr/gopencils"
	"log"
	"fmt"
)

var (
	allTypes = []string{
		"dns",
		"http",
		"ntp",
		"ping",
		"sslcert",
		"traceroute",
		"wifi",
	}
)

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

var ErrInvalidAPIKey = errors.New("invalid API key")

// -- private

// checkType verify that the type is valid
func checkType(d Definition) (valid bool) {
	valid = false
	for _, t := range allTypes {
		if d.Type == t {
			valid = true
			break
		}
	}
	return
}

// checkTypeAs is a shortcut
func checkTypeAs(d Definition, t string) bool {
	valid := checkType(d)
	return valid && d.Type == t
}

// checkAllTypesAs is a generalization of checkTypeAs
func checkAllTypesAs(dl []Definition, t string) (valid bool) {
	valid = true
	for _, d := range dl {
		if d.Type != t {
			valid = false
			break
		}
	}
	return
}

// measurementList is our main answer
type measurementList struct {
	Count    int
	Next     string
	Previous string
	Results  []Measurement
}

// fetch the given resource
func fetchOneMeasurementPage(api *gopencils.Resource, opts map[string]string) (raw *measurementList, err error) {
	r, err := api.Res("measurements", &raw).Get(opts)
	if err != nil {
		log.Printf("err: %v", err)
		err = fmt.Errorf("%v - r:%v\n", err, r)
	}
	//log.Printf(">> rawlist=%+v r=%+v Next=|%s|", rawlist, r, rawlist.Next)
	return
}

// -- public

// GetMeasurement gets info for a single one
func GetMeasurement(id int) (m *Measurement, err error) {
	key, ok := HasAPIKey()
	api := gopencils.Api(apiEndpoint, nil)

	// Add at least one option, the APIkey if present
	var opts map[string]string

	if ok {
		opts["key"] = key
	}

	r, err := api.Res("measurements").Id(id, &m).Get(opts)
	if err != nil {
		err = fmt.Errorf("%v - r:%#v\n", err, r.Raw)
		return
	}
	return
}

// GetMeasurements gets info for a set
func GetMeasurements(opts map[string]string) (m []Measurement, err error) {
	key, ok := HasAPIKey()
	api := gopencils.Api(apiEndpoint, nil)

	// Add at least one option, the APIkey if present
	if ok {
		opts["key"] = key
	}

	rawlist, err := fetchOneMeasurementPage(api, opts)

	// Empty answer
	if rawlist.Count == 0 {
		return nil, fmt.Errorf("empty measurement list")
	}

	var res []Measurement

	res = append(res, rawlist.Results...)
	if rawlist.Next != "" {
		// We have pagination
		for pn := getPageNum(rawlist.Next); rawlist.Next != ""; pn = getPageNum(rawlist.Next) {
			opts["page"] = pn

			rawlist, err = fetchOneMeasurementPage(api, opts)
			if err != nil {
				return
			}

			res = append(res, rawlist.Results...)
		}
	}
	m = res
	return
}

// DNS creates a measurement
func DNS(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "dns") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// HTTP creates a measurement
func HTTP(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "http") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// NTP creates a measurement
func NTP(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "ntp") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// Ping creates a measurement
func Ping(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "ping") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// SSLCert creates a measurement
func SSLCert(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "sslcert") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// Traceroute creates a measurement
func Traceroute(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "traceroute") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// Measurement-related methods

// Start is for starting a given measurement
func (m *Measurement) Start(id int) (err error) {
	return nil
}

// Stop is for stopping a given measurement
func (m *Measurement) Stop(id int) (err error) {
	return nil
}
<commit_msg>WIP: implement Ping().<commit_after>package atlas

import (
	"errors"
	"github.com/bndr/gopencils"
	"github.com/go-resty/resty"
	"log"
	"fmt"
)

var (
	allTypes = []string{
		"dns",
		"http",
		"ntp",
		"ping",
		"sslcert",
		"traceroute",
		"wifi",
	}
)

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

var ErrInvalidAPIKey = errors.New("invalid API key")

// -- private

// checkType verify that the type is valid
func checkType(d Definition) (valid bool) {
	valid = false
	for _, t := range allTypes {
		if d.Type == t {
			valid = true
			break
		}
	}
	return
}

// checkTypeAs is a shortcut
func checkTypeAs(d Definition, t string) bool {
	valid := checkType(d)
	return valid && d.Type == t
}

// checkAllTypesAs is a generalization of checkTypeAs
func checkAllTypesAs(dl []Definition, t string) (valid bool) {
	valid = true
	for _, d := range dl {
		if d.Type != t {
			valid = false
			break
		}
	}
	return
}

// measurementList is our main answer
type measurementList struct {
	Count    int
	Next     string
	Previous string
	Results  []Measurement
}

// fetch the given resource
func fetchOneMeasurementPage(api *gopencils.Resource, opts map[string]string) (raw *measurementList, err error) {
	r, err := api.Res("measurements", &raw).Get(opts)
	if err != nil {
		log.Printf("err: %v", err)
		err = fmt.Errorf("%v - r:%v\n", err, r)
	}
	//log.Printf(">> rawlist=%+v r=%+v Next=|%s|", rawlist, r, rawlist.Next)
	return
}

// -- public

// GetMeasurement gets info for a single one
func GetMeasurement(id int) (m *Measurement, err error) {
	key, ok := HasAPIKey()
	api := gopencils.Api(apiEndpoint, nil)

	// Add at least one option, the APIkey if present
	var opts map[string]string

	if ok {
		opts["key"] = key
	}

	r, err := api.Res("measurements").Id(id, &m).Get(opts)
	if err != nil {
		err = fmt.Errorf("%v - r:%#v\n", err, r.Raw)
		return
	}
	return
}

// GetMeasurements gets info for a set
func GetMeasurements(opts map[string]string) (m []Measurement, err error) {
	key, ok := HasAPIKey()
	api := gopencils.Api(apiEndpoint, nil)

	// Add at least one option, the APIkey if present
	if ok {
		opts["key"] = key
	}

	rawlist, err := fetchOneMeasurementPage(api, opts)

	// Empty answer
	if rawlist.Count == 0 {
		return nil, fmt.Errorf("empty measurement list")
	}

	var res []Measurement

	res = append(res, rawlist.Results...)
	if rawlist.Next != "" {
		// We have pagination
		for pn := getPageNum(rawlist.Next); rawlist.Next != ""; pn = getPageNum(rawlist.Next) {
			opts["page"] = pn

			rawlist, err = fetchOneMeasurementPage(api, opts)
			if err != nil {
				return
			}

			res = append(res, rawlist.Results...)
		}
	}
	m = res
	return
}

// DNS creates a measurement
func DNS(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "dns") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// HTTP creates a measurement
func HTTP(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "http") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// NTP creates a measurement
func NTP(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "ntp") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

type pingResp struct {
	Measurements []int
}

type pingError struct {
	Error struct {
			  Status int
			  Code   int
			  Detail string
			  Title  string
		  }
}

// Ping creates a measurement
func Ping(d MeasurementRequest) (m pingResp, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "ping") {
		err = ErrInvalidMeasurementType
		return
	}

	//api := gopencils.Api(apiEndpoint, nil)
	//fmt.Printf("api: %#v\n", api)

	// Add at least one option, the APIkey if present
	var opts = make(map[string]string)

	key, ok := HasAPIKey()
	if ok {
		opts["key"] = key
	} else {
		err = ErrInvalidAPIKey
		return
	}

	var mr pingResp
	var pe pingError

	base := fmt.Sprintf("%s/%s/?key=%s", apiEndpoint, "measurements/ping", key)

	resp, err := resty.R().
					SetBody(d).
					SetResult(&mr).
					SetError(&pe).
					Post(base)

	//r, err := api.Res(base, &resp).Post(d)
	fmt.Printf("mr: %v\nresp: %#v\nd: %v\n\npe: %#v", mr, string(resp.Body()), d, pe)
	if err != nil {
		err = fmt.Errorf("err: %v - mr:%v - pe: %v\n", err, mr, pe)
		return
	}

	m = mr
	return
}

// SSLCert creates a measurement
func SSLCert(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "sslcert") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// Traceroute creates a measurement
func Traceroute(d MeasurementRequest) (m *Measurement, err error) {
	// Check that all Definition.Type are the same and compliant
	if !checkAllTypesAs(d.Definitions, "traceroute") {
		err = ErrInvalidMeasurementType
		return
	}
	return
}

// Measurement-related methods

// Start is for starting a given measurement
func (m *Measurement) Start(id int) (err error) {
	return nil
}

// Stop is for stopping a given measurement
func (m *Measurement) Stop(id int) (err error) {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/fweikert/continuous-integration/metrics/clients"
	"github.com/fweikert/continuous-integration/metrics/metrics"
	"github.com/fweikert/continuous-integration/metrics/publishers"
	"github.com/fweikert/continuous-integration/metrics/service"
)

var (
	projectID             = flag.String("project_id", "bazel-untrusted", "ID of the GCP project.")
	datastoreSettingsName = flag.String("datastore_settings_name", "MetricSettings", "Name of the settings entity in Datastore.")
	testMode              = flag.Bool("test", false, "If true, the service will collect and publish all metrics immediately and only once.")
)

const megaByte = 1024 * 1024

func handleError(metricName string, err error) {
	fmt.Printf("[%s] %v\n", metricName, err)
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "You should try https://bazel.build")
}

func main() {
	flag.Parse()

	settings, err := ReadSettingsFromDatastore(*projectID, *datastoreSettingsName)
	if err != nil {
		log.Fatalf("Could not read settings from Datastore: %v", err)
	}
	pipelines, err := settings.GetPipelineIDs()
	if err != nil {
		log.Fatalf("Could not get Buildkite pipeline IDs from Datastore: %v", err)
	}
	if len(pipelines) == 0 {
		log.Fatalf("No pipelines were specified.")
	}

	/*
		bk, err := clients.CreateBuildkiteClient(settings.BuildkiteApiToken, settings.BuildkiteDebug)
		if err != nil {
			log.Fatalf("Cannot create Buildkite client: %v", err)
		}
	*/

	gcs, err := clients.CreateGcsClient()
	if err != nil {
		log.Fatalf("Cannot create GCS client: %v", err)
	}

	/*
		stackdriverClient, err := clients.CreateStackdriverClient()
		if err != nil {
			log.Fatalf("Cannot create Stackdriver client: %v", err)
		}

		cloudSql, err := publishers.CreateCloudSqlPublisher(settings.CloudSqlUser, settings.CloudSqlPassword, settings.CloudSqlInstance, settings.CloudSqlDatabase, settings.CloudSqlLocalPort)
		if err != nil {
			log.Fatalf("Failed to set up Cloud SQL publisher: %v", err)
		}

		stackdriver := publishers.CreateStackdriverPublisher(stackdriverClient, *projectID)
	*/

	stdout := publishers.CreateStdoutPublisher(publishers.Csv)

	srv := service.CreateService(handleError)

	/*
		// TODO: support multiple organizations
		platformLoad := metrics.CreatePlatformLoad(bk, 100, settings.BuildkiteOrgs...)
		srv.AddMetric(platformLoad, 60, stackdriver)

		buildsPerChange := metrics.CreateBuildsPerChange(bk, 500, pipelines...)
		srv.AddMetric(buildsPerChange, 60, stdout)

		buildSuccess := metrics.CreateBuildSuccess(bk, 200, pipelines...)
		srv.AddMetric(buildSuccess, 60, stdout)
	*/

	flakiness := metrics.CreateFlakiness(gcs, "bazel-buildkite-stats", "flaky-tests-bep", pipelines...)
	srv.AddMetric(flakiness, 60, stdout)

	/*
		pid := &data.PipelineID{Org: "bazel", Slug: "google-bazel-presubmit"}

		macPerformance := metrics.CreateMacPerformance(bk, 20, pid) // TODO: pipelines...)
		srv.AddMetric(macPerformance, 60, stdout)

		pipelinePerformance := metrics.CreatePipelinePerformance(bk, 20, pipelines...)
		srv.AddMetric(pipelinePerformance, 60, stdout)

		platformSignificance := metrics.CreatePlatformSignificance(bk, 100, pipelines...)
		srv.AddMetric(platformSignificance, 24*60, stdout)

		platformUsage := metrics.CreatePlatformUsage(bk, 100, settings.BuildkiteOrgs...)
		srv.AddMetric(platformUsage, 60, stdout)

		releaseDownloads := metrics.CreateReleaseDownloads(settings.GitHubOrg,
			settings.GitHubRepo,
			settings.GitHubApiToken, megaByte)
		srv.AddMetric(releaseDownloads, 12*60, stdout)

		workerAvailability := metrics.CreateWorkerAvailability(bk, settings.BuildkiteOrgs...)
		srv.AddMetric(workerAvailability, 60, stdout)
	*/

	if *testMode {
		log.Println("[Test mode] Running all jobs exactly once...")
		srv.RunJobsOnce()
		os.Exit(0)
	}

	srv.Start()
	http.HandleFunc("/", handleRequest)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>Read HTTP port from the environment<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/fweikert/continuous-integration/metrics/clients"
	"github.com/fweikert/continuous-integration/metrics/metrics"
	"github.com/fweikert/continuous-integration/metrics/publishers"
	"github.com/fweikert/continuous-integration/metrics/service"
)

var (
	projectID             = flag.String("project_id", "bazel-untrusted", "ID of the GCP project.")
	datastoreSettingsName = flag.String("datastore_settings_name", "MetricSettings", "Name of the settings entity in Datastore.")
	testMode              = flag.Bool("test", false, "If true, the service will collect and publish all metrics immediately and only once.")
)

const megaByte = 1024 * 1024

func handleError(metricName string, err error) {
	fmt.Printf("[%s] %v\n", metricName, err)
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "You should try https://bazel.build")
}

func main() {
	flag.Parse()

	settings, err := ReadSettingsFromDatastore(*projectID, *datastoreSettingsName)
	if err != nil {
		log.Fatalf("Could not read settings from Datastore: %v", err)
	}
	pipelines, err := settings.GetPipelineIDs()
	if err != nil {
		log.Fatalf("Could not get Buildkite pipeline IDs from Datastore: %v", err)
	}
	if len(pipelines) == 0 {
		log.Fatalf("No pipelines were specified.")
	}

	/*
		bk, err := clients.CreateBuildkiteClient(settings.BuildkiteApiToken, settings.BuildkiteDebug)
		if err != nil {
			log.Fatalf("Cannot create Buildkite client: %v", err)
		}
	*/

	gcs, err := clients.CreateGcsClient()
	if err != nil {
		log.Fatalf("Cannot create GCS client: %v", err)
	}

	/*
		stackdriverClient, err := clients.CreateStackdriverClient()
		if err != nil {
			log.Fatalf("Cannot create Stackdriver client: %v", err)
		}

		cloudSql, err := publishers.CreateCloudSqlPublisher(settings.CloudSqlUser, settings.CloudSqlPassword, settings.CloudSqlInstance, settings.CloudSqlDatabase, settings.CloudSqlLocalPort)
		if err != nil {
			log.Fatalf("Failed to set up Cloud SQL publisher: %v", err)
		}

		stackdriver := publishers.CreateStackdriverPublisher(stackdriverClient, *projectID)
	*/

	stdout := publishers.CreateStdoutPublisher(publishers.Csv)

	srv := service.CreateService(handleError)

	/*
		// TODO: support multiple organizations
		platformLoad := metrics.CreatePlatformLoad(bk, 100, settings.BuildkiteOrgs...)
		srv.AddMetric(platformLoad, 60, stackdriver)

		buildsPerChange := metrics.CreateBuildsPerChange(bk, 500, pipelines...)
		srv.AddMetric(buildsPerChange, 60, stdout)

		buildSuccess := metrics.CreateBuildSuccess(bk, 200, pipelines...)
		srv.AddMetric(buildSuccess, 60, stdout)
	*/

	flakiness := metrics.CreateFlakiness(gcs, "bazel-buildkite-stats", "flaky-tests-bep", pipelines...)
	srv.AddMetric(flakiness, 60, stdout)

	/*
		pid := &data.PipelineID{Org: "bazel", Slug: "google-bazel-presubmit"}

		macPerformance := metrics.CreateMacPerformance(bk, 20, pid) // TODO: pipelines...)
		srv.AddMetric(macPerformance, 60, stdout)

		pipelinePerformance := metrics.CreatePipelinePerformance(bk, 20, pipelines...)
		srv.AddMetric(pipelinePerformance, 60, stdout)

		platformSignificance := metrics.CreatePlatformSignificance(bk, 100, pipelines...)
		srv.AddMetric(platformSignificance, 24*60, stdout)

		platformUsage := metrics.CreatePlatformUsage(bk, 100, settings.BuildkiteOrgs...)
		srv.AddMetric(platformUsage, 60, stdout)

		releaseDownloads := metrics.CreateReleaseDownloads(settings.GitHubOrg,
			settings.GitHubRepo,
			settings.GitHubApiToken, megaByte)
		srv.AddMetric(releaseDownloads, 12*60, stdout)

		workerAvailability := metrics.CreateWorkerAvailability(bk, settings.BuildkiteOrgs...)
		srv.AddMetric(workerAvailability, 60, stdout)
	*/

	if *testMode {
		log.Println("[Test mode] Running all jobs exactly once...")
		srv.RunJobsOnce()
		os.Exit(0)
	}

	srv.Start()
	http.HandleFunc("/", handleRequest)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
<|endoftext|>


<commit_before>package metrics

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strings"
)

// Combine two maps, with the second one overriding duplicate values.
func combine(original, override map[string]string) map[string]string {
	// We know the size must be at least the length of the existing tag map, but
	// since values can be overridden we cannot assume the length is the sum of
	// both inputs.
	combined := make(map[string]string, len(original))

	for k, v := range original {
		combined[k] = v
	}
	for k, v := range override {
		combined[k] = v
	}

	return combined
}

// cloneTagsWithMap clones the original string slice and appends the new tags in the map
func cloneTagsWithMap(original []string, newTags map[string]string) []string {
	combined := make([]string, len(original)+len(newTags))
	copy(combined, original)

	i := len(original)
	for k, v := range newTags {
		combined[i] = fmt.Sprintf("%s:%s", k, v)
		i++
	}

	return combined
}

// Converts a map to an array of strings like `key:value`.
func mapToStrings(tagMap map[string]string) []string {
	tags := make([]string, 0, len(tagMap))

	for k, v := range tagMap {
		tags = append(tags, fmt.Sprintf("%s:%s", k, v))
	}

	return tags
}

// convertType converts a value into an specific type if possible, otherwise
// panics. The returned interface is guaranteed to cast properly.
func convertType(value interface{}, toType reflect.Type) interface{} {
	v := reflect.Indirect(reflect.ValueOf(value))
	if !v.Type().ConvertibleTo(toType) {
		panic(fmt.Sprintf("cannot convert %v to %v", v.Type(), toType))
	}
	return v.Convert(toType).Interface()
}

// toFloat64 converts a value into a float64 if possible, otherwise panics.
func toFloat64(value interface{}) float64 {
	return convertType(value, reflect.TypeOf(float64(0.0))).(float64)
}

// getBlurb returns a line of text from the given file and line number. Useful
// for additional context in stack traces.
func getBlurb(fname string, lineno int) string {
	file, err := os.Open(fname)
	if err != nil {
		return ""
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	current := 1
	var blurb string
	for scanner.Scan() {
		if current == lineno {
			blurb = strings.Trim(scanner.Text(), " \t")
			break
		}
		current++
	}
	return blurb
}
<commit_msg>refactor(perf): improve tag serialization<commit_after>package metrics

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strings"
)

// Combine two maps, with the second one overriding duplicate values.
func combine(original, override map[string]string) map[string]string {
	// We know the size must be at least the length of the existing tag map, but
	// since values can be overridden we cannot assume the length is the sum of
	// both inputs.
	combined := make(map[string]string, len(original))

	for k, v := range original {
		combined[k] = v
	}
	for k, v := range override {
		combined[k] = v
	}

	return combined
}

// cloneTagsWithMap clones the original string slice and appends the new tags in the map
func cloneTagsWithMap(original []string, newTags map[string]string) []string {
	combined := make([]string, len(original)+len(newTags))
	copy(combined, original)

	i := len(original)
	for k, v := range newTags {
		combined[i] = buildTag(k, v)
		i++
	}

	return combined
}

// Converts a map to an array of strings like `key:value`.
func mapToStrings(tagMap map[string]string) []string {
	tags := make([]string, 0, len(tagMap))

	for k, v := range tagMap {
		tags = append(tags, buildTag(k, v))
	}

	return tags
}

func buildTag(k, v string) string {
	var b strings.Builder
	b.Grow(len(k) + len(v) + 1)
	b.WriteString(k)
	b.WriteByte(':')
	b.WriteString(v)
	return b.String()
}

// convertType converts a value into an specific type if possible, otherwise
// panics. The returned interface is guaranteed to cast properly.
func convertType(value interface{}, toType reflect.Type) interface{} {
	v := reflect.Indirect(reflect.ValueOf(value))
	if !v.Type().ConvertibleTo(toType) {
		panic(fmt.Sprintf("cannot convert %v to %v", v.Type(), toType))
	}
	return v.Convert(toType).Interface()
}

// toFloat64 converts a value into a float64 if possible, otherwise panics.
func toFloat64(value interface{}) float64 {
	return convertType(value, reflect.TypeOf(float64(0.0))).(float64)
}

// getBlurb returns a line of text from the given file and line number. Useful
// for additional context in stack traces.
func getBlurb(fname string, lineno int) string {
	file, err := os.Open(fname)
	if err != nil {
		return ""
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	current := 1
	var blurb string
	for scanner.Scan() {
		if current == lineno {
			blurb = strings.Trim(scanner.Text(), " \t")
			break
		}
		current++
	}
	return blurb
}
<|endoftext|>


<commit_before>package main

import (
	"goim/libs/proto"
	"time"
	"net/http"

	log "github.com/thinkboy/log4go"
	"github.com/nkovacs/go-socket.io"

)

// Initsocket.io listen all tcp.bind and start accept connections.
func InitSocketIO(addrs string, transport []string, accept string) (err error) {
	server,err := socketio.NewServer(transport)
	if(err != nil){
		log.Warn("socketio init err")
	}
	http.Handle("/socket.io/", server)
	log.Info("socketio Serving at ",addrs)
	http.ListenAndServe(addrs, nil))
	key := accept
	go dispatchSocketIOEvent(server,key)

	return err
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func dispatchSocketIOEvent(server *socketio.Server,key string){
	if Debug {
		log.Debug("key: %s start dispatch tcp goroutine", key)
	}
	server.On("connection", func(so socketio.Socket) {
		log.Info("on connection")
		so.Join(key)
		so.On("chat message", func(msg string) {
			log.Info("emit:", so.Emit("chat message", msg))
			so.BroadcastTo(key, "chat message", msg)
		})
		so.On("disconnection", func() {
			log.Info("on disconnect")
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Info("error:", err)
	})
	if Debug {
		log.Debug("key: %s dispatch goroutine exit", key)
	}
	return
}

// auth for goim handshake with client, use rsa & aes.
func (server *Server) authSocketio(ws *socketio.Socket, p *proto.Proto) (key string, rid int32, heartbeat time.Duration, err error) {
	if err = p.ReadWebsocket(ws); err != nil {
		return
	}
	return
}
<commit_msg>Bug Fix.#2<commit_after>package main

import (
	"goim/libs/proto"
	"time"
	"net/http"

	log "github.com/thinkboy/log4go"
	"github.com/nkovacs/go-socket.io"

)

// Initsocket.io listen all tcp.bind and start accept connections.
func InitSocketIO(addrs string, transport []string, accept string) (err error) {
	server,err := socketio.NewServer(transport)
	if(err != nil){
		log.Warn("socketio init err")
	}
	http.Handle("/socket.io/", server)
	log.Info("socketio Serving at ",addrs)
	http.ListenAndServe(addrs, nil)
	key := accept
	go dispatchSocketIOEvent(server,key)

	return err
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func dispatchSocketIOEvent(server *socketio.Server,key string){
	if Debug {
		log.Debug("key: %s start dispatch tcp goroutine", key)
	}
	server.On("connection", func(so socketio.Socket) {
		log.Info("on connection")
		so.Join(key)
		so.On("chat message", func(msg string) {
			log.Info("emit:", so.Emit("chat message", msg))
			so.BroadcastTo(key, "chat message", msg)
		})
		so.On("disconnection", func() {
			log.Info("on disconnect")
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Info("error:", err)
	})
	if Debug {
		log.Debug("key: %s dispatch goroutine exit", key)
	}
	return
}

// auth for goim handshake with client, use rsa & aes.
func (server *Server) authSocketio(ws *socketio.Socket, p *proto.Proto) (key string, rid int32, heartbeat time.Duration, err error) {
	if err = p.ReadWebsocket(ws); err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>package sc

import (
	"context"
	"testing"

	"github.com/atlassian/smith/it"
	smith_v1 "github.com/atlassian/smith/pkg/apis/smith/v1"

	sc_v1a1 "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1alpha1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	api_v1 "k8s.io/client-go/pkg/api/v1"
)

func TestServiceCatalog(t *testing.T) {
	instance := &sc_v1a1.ServiceInstance{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       "ServiceInstance",
			APIVersion: sc_v1a1.SchemeGroupVersion.String(),
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "instance1",
		},
		Spec: sc_v1a1.ServiceInstanceSpec{
			ServiceClassName: "user-provided-service",
			PlanName:         "default",
		},
	}
	binding := &sc_v1a1.ServiceInstanceCredential{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       "ServiceInstanceCredential",
			APIVersion: sc_v1a1.SchemeGroupVersion.String(),
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "binding1",
		},
		Spec: sc_v1a1.ServiceInstanceCredentialSpec{
			ServiceInstanceRef: api_v1.LocalObjectReference{
				Name: instance.Name,
			},
			SecretName: "secret1",
		},
	}
	bundle := &smith_v1.Bundle{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       smith_v1.BundleResourceKind,
			APIVersion: smith_v1.BundleResourceGroupVersion,
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "bundle-cs",
		},
		Spec: smith_v1.BundleSpec{
			Resources: []smith_v1.Resource{
				{
					Name: smith_v1.ResourceName(instance.Name),
					Spec: instance,
				},
				{
					Name:      smith_v1.ResourceName(binding.Name),
					DependsOn: []smith_v1.ResourceName{smith_v1.ResourceName(instance.Name)},
					Spec:      binding,
				},
			},
		},
	}
	it.SetupApp(t, bundle, true, true, testServiceCatalog)
}

func testServiceCatalog(t *testing.T, ctx context.Context, cfg *it.ItConfig, args ...interface{}) {
	cfg.AssertBundleTimeout(ctx, cfg.Bundle, "")
}
<commit_msg>Parallel test<commit_after>package sc

import (
	"context"
	"testing"

	"github.com/atlassian/smith/it"
	smith_v1 "github.com/atlassian/smith/pkg/apis/smith/v1"

	sc_v1a1 "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1alpha1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	api_v1 "k8s.io/client-go/pkg/api/v1"
)

func TestServiceCatalog(t *testing.T) {
	t.Parallel()
	instance := &sc_v1a1.ServiceInstance{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       "ServiceInstance",
			APIVersion: sc_v1a1.SchemeGroupVersion.String(),
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "instance1",
		},
		Spec: sc_v1a1.ServiceInstanceSpec{
			ServiceClassName: "user-provided-service",
			PlanName:         "default",
		},
	}
	binding := &sc_v1a1.ServiceInstanceCredential{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       "ServiceInstanceCredential",
			APIVersion: sc_v1a1.SchemeGroupVersion.String(),
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "binding1",
		},
		Spec: sc_v1a1.ServiceInstanceCredentialSpec{
			ServiceInstanceRef: api_v1.LocalObjectReference{
				Name: instance.Name,
			},
			SecretName: "secret1",
		},
	}
	bundle := &smith_v1.Bundle{
		TypeMeta: meta_v1.TypeMeta{
			Kind:       smith_v1.BundleResourceKind,
			APIVersion: smith_v1.BundleResourceGroupVersion,
		},
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "bundle-cs",
		},
		Spec: smith_v1.BundleSpec{
			Resources: []smith_v1.Resource{
				{
					Name: smith_v1.ResourceName(instance.Name),
					Spec: instance,
				},
				{
					Name:      smith_v1.ResourceName(binding.Name),
					DependsOn: []smith_v1.ResourceName{smith_v1.ResourceName(instance.Name)},
					Spec:      binding,
				},
			},
		},
	}
	it.SetupApp(t, bundle, true, true, testServiceCatalog)
}

func testServiceCatalog(t *testing.T, ctx context.Context, cfg *it.ItConfig, args ...interface{}) {
	cfg.AssertBundleTimeout(ctx, cfg.Bundle, "")
}
<|endoftext|>


<commit_before>package main

import (
  "path/filepath"
  "fmt"
  "time"
  "strconv"
  "github.com/bdon/jklmnt/nextbus"
  "github.com/bdon/jklmnt/state"
  "encoding/json"
)

func main() {
  const longForm = "2006-01-02 15:04:05 -0700 MST"
  t1, _ := time.Parse(longForm, "2013-08-26 06:00:01 -0700 PDT")
  t2, _ := time.Parse(longForm, "2013-08-27 03:00:01 -0700 PDT")

  list, _ := filepath.Glob("/Volumes/shrub/njudahdata/N/*.xml")
  relevantFiles := []string{}
  for _, entry := range list {
    extension := filepath.Ext(entry)
    filename := filepath.Base(entry)
    var unixstamp = filename[0:len(filename)-len(extension)]
    theint, _ := strconv.ParseInt(unixstamp, 10, 64)
    theTime := time.Unix(theint, 0)
    if theTime.After(t1) && theTime.Before(t2) {
      relevantFiles = append(relevantFiles, entry)
    }
  }

  // now we only have files for 8/26-8/27

  stat := state.NewSystemState()
  for _, entry := range relevantFiles {
    extension := filepath.Ext(entry)
    filename := filepath.Base(entry)
    var unixstamp = filename[0:len(filename)-len(extension)]
    theint, _ := strconv.ParseInt(unixstamp, 10, 64)
    resp := nextbus.ResponseFromFileWithReferencer(entry, stat.Referencer, int(theint))
    stat.AddResponse(resp, int(theint))
  }

  result, _ := json.Marshal(stat.Map)
  fmt.Println(string(result))
}

<commit_msg>output as array<commit_after>package main

import (
  "path/filepath"
  "fmt"
  "time"
  "strconv"
  "github.com/bdon/jklmnt/nextbus"
  "github.com/bdon/jklmnt/state"
  "encoding/json"
)

type ArrEntry struct {
  VehicleId string `json:"vehicle_id"`
  States []state.VehicleState `json:"states"`
}

func main() {
  const longForm = "2006-01-02 15:04:05 -0700 MST"
  t1, _ := time.Parse(longForm, "2013-08-26 06:00:01 -0700 PDT")
  t2, _ := time.Parse(longForm, "2013-08-27 03:00:01 -0700 PDT")

  list, _ := filepath.Glob("/Volumes/shrub/njudahdata/N/*.xml")
  relevantFiles := []string{}
  for _, entry := range list {
    extension := filepath.Ext(entry)
    filename := filepath.Base(entry)
    var unixstamp = filename[0:len(filename)-len(extension)]
    theint, _ := strconv.ParseInt(unixstamp, 10, 64)
    theTime := time.Unix(theint, 0)
    if theTime.After(t1) && theTime.Before(t2) {
      relevantFiles = append(relevantFiles, entry)
    }
  }

  // now we only have files for 8/26-8/27

  stat := state.NewSystemState()
  for _, entry := range relevantFiles {
    extension := filepath.Ext(entry)
    filename := filepath.Base(entry)
    var unixstamp = filename[0:len(filename)-len(extension)]
    theint, _ := strconv.ParseInt(unixstamp, 10, 64)
    resp := nextbus.ResponseFromFileWithReferencer(entry, stat.Referencer, int(theint))
    stat.AddResponse(resp, int(theint))
  }

  arr := []ArrEntry{}
  // turn this into an array
  for k, _ := range stat.Map {
    entry := ArrEntry{}
    entry.VehicleId = k
    entry.States = stat.Map[k]
    arr = append(arr, entry)
  }

  result, _ := json.Marshal(arr)
  fmt.Println(string(result))
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"log"
	"net/http"
	"time"
)

func main() {
	seefor := r2router.NewSeeforRouter()

	// measure time middleware
	seefor.Before(func(handler http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			handler.ServeHTTP(w, r)
			log.Printf("took: %s", time.Now().Sub(start))
		})
	})
	// set label "say"
	seefor.After(r2router.Wrap(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		p.AppSet("say", "Hello")
	}))
	seefor.Get("/hello/:name", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		w.Write([]byte(fmt.Sprintf("%s %s!", p.AppGet("say").(string), p.Get("name"))))
	})

	timer := seefor.UseTimer(nil)

	go http.ListenAndServe("127.0.0.1:8080", seefor)
	http.ListenAndServe("127.0.0.1:8081", timer)
}
<commit_msg>Adding preferred After middleware implementation example<commit_after>package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"log"
	"net/http"
	"time"
)

func main() {
	seefor := r2router.NewSeeforRouter()

	// measure time middleware
	seefor.Before(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			next.ServeHTTP(w, r)
			log.Printf("took: %s", time.Now().Sub(start))
		})
	})
	// set label "say"
	seefor.After(func(next r2router.Handler) r2router.Handler {
		return r2router.HandlerFunc(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
			p.AppSet("say", "Hello")
			next.ServeHTTP(w, r, p)
		})
	})

	seefor.After(r2router.Wrap(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		p.AppSet("goodbye", "Bye bye")
	}))

	seefor.Get("/hello/:name", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		w.Write([]byte(fmt.Sprintf("%s %s!\n%s", p.AppGet("say").(string), p.Get("name"), p.AppGet("goodbye"))))
	})

	timer := seefor.UseTimer(nil)

	go http.ListenAndServe("127.0.0.1:8080", seefor)
	http.ListenAndServe("127.0.0.1:8081", timer)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	_ "image/jpeg"
	"image/png"
	"io/ioutil"
	"os"

	"github.com/golang/freetype/truetype"
	"github.com/nfnt/resize"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

func mustGetImage(path string) image.Image {
	image, err := getImage(path)
	if err != nil {
		panic(err)
	}
	return image
}

func getImage(path string) (image.Image, error) {
	imageFd, err := os.Open(path)
	if err != nil {
		return image.Black, err
	}
	defer imageFd.Close()

	img, _, err := image.Decode(imageFd)
	if err != nil {
		return image.Black, err
	}
	return img, nil
}

func generateBasicTemplate() draw.Image {
	templateImage := mustGetImage("template.png")
	destinationImage := image.NewNRGBA(templateImage.Bounds())

	// put base template into our destination
	draw.Draw(
		destinationImage,
		destinationImage.Bounds(),
		templateImage,
		image.ZP,
		draw.Src,
	)
	return destinationImage
}

type backgroundConf struct {
	Path      string
	Placement placement
}

func writeBackground(backgroundConfig backgroundConf, destinationImage draw.Image) draw.Image {
	templateMask := mustGetImage("template_mask.png")
	if backgroundConfig.Path == "" {
		backgroundConfig.Path = "background"
	}
	backgroundImage := mustGetImage(backgroundConfig.Path)

	// resize to the size of the template
	backgroundImage = resize.Resize(
		// scale to the width of the template
		comicWidth,
		0,
		backgroundImage,
		resize.Bilinear,
	)
	backgroundImageHeight := backgroundImage.Bounds().Dy()
	backgroundSegmentSize := backgroundImageHeight / 5
	backgroundStartingY := (int(backgroundConfig.Placement) - 1) * backgroundSegmentSize

	// if the placement makes the image not fully fit in the template, align the bottom edge with the bottom edge of the template
	if destinationImageHeight, pixelsInImage := destinationImage.Bounds().Dy(), backgroundImageHeight-backgroundStartingY; pixelsInImage < destinationImageHeight {
		backgroundStartingY = backgroundImageHeight - destinationImageHeight
	}

	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		backgroundImage,
		image.Pt(0, backgroundStartingY),
		templateMask,
		image.ZP,
		draw.Over,
	)

	return destinationImage
}

func getFont() *truetype.Font {
	fontFd, err := os.Open("Loveletter_TW.ttf")
	if err != nil {
		panic(err)
	}

	fontBytes, err := ioutil.ReadAll(fontFd)
	if err != nil {
		panic(err)
	}

	font, err := truetype.Parse(fontBytes)
	if err != nil {
		panic(err)
	}

	return font
}

const (
	comicWidth            = 720
	comicHeight           = 275
	fontSize              = 14.0
	textBackgroundPadding = 3
	numPlacements         = 5
	baselineX             = 30
)

func baselinePointForPlacement(place placement) image.Point {
	segmentSize := panelRectangle.Dy() / numPlacements

	// multiply the number of segments above (which corresponds to the number of the placement minus 1)
	// then add half a segment to put it in the middle of that (this helps put it not right next to edges)
	baselineY := (int(place)-1)*segmentSize + segmentSize/2
	return image.Pt(baselineX, baselineY)
}

func withPadding(rect image.Rectangle, padding int) image.Rectangle {
	return image.Rect(
		rect.Min.X-padding,
		rect.Min.Y-padding,
		rect.Max.X+padding,
		rect.Max.Y+padding,
	)
}

var panelToTopLeft = map[int]image.Point{
	0: image.Pt(13, 37),
	1: image.Pt(254, 37),
	2: image.Pt(493, 38),
}

var panelRectangle = image.Rect(
	0, 0,
	212, 216,
)

var panelToRectangle = func() map[int]image.Rectangle {
	m := make(map[int]image.Rectangle)
	for panelNumber, topLeft := range panelToTopLeft {
		m[panelNumber] = panelRectangle.Add(topLeft)
	}
	return m
}()

func copyImage(img image.Image) draw.Image {
	// create a new image
	copyTo := image.NewNRGBA(img.Bounds())

	// copy stuff to that image
	draw.Draw(
		copyTo,
		copyTo.Bounds(),
		img,
		image.ZP,
		draw.Src,
	)
	return copyTo
}

func writeTextList(textConfigList []textConf, destinationImage draw.Image) draw.Image {
	// copy for easier semantics
	destinationImage = copyImage(destinationImage)

	for i, textConfig := range textConfigList {
		// writing an empty string still does a background, so let's not do that
		if textConfig.Text == "" {
			continue
		}
		// create text image for panel
		textImage := writeSingleText(textConfig)
		// write text image on top of panel
		draw.DrawMask(
			destinationImage,
			panelToRectangle[i],
			textImage,
			image.ZP,
			image.Black,
			image.ZP,
			draw.Over,
		)
	}
	return destinationImage
}

// between -10 and 10 pixel offset
const offsetBound = 21

func hashString(text string, reduce func(left, right rune) rune) int {
	var accumulator rune
	for _, ch := range text {
		accumulator = reduce(accumulator, ch)
	}
	return int(accumulator)
}

func choosePlacement(text string) placement {
	hash := hashString(text, func(left, right rune) rune { return left | right })
	// mod by the number of placements and then add one to not get noPlacement
	return placement((hash % numPlacements) + 1)
}

func offset(text string, reduce func(left, right rune) rune) int {
	hash := hashString(text, reduce)
	return int(hash%offsetBound - (offsetBound / 2))
}

func offsetX(text string) int {
	return offset(text, func(left, right rune) rune { return left * right })
}

func offsetY(text string) int {
	return offset(text, func(left, right rune) rune { return left + right })
}

type placement int

const (
	noPlacement placement = iota
	topPlacement
	topMiddlePlacement
	middlePlacement
	bottomMiddlePlacement
	bottomPlacement
)

type textConf struct {
	Text      string    `json:"text"`
	Placement placement `json:"placement"`
}

func writeSingleText(textConfig textConf) draw.Image {
	// create a panel image to draw our text to
	destinationImage := image.NewNRGBA(panelRectangle)

	// create font face for our font
	fontFace := truetype.NewFace(
		getFont(),
		&truetype.Options{Size: fontSize},
	)

	// create a drawer to draw the text starting at the baseline point, in the font and measure the distance of the string
	drawDistance := (&font.Drawer{Face: fontFace}).MeasureString(textConfig.Text)

	// get the baseline start point based on the placement
	if textConfig.Placement == noPlacement {
		textConfig.Placement = choosePlacement(textConfig.Text)
	}
	baselineStartPoint := baselinePointForPlacement(textConfig.Placement)

	// add some variance to the starting baseline
	startPoint := image.Pt(
		baselineStartPoint.X+offsetX(textConfig.Text),
		baselineStartPoint.Y+offsetY(textConfig.Text),
	)

	borderRect := withPadding(
		// create a rectangle for the border
		image.Rect(
			// top left x is the same as the baseline
			startPoint.X,
			// top left y is the baseline y moved up by the ascent of the font (the distance between the baseline and the top of the font)
			startPoint.Y-fontFace.Metrics().Ascent.Round(),
			// bottom right x is the baseline start point x plus the calculated distance for drawing
			startPoint.X+drawDistance.Round(),
			// bottom right y is the same as the baseline
			startPoint.Y,
		),
		// pad that rectangle
		textBackgroundPadding,
	)

	// draw the background rectangle into the destination image in white
	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		image.White,
		image.ZP,
		borderRect,
		image.ZP,
		draw.Over,
	)

	// draw the text, in black to the return value
	drawer := &font.Drawer{
		Dst:  destinationImage,
		Src:  image.Black,
		Face: fontFace,
		Dot: fixed.P(
			startPoint.X,
			startPoint.Y,
		),
	}
	drawer.DrawString(textConfig.Text)

	return destinationImage
}

func writeImage(path string, image image.Image) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()

	return png.Encode(fd, image)
}

type panelConf struct {
	Text      string `json:"text"`
	Placement string `json:"placement"`
}

type comicBackgroundConf struct {
	Path      string `json:"path"`
	Placement string `json:"placement"`
}

type config struct {
	PanelConfigList  []panelConf         `json:"panels"`
	BackgroundConfig comicBackgroundConf `json:"background"`
}

func string2placement(str string) (place placement) {
	switch str {
	case "top":
		place = topPlacement
	case "top-middle":
		place = topMiddlePlacement
	case "middle":
		place = middlePlacement
	case "bottom-middle":
		place = bottomMiddlePlacement
	case "bottom":
		place = bottomPlacement
	}
	return
}

func panelConfList2textConfList(panelConfigList []panelConf) []textConf {
	textConfigList := make([]textConf, 0, len(panelConfigList))
	for _, panelConfig := range panelConfigList {
		place := string2placement(panelConfig.Placement)

		textConfigList = append(
			textConfigList,
			textConf{
				Text:      panelConfig.Text,
				Placement: place,
			},
		)
	}
	return textConfigList
}

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

	conf := config{}
	configFd, err := os.Open("config.json")
	if err != nil {
		panic(err)
	}
	err = json.NewDecoder(configFd).Decode(&conf)
	if err != nil {
		panic(err)
	}
	fmt.Println(conf)

	destinationImage := writeTextList(
		panelConfList2textConfList(conf.PanelConfigList),
		writeBackground(backgroundConf{Placement: topMiddlePlacement, Path: "background"}, generateBasicTemplate()),
	)

	err = writeImage("out.png", destinationImage)
	if err != nil {
		panic(err)
	}
}
<commit_msg>read background info from config<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	_ "image/jpeg"
	"image/png"
	"io/ioutil"
	"os"

	"github.com/golang/freetype/truetype"
	"github.com/nfnt/resize"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

func mustGetImage(path string) image.Image {
	image, err := getImage(path)
	if err != nil {
		panic(err)
	}
	return image
}

func getImage(path string) (image.Image, error) {
	imageFd, err := os.Open(path)
	if err != nil {
		return image.Black, err
	}
	defer imageFd.Close()

	img, _, err := image.Decode(imageFd)
	if err != nil {
		return image.Black, err
	}
	return img, nil
}

func generateBasicTemplate() draw.Image {
	templateImage := mustGetImage("template.png")
	destinationImage := image.NewNRGBA(templateImage.Bounds())

	// put base template into our destination
	draw.Draw(
		destinationImage,
		destinationImage.Bounds(),
		templateImage,
		image.ZP,
		draw.Src,
	)
	return destinationImage
}

type backgroundConf struct {
	Path      string
	Placement placement
}

func writeBackground(backgroundConfig backgroundConf, destinationImage draw.Image) draw.Image {
	templateMask := mustGetImage("template_mask.png")
	if backgroundConfig.Path == "" {
		backgroundConfig.Path = "background"
	}
	backgroundImage := mustGetImage(backgroundConfig.Path)

	// resize to the size of the template
	backgroundImage = resize.Resize(
		// scale to the width of the template
		comicWidth,
		0,
		backgroundImage,
		resize.Bilinear,
	)
	backgroundImageHeight := backgroundImage.Bounds().Dy()
	backgroundSegmentSize := backgroundImageHeight / 5
	backgroundStartingY := (int(backgroundConfig.Placement) - 1) * backgroundSegmentSize

	// if the placement makes the image not fully fit in the template, align the bottom edge with the bottom edge of the template
	if destinationImageHeight, pixelsInImage := destinationImage.Bounds().Dy(), backgroundImageHeight-backgroundStartingY; pixelsInImage < destinationImageHeight {
		backgroundStartingY = backgroundImageHeight - destinationImageHeight
	}

	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		backgroundImage,
		image.Pt(0, backgroundStartingY),
		templateMask,
		image.ZP,
		draw.Over,
	)

	return destinationImage
}

func getFont() *truetype.Font {
	fontFd, err := os.Open("Loveletter_TW.ttf")
	if err != nil {
		panic(err)
	}

	fontBytes, err := ioutil.ReadAll(fontFd)
	if err != nil {
		panic(err)
	}

	font, err := truetype.Parse(fontBytes)
	if err != nil {
		panic(err)
	}

	return font
}

const (
	comicWidth            = 720
	comicHeight           = 275
	fontSize              = 14.0
	textBackgroundPadding = 3
	numPlacements         = 5
	baselineX             = 30
)

func baselinePointForPlacement(place placement) image.Point {
	segmentSize := panelRectangle.Dy() / numPlacements

	// multiply the number of segments above (which corresponds to the number of the placement minus 1)
	// then add half a segment to put it in the middle of that (this helps put it not right next to edges)
	baselineY := (int(place)-1)*segmentSize + segmentSize/2
	return image.Pt(baselineX, baselineY)
}

func withPadding(rect image.Rectangle, padding int) image.Rectangle {
	return image.Rect(
		rect.Min.X-padding,
		rect.Min.Y-padding,
		rect.Max.X+padding,
		rect.Max.Y+padding,
	)
}

var panelToTopLeft = map[int]image.Point{
	0: image.Pt(13, 37),
	1: image.Pt(254, 37),
	2: image.Pt(493, 38),
}

var panelRectangle = image.Rect(
	0, 0,
	212, 216,
)

var panelToRectangle = func() map[int]image.Rectangle {
	m := make(map[int]image.Rectangle)
	for panelNumber, topLeft := range panelToTopLeft {
		m[panelNumber] = panelRectangle.Add(topLeft)
	}
	return m
}()

func copyImage(img image.Image) draw.Image {
	// create a new image
	copyTo := image.NewNRGBA(img.Bounds())

	// copy stuff to that image
	draw.Draw(
		copyTo,
		copyTo.Bounds(),
		img,
		image.ZP,
		draw.Src,
	)
	return copyTo
}

func writeTextList(textConfigList []textConf, destinationImage draw.Image) draw.Image {
	// copy for easier semantics
	destinationImage = copyImage(destinationImage)

	for i, textConfig := range textConfigList {
		// writing an empty string still does a background, so let's not do that
		if textConfig.Text == "" {
			continue
		}
		// create text image for panel
		textImage := writeSingleText(textConfig)
		// write text image on top of panel
		draw.DrawMask(
			destinationImage,
			panelToRectangle[i],
			textImage,
			image.ZP,
			image.Black,
			image.ZP,
			draw.Over,
		)
	}
	return destinationImage
}

// between -10 and 10 pixel offset
const offsetBound = 21

func hashString(text string, reduce func(left, right rune) rune) int {
	var accumulator rune
	for _, ch := range text {
		accumulator = reduce(accumulator, ch)
	}
	return int(accumulator)
}

func choosePlacement(text string) placement {
	hash := hashString(text, func(left, right rune) rune { return left | right })
	// mod by the number of placements and then add one to not get noPlacement
	return placement((hash % numPlacements) + 1)
}

func offset(text string, reduce func(left, right rune) rune) int {
	hash := hashString(text, reduce)
	return int(hash%offsetBound - (offsetBound / 2))
}

func offsetX(text string) int {
	return offset(text, func(left, right rune) rune { return left * right })
}

func offsetY(text string) int {
	return offset(text, func(left, right rune) rune { return left + right })
}

type placement int

const (
	noPlacement placement = iota
	topPlacement
	topMiddlePlacement
	middlePlacement
	bottomMiddlePlacement
	bottomPlacement
)

type textConf struct {
	Text      string    `json:"text"`
	Placement placement `json:"placement"`
}

func writeSingleText(textConfig textConf) draw.Image {
	// create a panel image to draw our text to
	destinationImage := image.NewNRGBA(panelRectangle)

	// create font face for our font
	fontFace := truetype.NewFace(
		getFont(),
		&truetype.Options{Size: fontSize},
	)

	// create a drawer to draw the text starting at the baseline point, in the font and measure the distance of the string
	drawDistance := (&font.Drawer{Face: fontFace}).MeasureString(textConfig.Text)

	// get the baseline start point based on the placement
	if textConfig.Placement == noPlacement {
		textConfig.Placement = choosePlacement(textConfig.Text)
	}
	baselineStartPoint := baselinePointForPlacement(textConfig.Placement)

	// add some variance to the starting baseline
	startPoint := image.Pt(
		baselineStartPoint.X+offsetX(textConfig.Text),
		baselineStartPoint.Y+offsetY(textConfig.Text),
	)

	borderRect := withPadding(
		// create a rectangle for the border
		image.Rect(
			// top left x is the same as the baseline
			startPoint.X,
			// top left y is the baseline y moved up by the ascent of the font (the distance between the baseline and the top of the font)
			startPoint.Y-fontFace.Metrics().Ascent.Round(),
			// bottom right x is the baseline start point x plus the calculated distance for drawing
			startPoint.X+drawDistance.Round(),
			// bottom right y is the same as the baseline
			startPoint.Y,
		),
		// pad that rectangle
		textBackgroundPadding,
	)

	// draw the background rectangle into the destination image in white
	draw.DrawMask(
		destinationImage,
		destinationImage.Bounds(),
		image.White,
		image.ZP,
		borderRect,
		image.ZP,
		draw.Over,
	)

	// draw the text, in black to the return value
	drawer := &font.Drawer{
		Dst:  destinationImage,
		Src:  image.Black,
		Face: fontFace,
		Dot: fixed.P(
			startPoint.X,
			startPoint.Y,
		),
	}
	drawer.DrawString(textConfig.Text)

	return destinationImage
}

func writeImage(path string, image image.Image) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()

	return png.Encode(fd, image)
}

type panelConf struct {
	Text      string `json:"text"`
	Placement string `json:"placement"`
}

type comicBackgroundConf struct {
	Path      string `json:"path"`
	Placement string `json:"placement"`
}

type config struct {
	PanelConfigList  []panelConf         `json:"panels"`
	BackgroundConfig comicBackgroundConf `json:"background"`
}

func string2placement(str string) (place placement) {
	switch str {
	case "top":
		place = topPlacement
	case "top-middle":
		place = topMiddlePlacement
	case "middle":
		place = middlePlacement
	case "bottom-middle":
		place = bottomMiddlePlacement
	case "bottom":
		place = bottomPlacement
	}
	return
}

func panelConfList2textConfList(panelConfigList []panelConf) []textConf {
	textConfigList := make([]textConf, 0, len(panelConfigList))
	for _, panelConfig := range panelConfigList {
		place := string2placement(panelConfig.Placement)

		textConfigList = append(
			textConfigList,
			textConf{
				Text:      panelConfig.Text,
				Placement: place,
			},
		)
	}
	return textConfigList
}

func comicBackgroundConf2backgroundConf(comicBackgroundConfig comicBackgroundConf) backgroundConf {
	return backgroundConf{
		Placement: string2placement(comicBackgroundConfig.Placement),
		Path:      comicBackgroundConfig.Path,
	}
}

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

	conf := config{}
	configFd, err := os.Open("config.json")
	if err != nil {
		panic(err)
	}
	err = json.NewDecoder(configFd).Decode(&conf)
	if err != nil {
		panic(err)
	}
	fmt.Println(conf)

	destinationImage := writeTextList(
		panelConfList2textConfList(conf.PanelConfigList),
		writeBackground(
			comicBackgroundConf2backgroundConf(conf.BackgroundConfig),
			generateBasicTemplate(),
		),
	)

	err = writeImage("out.png", destinationImage)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strconv"
	"testing"

	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

func TestDownloadWithoutToken(t *testing.T) {
	cfg := config.Config{
		UserViperConfig: viper.New(),
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "Welcome to Exercism", err.Error())
		// It uses the default base API url to infer the host
		assert.Regexp(t, "exercism.io/my/settings", err.Error())
	}
}

func TestDownloadWithoutWorkspace(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	cfg := config.Config{
		UserViperConfig: v,
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "re-run the configure", err.Error())
	}
}

func TestDownloadWithoutBaseURL(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	v.Set("workspace", "/home/whatever")
	cfg := config.Config{
		UserViperConfig: v,
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "re-run the configure", err.Error())
	}
}

func TestDownloadWithoutFlags(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	v.Set("workspace", "/home/username")
	v.Set("apibaseurl", "http://example.com")

	cfg := config.Config{
		UserViperConfig: v,
	}

	flags := pflag.NewFlagSet("fake", pflag.PanicOnError)
	setupDownloadFlags(flags)

	err := runDownload(cfg, flags, []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "need an --exercise name or a solution --uuid", err.Error())
	}
}

func TestDownload(t *testing.T) {
	oldOut := Out
	oldErr := Err
	Out = ioutil.Discard
	Err = ioutil.Discard
	defer func() {
		Out = oldOut
		Err = oldErr
	}()

	testCases := []struct {
		requester   bool
		expectedDir string
		flags       map[string]string
	}{
		{
			requester:   true,
			expectedDir: "",
			flags:       map[string]string{"exercise": "bogus-exercise"},
		},
		{
			requester:   true,
			expectedDir: "",
			flags:       map[string]string{"uuid": "bogus-id"},
		},
		{
			requester:   false,
			expectedDir: filepath.Join("users", "alice"),
			flags:       map[string]string{"uuid": "bogus-id"},
		},
		{
			requester:   true,
			expectedDir: filepath.Join("teams", "bogus-team"),
			flags:       map[string]string{"exercise": "bogus-exercise", "track": "bogus-track", "team": "bogus-team"},
		},
	}

	for _, tc := range testCases {
		tmpDir, err := ioutil.TempDir("", "download-cmd")
		defer os.RemoveAll(tmpDir)
		assert.NoError(t, err)

		ts := fakeDownloadServer(strconv.FormatBool(tc.requester), tc.flags["team"])
		defer ts.Close()

		v := viper.New()
		v.Set("workspace", tmpDir)
		v.Set("apibaseurl", ts.URL)
		v.Set("token", "abc123")

		cfg := config.Config{
			UserViperConfig: v,
		}
		flags := pflag.NewFlagSet("fake", pflag.PanicOnError)
		setupDownloadFlags(flags)
		for name, value := range tc.flags {
			flags.Set(name, value)
		}

		err = runDownload(cfg, flags, []string{})
		assert.NoError(t, err)

		targetDir := filepath.Join(tmpDir, tc.expectedDir)
		assertDownloadedCorrectFiles(t, targetDir)

		path := filepath.Join(targetDir, "bogus-track", "bogus-exercise", ".solution.json")
		b, err := ioutil.ReadFile(path)
		var s workspace.Solution
		err = json.Unmarshal(b, &s)
		assert.NoError(t, err)

		assert.Equal(t, "bogus-track", s.Track)
		assert.Equal(t, "bogus-exercise", s.Exercise)
		assert.Equal(t, tc.requester, s.IsRequester)
	}
}

func fakeDownloadServer(requestor, teamSlug string) *httptest.Server {
	mux := http.NewServeMux()
	server := httptest.NewServer(mux)

	mux.HandleFunc("/file-1.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is file 1")
	})

	mux.HandleFunc("/subdir/file-2.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is file 2")
	})

	mux.HandleFunc("/special-char-filename#.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is a special file")
	})

	mux.HandleFunc("/with-leading-slash.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this has a slash")
	})

	mux.HandleFunc("/file-3.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "")
	})

	mux.HandleFunc("/solutions/latest", func(w http.ResponseWriter, r *http.Request) {
		team := "null"
		if teamSlug := r.FormValue("team_id"); teamSlug != "" {
			team = fmt.Sprintf(`{"name": "Bogus Team", "slug": "%s"}`, teamSlug)
		}
		payloadBody := fmt.Sprintf(payloadTemplate, requestor, team, server.URL+"/")
		fmt.Fprint(w, payloadBody)
	})
	mux.HandleFunc("/solutions/bogus-id", func(w http.ResponseWriter, r *http.Request) {
		payloadBody := fmt.Sprintf(payloadTemplate, requestor, "null", server.URL+"/")
		fmt.Fprint(w, payloadBody)
	})

	return server
}

func assertDownloadedCorrectFiles(t *testing.T, targetDir string) {
	expectedFiles := []struct {
		desc     string
		path     string
		contents string
	}{
		{
			desc:     "a file in the exercise root directory",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "file-1.txt"),
			contents: "this is file 1",
		},
		{
			desc:     "a file in a subdirectory",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "subdir", "file-2.txt"),
			contents: "this is file 2",
		},
		{
			desc:     "a file that requires URL encoding",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "special-char-filename#.txt"),
			contents: "this is a special file",
		},
		{
			desc:     "a file that has a leading slash",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "with-leading-slash.txt"),
			contents: "this has a slash",
		},
	}

	for _, file := range expectedFiles {
		t.Run(file.desc, func(t *testing.T) {
			b, err := ioutil.ReadFile(file.path)
			assert.NoError(t, err)
			assert.Equal(t, file.contents, string(b))
		})
	}

	path := filepath.Join(targetDir, "bogus-track", "bogus-exercise", "file-3.txt")
	_, err := os.Lstat(path)
	assert.True(t, os.IsNotExist(err), "It should not write the file if empty.")
}

const payloadTemplate = `
{
	"solution": {
		"id": "bogus-id",
		"user": {
			"handle": "alice",
			"is_requester": %s
		},
		"team": %s,
		"exercise": {
			"id": "bogus-exercise",
			"instructions_url": "http://example.com/bogus-exercise",
			"auto_approve": false,
			"track": {
				"id": "bogus-track",
				"language": "Bogus Language"
			}
		},
		"file_download_base_url": "%s",
		"files": [
			"file-1.txt",
			"subdir/file-2.txt",
			"special-char-filename#.txt",
			"/with-leading-slash.txt",
			"file-3.txt"
		],
		"iteration": {
			"submitted_at": "2017-08-21t10:11:12.130z"
		}
	}
}
`
<commit_msg>Remove hard-coded metadata filename references<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strconv"
	"testing"

	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

func TestDownloadWithoutToken(t *testing.T) {
	cfg := config.Config{
		UserViperConfig: viper.New(),
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "Welcome to Exercism", err.Error())
		// It uses the default base API url to infer the host
		assert.Regexp(t, "exercism.io/my/settings", err.Error())
	}
}

func TestDownloadWithoutWorkspace(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	cfg := config.Config{
		UserViperConfig: v,
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "re-run the configure", err.Error())
	}
}

func TestDownloadWithoutBaseURL(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	v.Set("workspace", "/home/whatever")
	cfg := config.Config{
		UserViperConfig: v,
	}

	err := runDownload(cfg, pflag.NewFlagSet("fake", pflag.PanicOnError), []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "re-run the configure", err.Error())
	}
}

func TestDownloadWithoutFlags(t *testing.T) {
	v := viper.New()
	v.Set("token", "abc123")
	v.Set("workspace", "/home/username")
	v.Set("apibaseurl", "http://example.com")

	cfg := config.Config{
		UserViperConfig: v,
	}

	flags := pflag.NewFlagSet("fake", pflag.PanicOnError)
	setupDownloadFlags(flags)

	err := runDownload(cfg, flags, []string{})
	if assert.Error(t, err) {
		assert.Regexp(t, "need an --exercise name or a solution --uuid", err.Error())
	}
}

func TestDownload(t *testing.T) {
	oldOut := Out
	oldErr := Err
	Out = ioutil.Discard
	Err = ioutil.Discard
	defer func() {
		Out = oldOut
		Err = oldErr
	}()

	testCases := []struct {
		requester   bool
		expectedDir string
		flags       map[string]string
	}{
		{
			requester:   true,
			expectedDir: "",
			flags:       map[string]string{"exercise": "bogus-exercise"},
		},
		{
			requester:   true,
			expectedDir: "",
			flags:       map[string]string{"uuid": "bogus-id"},
		},
		{
			requester:   false,
			expectedDir: filepath.Join("users", "alice"),
			flags:       map[string]string{"uuid": "bogus-id"},
		},
		{
			requester:   true,
			expectedDir: filepath.Join("teams", "bogus-team"),
			flags:       map[string]string{"exercise": "bogus-exercise", "track": "bogus-track", "team": "bogus-team"},
		},
	}

	for _, tc := range testCases {
		tmpDir, err := ioutil.TempDir("", "download-cmd")
		defer os.RemoveAll(tmpDir)
		assert.NoError(t, err)

		ts := fakeDownloadServer(strconv.FormatBool(tc.requester), tc.flags["team"])
		defer ts.Close()

		v := viper.New()
		v.Set("workspace", tmpDir)
		v.Set("apibaseurl", ts.URL)
		v.Set("token", "abc123")

		cfg := config.Config{
			UserViperConfig: v,
		}
		flags := pflag.NewFlagSet("fake", pflag.PanicOnError)
		setupDownloadFlags(flags)
		for name, value := range tc.flags {
			flags.Set(name, value)
		}

		err = runDownload(cfg, flags, []string{})
		assert.NoError(t, err)

		targetDir := filepath.Join(tmpDir, tc.expectedDir)
		assertDownloadedCorrectFiles(t, targetDir)

		path := filepath.Join(targetDir, "bogus-track", "bogus-exercise")
		b, err := ioutil.ReadFile(workspace.NewExerciseFromDir(path).MetadataFilepath())
		var s workspace.Solution
		err = json.Unmarshal(b, &s)
		assert.NoError(t, err)

		assert.Equal(t, "bogus-track", s.Track)
		assert.Equal(t, "bogus-exercise", s.Exercise)
		assert.Equal(t, tc.requester, s.IsRequester)
	}
}

func fakeDownloadServer(requestor, teamSlug string) *httptest.Server {
	mux := http.NewServeMux()
	server := httptest.NewServer(mux)

	mux.HandleFunc("/file-1.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is file 1")
	})

	mux.HandleFunc("/subdir/file-2.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is file 2")
	})

	mux.HandleFunc("/special-char-filename#.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this is a special file")
	})

	mux.HandleFunc("/with-leading-slash.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "this has a slash")
	})

	mux.HandleFunc("/file-3.txt", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "")
	})

	mux.HandleFunc("/solutions/latest", func(w http.ResponseWriter, r *http.Request) {
		team := "null"
		if teamSlug := r.FormValue("team_id"); teamSlug != "" {
			team = fmt.Sprintf(`{"name": "Bogus Team", "slug": "%s"}`, teamSlug)
		}
		payloadBody := fmt.Sprintf(payloadTemplate, requestor, team, server.URL+"/")
		fmt.Fprint(w, payloadBody)
	})
	mux.HandleFunc("/solutions/bogus-id", func(w http.ResponseWriter, r *http.Request) {
		payloadBody := fmt.Sprintf(payloadTemplate, requestor, "null", server.URL+"/")
		fmt.Fprint(w, payloadBody)
	})

	return server
}

func assertDownloadedCorrectFiles(t *testing.T, targetDir string) {
	expectedFiles := []struct {
		desc     string
		path     string
		contents string
	}{
		{
			desc:     "a file in the exercise root directory",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "file-1.txt"),
			contents: "this is file 1",
		},
		{
			desc:     "a file in a subdirectory",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "subdir", "file-2.txt"),
			contents: "this is file 2",
		},
		{
			desc:     "a file that requires URL encoding",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "special-char-filename#.txt"),
			contents: "this is a special file",
		},
		{
			desc:     "a file that has a leading slash",
			path:     filepath.Join(targetDir, "bogus-track", "bogus-exercise", "with-leading-slash.txt"),
			contents: "this has a slash",
		},
	}

	for _, file := range expectedFiles {
		t.Run(file.desc, func(t *testing.T) {
			b, err := ioutil.ReadFile(file.path)
			assert.NoError(t, err)
			assert.Equal(t, file.contents, string(b))
		})
	}

	path := filepath.Join(targetDir, "bogus-track", "bogus-exercise", "file-3.txt")
	_, err := os.Lstat(path)
	assert.True(t, os.IsNotExist(err), "It should not write the file if empty.")
}

const payloadTemplate = `
{
	"solution": {
		"id": "bogus-id",
		"user": {
			"handle": "alice",
			"is_requester": %s
		},
		"team": %s,
		"exercise": {
			"id": "bogus-exercise",
			"instructions_url": "http://example.com/bogus-exercise",
			"auto_approve": false,
			"track": {
				"id": "bogus-track",
				"language": "Bogus Language"
			}
		},
		"file_download_base_url": "%s",
		"files": [
			"file-1.txt",
			"subdir/file-2.txt",
			"special-char-filename#.txt",
			"/with-leading-slash.txt",
			"file-3.txt"
		],
		"iteration": {
			"submitted_at": "2017-08-21t10:11:12.130z"
		}
	}
}
`
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
)

const (
	cmdName = "fdroidcl"

	repoName = "repo"
	repoURL  = "https://f-droid.org/repo"
)

// A Command is an implementation of a go command
// like go build or go fix.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description.
	Short string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s %s\n", cmdName, c.UsageLine)
	anyFlags := false
	c.Flag.VisitAll(func(f *flag.Flag) { anyFlags = true })
	if anyFlags {
		fmt.Fprintf(os.Stderr, "\nAvailable options:\n")
		c.Flag.PrintDefaults()
	}
	os.Exit(2)
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-h] <command> [<args>]\n\n", cmdName)
		fmt.Fprintf(os.Stderr, "Available commands:\n")
		maxUsageLen := 0
		for _, c := range commands {
			if len(c.UsageLine) > maxUsageLen {
				maxUsageLen = len(c.UsageLine)
			}
		}
		for _, c := range commands {
			fmt.Fprintf(os.Stderr, "   %s%s  %s\n", c.UsageLine,
				strings.Repeat(" ", maxUsageLen-len(c.UsageLine)), c.Short)
		}
	}
}

// Commands lists the available commands.
var commands = []*Command{
	cmdUpdate,
	cmdList,
	cmdSearch,
	cmdShow,
	cmdDevices,
	cmdInstalled,
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(2)
	}

	for _, cmd := range commands {
		if cmd.Name() != args[0] {
			continue
		}
		cmd.Flag.Usage = func() { cmd.Usage() }
		cmd.Flag.Parse(args[1:])
		args = cmd.Flag.Args()
		cmd.Run(args)
		os.Exit(0)
	}

	switch args[0] {
	default:
		log.Printf("Unrecognised command '%s'\n\n", args[0])
		flag.Usage()
		os.Exit(2)
	}
}
<commit_msg>Mention -h usages per command<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
)

const (
	cmdName = "fdroidcl"

	repoName = "repo"
	repoURL  = "https://f-droid.org/repo"
)

// A Command is an implementation of a go command
// like go build or go fix.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description.
	Short string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s %s [-h]\n", cmdName, c.UsageLine)
	anyFlags := false
	c.Flag.VisitAll(func(f *flag.Flag) { anyFlags = true })
	if anyFlags {
		fmt.Fprintf(os.Stderr, "\nAvailable options:\n")
		c.Flag.PrintDefaults()
	}
	os.Exit(2)
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-h] <command> [<args>]\n\n", cmdName)
		fmt.Fprintf(os.Stderr, "Available commands:\n")
		maxUsageLen := 0
		for _, c := range commands {
			if len(c.UsageLine) > maxUsageLen {
				maxUsageLen = len(c.UsageLine)
			}
		}
		for _, c := range commands {
			fmt.Fprintf(os.Stderr, "   %s%s  %s\n", c.UsageLine,
				strings.Repeat(" ", maxUsageLen-len(c.UsageLine)), c.Short)
		}
		fmt.Fprintf(os.Stderr, "\nUse %s <command> -h for more info\n", cmdName)
	}
}

// Commands lists the available commands.
var commands = []*Command{
	cmdUpdate,
	cmdList,
	cmdSearch,
	cmdShow,
	cmdDevices,
	cmdInstalled,
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(2)
	}

	for _, cmd := range commands {
		if cmd.Name() != args[0] {
			continue
		}
		cmd.Flag.Usage = func() { cmd.Usage() }
		cmd.Flag.Parse(args[1:])
		args = cmd.Flag.Args()
		cmd.Run(args)
		os.Exit(0)
	}

	switch args[0] {
	default:
		log.Printf("Unrecognised command '%s'\n\n", args[0])
		flag.Usage()
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/mvdan/fdroidcl"
)

var cmdShow = &Command{
	UsageLine: "show <appid...>",
	Short:     "Show detailed info about an app",
}

func init() {
	cmdShow.Run = runShow
}

func runShow(args []string) {
	if len(args) < 1 {
		log.Fatalf("No package names given")
	}
	apps := findApps(args)
	for i, app := range apps {
		if i > 0 {
			fmt.Printf("\n--\n\n")
		}
		printAppDetailed(*app)
	}
}

func appsMap(apps []fdroidcl.App) map[string]*fdroidcl.App {
	m := make(map[string]*fdroidcl.App, len(apps))
	for i := range apps {
		app := &apps[i]
		m[app.ID] = app
	}
	return m
}

func findApps(ids []string) []*fdroidcl.App {
	apps := appsMap(mustLoadIndexes())
	result := make([]*fdroidcl.App, len(ids))
	for i, id := range ids {
		var vcode = -1
		j := strings.Index(id, ":")
		if j > -1 {
			var err error
			vcode, err = strconv.Atoi(id[j+1:])
			if err != nil {
				log.Fatalf("Could not parse version code from '%s'", id)
			}
			id = id[:j]
		}

		app, e := apps[id]
		if !e {
			log.Fatalf("Could not find app with ID '%s'", id)
		}

		if vcode > -1 {
			found := false
			for _, apk := range app.Apks {
				if apk.VCode == vcode {
					app.Apks = []fdroidcl.Apk{apk}
					found = true
				}
			}
			if !found {
				log.Fatalf("Could not find version %d for app with ID '%s'", vcode, id)
			}
		}
		result[i] = app
	}
	return result
}

func printAppDetailed(app fdroidcl.App) {
	p := func(title string, format string, args ...interface{}) {
		if format == "" {
			fmt.Println(title)
		} else {
			fmt.Printf("%s %s\n", title, fmt.Sprintf(format, args...))
		}
	}
	p("Package          :", "%s", app.ID)
	p("Name             :", "%s", app.Name)
	p("Summary          :", "%s", app.Summary)
	p("Added            :", "%s", app.Added.String())
	p("Last Updated     :", "%s", app.Updated.String())
	p("Version          :", "%s (%d)", app.CVName, app.CVCode)
	p("License          :", "%s", app.License)
	if app.Categs != nil {
		p("Categories       :", "%s", strings.Join(app.Categs, ", "))
	}
	if app.Website != "" {
		p("Website          :", "%s", app.Website)
	}
	if app.Source != "" {
		p("Source           :", "%s", app.Source)
	}
	if app.Tracker != "" {
		p("Tracker          :", "%s", app.Tracker)
	}
	if app.Changelog != "" {
		p("Changelog        :", "%s", app.Changelog)
	}
	if app.Donate != "" {
		p("Donate           :", "%s", app.Donate)
	}
	if app.Bitcoin != "" {
		p("Bitcoin          :", "bitcoin:%s", app.Bitcoin)
	}
	if app.Litecoin != "" {
		p("Litecoin         :", "litecoin:%s", app.Litecoin)
	}
	if app.FlattrID != "" {
		p("Flattr           :", "https://flattr.com/thing/%s", app.FlattrID)
	}
	fmt.Println()
	p("Description :", "")
	fmt.Println()
	app.TextDesc(os.Stdout)
	fmt.Println()
	p("Available Versions :", "")
	for _, apk := range app.Apks {
		fmt.Println()
		p("    Name   :", "%s (%d)", apk.VName, apk.VCode)
		p("    Size   :", "%d", apk.Size)
		p("    MinSdk :", "%d", apk.MinSdk)
		if apk.MaxSdk > 0 {
			p("    MaxSdk :", "%d", apk.MaxSdk)
		}
		if apk.ABIs != nil {
			p("    ABIs   :", "%s", strings.Join(apk.ABIs, ", "))
		}
	}
}
<commit_msg>show: display permissions too<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/mvdan/fdroidcl"
)

var cmdShow = &Command{
	UsageLine: "show <appid...>",
	Short:     "Show detailed info about an app",
}

func init() {
	cmdShow.Run = runShow
}

func runShow(args []string) {
	if len(args) < 1 {
		log.Fatalf("No package names given")
	}
	apps := findApps(args)
	for i, app := range apps {
		if i > 0 {
			fmt.Printf("\n--\n\n")
		}
		printAppDetailed(*app)
	}
}

func appsMap(apps []fdroidcl.App) map[string]*fdroidcl.App {
	m := make(map[string]*fdroidcl.App, len(apps))
	for i := range apps {
		app := &apps[i]
		m[app.ID] = app
	}
	return m
}

func findApps(ids []string) []*fdroidcl.App {
	apps := appsMap(mustLoadIndexes())
	result := make([]*fdroidcl.App, len(ids))
	for i, id := range ids {
		var vcode = -1
		j := strings.Index(id, ":")
		if j > -1 {
			var err error
			vcode, err = strconv.Atoi(id[j+1:])
			if err != nil {
				log.Fatalf("Could not parse version code from '%s'", id)
			}
			id = id[:j]
		}

		app, e := apps[id]
		if !e {
			log.Fatalf("Could not find app with ID '%s'", id)
		}

		if vcode > -1 {
			found := false
			for _, apk := range app.Apks {
				if apk.VCode == vcode {
					app.Apks = []fdroidcl.Apk{apk}
					found = true
				}
			}
			if !found {
				log.Fatalf("Could not find version %d for app with ID '%s'", vcode, id)
			}
		}
		result[i] = app
	}
	return result
}

func printAppDetailed(app fdroidcl.App) {
	p := func(title string, format string, args ...interface{}) {
		if format == "" {
			fmt.Println(title)
		} else {
			fmt.Printf("%s %s\n", title, fmt.Sprintf(format, args...))
		}
	}
	p("Package          :", "%s", app.ID)
	p("Name             :", "%s", app.Name)
	p("Summary          :", "%s", app.Summary)
	p("Added            :", "%s", app.Added.String())
	p("Last Updated     :", "%s", app.Updated.String())
	p("Version          :", "%s (%d)", app.CVName, app.CVCode)
	p("License          :", "%s", app.License)
	if app.Categs != nil {
		p("Categories       :", "%s", strings.Join(app.Categs, ", "))
	}
	if app.Website != "" {
		p("Website          :", "%s", app.Website)
	}
	if app.Source != "" {
		p("Source           :", "%s", app.Source)
	}
	if app.Tracker != "" {
		p("Tracker          :", "%s", app.Tracker)
	}
	if app.Changelog != "" {
		p("Changelog        :", "%s", app.Changelog)
	}
	if app.Donate != "" {
		p("Donate           :", "%s", app.Donate)
	}
	if app.Bitcoin != "" {
		p("Bitcoin          :", "bitcoin:%s", app.Bitcoin)
	}
	if app.Litecoin != "" {
		p("Litecoin         :", "litecoin:%s", app.Litecoin)
	}
	if app.FlattrID != "" {
		p("Flattr           :", "https://flattr.com/thing/%s", app.FlattrID)
	}
	fmt.Println()
	p("Description :", "")
	fmt.Println()
	app.TextDesc(os.Stdout)
	fmt.Println()
	p("Available Versions :", "")
	for _, apk := range app.Apks {
		fmt.Println()
		p("    Name   :", "%s (%d)", apk.VName, apk.VCode)
		p("    Size   :", "%d", apk.Size)
		p("    MinSdk :", "%d", apk.MinSdk)
		if apk.MaxSdk > 0 {
			p("    MaxSdk :", "%d", apk.MaxSdk)
		}
		if apk.ABIs != nil {
			p("    ABIs   :", "%s", strings.Join(apk.ABIs, ", "))
		}
		if apk.Perms != nil {
			p("    Perms  :", "%s", strings.Join(apk.Perms, ", "))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
)

// Applet imports
import (
	"applets/echo"
	"applets/shell"
)

// This map contains the mappings from callname
// to applet function.
var Applets map[string]Applet = map[string]Applet{
	"echo": echo.Echo,
	"shell": shell.Shell,
	"bash": shell.Shell,
	"sh": shell.Shell,
}

// Signature of applet functions.
// call is like os.Argv, and therefore contains the
// name of the applet itself in call[0].
// If the returned error is not nil, it is printed
// to stdout.
type Applet func(call []string) os.Error
<commit_msg>Disabled irritating and potentially dangerous "bash"<commit_after>package main

import (
	"os"
)

// Applet imports
import (
	"applets/echo"
	"applets/shell"
)

// This map contains the mappings from callname
// to applet function.
var Applets map[string]Applet = map[string]Applet{
	"echo":  echo.Echo,
	"shell": shell.Shell,
	//"bash": shell.Shell,
	//"sh": shell.Shell,
}

// Signature of applet functions.
// call is like os.Argv, and therefore contains the
// name of the applet itself in call[0].
// If the returned error is not nil, it is printed
// to stdout.
type Applet func(call []string) os.Error
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
)

var _ = Suite(&SSHSuite{})

type SSHSuite struct {
	testing.JujuConnSuite
	oldpath string
}

// fakessh outputs its arguments to stdout for verification
var fakessh = `#!/bin/bash

echo $@
`

func (s *SSHSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)

	path := c.MkDir()
	s.oldpath = os.Getenv("PATH")
	os.Setenv("PATH", path+":"+s.oldpath)
	f, err := os.OpenFile(filepath.Join(path, "ssh"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
	c.Assert(err, IsNil)
	_, err = f.Write([]byte(fakessh))
	c.Assert(err, IsNil)
	err = f.Close()
	c.Assert(err, IsNil)
}

func (s *SSHSuite) TearDownTest(c *C) {
	os.Setenv("PATH", s.oldpath)
	s.JujuConnSuite.TearDownTest(c)
}

func (s *SSHSuite) TestFakeSSH(c *C) {
	cmd := exec.Command("ssh", "1", "two", "III")
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	err := cmd.Run()
	c.Assert(err, IsNil)
	c.Assert(stdout.String(), Equals, "1 two III\n")
}

var sshTests = []struct {
	args   []string
	result string
}{
	{[]string{"0"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns --\n"},
	{[]string{"0", "uname -a"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns -- uname -a\n"},
	{[]string{"mysql/0"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns --\n"},
	{[]string{"mongodb/1"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-2.dns --\n"},
}

func (s *SSHSuite) TestSSHCommand(c *C) {
	m := s.makeMachines(3, c)
	ch := coretesting.Charms.Dir("series", "dummy")
	curl := charm.MustParseURL(
		fmt.Sprintf("local:series/%s-%d", ch.Meta().Name, ch.Revision()),
	)
	bundleURL, err := url.Parse("http://bundles.example.com/dummy-1")
	c.Assert(err, IsNil)
	dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256")
	c.Assert(err, IsNil)
	srv, err := s.State.AddService("mysql", dummy)
	c.Assert(err, IsNil)
	u, err := srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[0])
	c.Assert(err, IsNil)

	srv, err = s.State.AddService("mongodb", dummy)
	c.Assert(err, IsNil)
	u, err = srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[1])
	c.Assert(err, IsNil)
	u, err = srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[2])
	c.Assert(err, IsNil)

	for _, t := range sshTests {
		c.Logf("testing juju ssh %s", t.args)
		ctx := &cmd.Context{c.MkDir(), &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}}
		code := cmd.Main(&SSHCommand{}, ctx, t.args)
		c.Check(code, Equals, 0)
		c.Check(ctx.Stderr.(*bytes.Buffer).String(), Equals, "")
		c.Check(ctx.Stdout.(*bytes.Buffer).String(), Equals, t.result)
	}
}

func (s *SSHSuite) makeMachines(n int, c *C) []*state.Machine {
	var machines = make([]*state.Machine, n)
	for i := 0; i < n; i++ {
		m, err := s.State.AddMachine()
		c.Assert(err, IsNil)
		// must set an instance id as the ssh command uses that as a signal the machine
		// has been provisioned
		inst, err := s.Conn.Environ.StartInstance(m.Id(), nil, nil)
		c.Assert(err, IsNil)
		c.Assert(m.SetInstanceId(inst.Id()), IsNil)
		machines[i] = m
	}
	return machines
}
<commit_msg>add additional test cases<commit_after>package main

import (
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
)

var _ = Suite(&SSHSuite{})

type SSHSuite struct {
	testing.JujuConnSuite
	oldpath string
}

// fakessh outputs its arguments to stdout for verification
var fakessh = `#!/bin/bash

echo $@
`

func (s *SSHSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)

	path := c.MkDir()
	s.oldpath = os.Getenv("PATH")
	os.Setenv("PATH", path+":"+s.oldpath)
	f, err := os.OpenFile(filepath.Join(path, "ssh"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
	c.Assert(err, IsNil)
	_, err = f.Write([]byte(fakessh))
	c.Assert(err, IsNil)
	err = f.Close()
	c.Assert(err, IsNil)
}

func (s *SSHSuite) TearDownTest(c *C) {
	os.Setenv("PATH", s.oldpath)
	s.JujuConnSuite.TearDownTest(c)
}

func (s *SSHSuite) TestFakeSSH(c *C) {
	cmd := exec.Command("ssh", "1", "two", "III")
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	err := cmd.Run()
	c.Assert(err, IsNil)
	c.Assert(stdout.String(), Equals, "1 two III\n")
}

var sshTests = []struct {
	args   []string
	result string
}{
	{[]string{"0"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns --\n"},
	// juju ssh 0 'uname -a'
	{[]string{"0", "uname -a"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns -- uname -a\n"},
	// juju ssh 0 -- uname -a
	{[]string{"0", "--", "uname", "-a"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns -- uname -a\n"},
	{[]string{"mysql/0"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-0.dns --\n"},
	{[]string{"mongodb/1"}, "-l ubuntu -t -o StrictHostKeyChecking no -o PasswordAuthentication no dummyenv-2.dns --\n"},
}

func (s *SSHSuite) TestSSHCommand(c *C) {
	m := s.makeMachines(3, c)
	ch := coretesting.Charms.Dir("series", "dummy")
	curl := charm.MustParseURL(
		fmt.Sprintf("local:series/%s-%d", ch.Meta().Name, ch.Revision()),
	)
	bundleURL, err := url.Parse("http://bundles.example.com/dummy-1")
	c.Assert(err, IsNil)
	dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256")
	c.Assert(err, IsNil)
	srv, err := s.State.AddService("mysql", dummy)
	c.Assert(err, IsNil)
	u, err := srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[0])
	c.Assert(err, IsNil)

	srv, err = s.State.AddService("mongodb", dummy)
	c.Assert(err, IsNil)
	u, err = srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[1])
	c.Assert(err, IsNil)
	u, err = srv.AddUnit()
	c.Assert(err, IsNil)
	err = u.AssignToMachine(m[2])
	c.Assert(err, IsNil)

	for _, t := range sshTests {
		c.Logf("testing juju ssh %s", t.args)
		ctx := &cmd.Context{c.MkDir(), &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}}
		code := cmd.Main(&SSHCommand{}, ctx, t.args)
		c.Check(code, Equals, 0)
		c.Check(ctx.Stderr.(*bytes.Buffer).String(), Equals, "")
		c.Check(ctx.Stdout.(*bytes.Buffer).String(), Equals, t.result)
	}
}

func (s *SSHSuite) makeMachines(n int, c *C) []*state.Machine {
	var machines = make([]*state.Machine, n)
	for i := 0; i < n; i++ {
		m, err := s.State.AddMachine()
		c.Assert(err, IsNil)
		// must set an instance id as the ssh command uses that as a signal the machine
		// has been provisioned
		inst, err := s.Conn.Environ.StartInstance(m.Id(), nil, nil)
		c.Assert(err, IsNil)
		c.Assert(m.SetInstanceId(inst.Id()), IsNil)
		machines[i] = m
	}
	return machines
}
<|endoftext|>