<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pq"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"strconv"
	"time"
)

func main() {
	log.Print("starting ChicagoWorksforYou.com API server")

	router := mux.NewRouter()
	router.HandleFunc("/health_check", HealthCheckHandler)
	router.HandleFunc("/services.json", ServicesHandler)
	router.HandleFunc("/wards/{id}/requests.json", WardRequestsHandler)
	router.HandleFunc("/wards/{id}/counts.json", WardCountsHandler)
	http.ListenAndServe(":5000", router)
}

func WardCountsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return the number of service requests opened
	// grouped by day, then by service request type

        // sample output
        // $ curl "http://localhost:5000/wards/10/counts.json?service_code=4fd3b167e750846744000005"
        // {
        //   "2013-06-06": 2,
        //   "2013-06-07": 4,
        //   "2013-06-09": 5,
        //   "2013-06-10": 6,
        //   "2013-06-12": 23
        // }
        //
	vars := mux.Vars(request)
	ward_id := vars["id"]
	params := request.URL.Query()

	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// determine date range. default is last 7 days.
	days, _ := strconv.Atoi(params["count"][0])
	since := time.Now().AddDate(0, 0, -(days - 1))
	log.Printf("fetching since %s", since)

	log.Printf("fetching counts for ward %s code %s for past %d days", ward_id, params["service_code"][0], days)

	rows, err := db.Query("SELECT COUNT(*), DATE(requested_datetime) as requested_date FROM service_requests WHERE ward = $1 AND duplicate IS NULL AND service_code = $2 AND requested_datetime >= $3::date GROUP BY DATE(requested_datetime) ORDER BY requested_date;", string(ward_id), params["service_code"][0], since)
	if err != nil {
		log.Fatal("error fetching data for WardCountsHandler", err)
	}

	type WardCount struct {
		Requested_date time.Time
		Count          int
	}

	var counts []WardCount
	for rows.Next() {
		wc := WardCount{}
		if err := rows.Scan(&wc.Count, &wc.Requested_date); err != nil {
			log.Print("error reading row of ward count", err)
		}

		// trunc the requested time to just date
		counts = append(counts, wc)
	}

	resp := make(map[string]int)

	for _, c := range counts {
		key := c.Requested_date.Format("2006-01-02")
		log.Print("key: ", key)
		resp[key] = c.Count
	}

	jsn, _ := json.MarshalIndent(resp, "", "  ")
	response.Write(jsn)
}

func WardRequestsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return recent service requests

	vars := mux.Vars(request)
	ward_id := vars["id"]

	log.Print("fetch requests for ward ", ward_id)

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT lat,long,ward,police_district,service_request_id,status,service_name,service_code,agency_responsible,address,channel,media_url,requested_datetime,updated_datetime,created_at,updated_at,duplicate,parent_service_request_id,id FROM service_requests WHERE duplicate IS NULL AND ward = $1 ORDER BY updated_at DESC LIMIT 100;", ward_id)

	if err != nil {
		log.Fatal("error fetching data for WardRequestsHandler", err)
	}

	type Open311RequestRow struct {
		Lat, Long                                                                                float64
		Ward, Police_district, Id                                                                                int
		Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url, Duplicate, Parent_service_request_id sql.NullString
		Requested_datetime, Updated_datetime, Created_at, Updated_at                                                                                pq.NullTime // FIXME: should these be proper time objects?
		Extended_attributes                                                                                map[string]interface{}
	}

	var result []Open311RequestRow

	for rows.Next() {
		var row Open311RequestRow
		if err := rows.Scan(&row.Lat, &row.Long, &row.Ward, &row.Police_district,
			&row.Service_request_id, &row.Status, &row.Service_name,
			&row.Service_code, &row.Agency_responsible, &row.Address,
			&row.Channel, &row.Media_url, &row.Requested_datetime,
			&row.Updated_datetime, &row.Created_at, &row.Updated_at,
			&row.Duplicate, &row.Parent_service_request_id,
			&row.Id); err != nil {
			log.Fatal("error reading row", err)
		}

		result = append(result, row)
	}

	jsn, _ := json.MarshalIndent(result, "", "  ")
	response.Write(jsn)
}

func ServicesHandler(response http.ResponseWriter, request *http.Request) {
	// return counts of requests, grouped by service name
	//
	// Sample output:
	//
	// [
	//   {
	//     "Count": 1139,
	//     "Service_code": "4fd3b167e750846744000005",
	//     "Service_name": "Graffiti Removal"
	//   },
	//   {
	//     "Count": 25,
	//     "Service_code": "4fd6e4ece750840569000019",
	//     "Service_name": "Restaurant Complaint"
	//   },
	//
	//  ... snip ...
	//
	// ]

	type ServicesCount struct {
		Count        int
		Service_code string
		Service_name string
	}

	var services []ServicesCount

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT COUNT(*), service_code, service_name FROM service_requests WHERE duplicate IS NULL GROUP BY service_code,service_name;")

	if err != nil {
		log.Fatal("error fetching data for ServicesHandler", err)
	}

	for rows.Next() {
		var count int
		var service_code, service_name string

		if err := rows.Scan(&count, &service_code, &service_name); err != nil {
			log.Fatal("error reading row", err)
		}

		row := ServicesCount{Count: count, Service_code: service_code, Service_name: service_name}
		services = append(services, row)
	}

	jsn, _ := json.MarshalIndent(services, "", "  ")
	response.Write(jsn)
}

func HealthCheckHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Add("Content-type", "application/json")

	type HealthCheck struct {
		Count    int
		Database bool
		Healthy  bool
	}

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	health_check := HealthCheck{}

	health_check.Database = db.Ping() == nil

	rows, _ := db.Query("SELECT COUNT(*) FROM service_requests;")
	for rows.Next() {
		if err := rows.Scan(&health_check.Count); err != nil {
			log.Fatal("error fetching count", err)
		}
	}

	// calculate overall health
	health_check.Healthy = health_check.Count > 0 && health_check.Database

	log.Printf("health_check: %+v", health_check)
	if !health_check.Healthy {
		log.Printf("health_check failed")
	}
	jsn, _ := json.Marshal(health_check)
	response.Write(jsn)
}
<commit_msg>update sample URL<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pq"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"strconv"
	"time"
)

func main() {
	log.Print("starting ChicagoWorksforYou.com API server")

	router := mux.NewRouter()
	router.HandleFunc("/health_check", HealthCheckHandler)
	router.HandleFunc("/services.json", ServicesHandler)
	router.HandleFunc("/wards/{id}/requests.json", WardRequestsHandler)
	router.HandleFunc("/wards/{id}/counts.json", WardCountsHandler)
	http.ListenAndServe(":5000", router)
}

func WardCountsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return the number of service requests opened
	// grouped by day, then by service request type

        // sample output
        // $ curl "http://localhost:5000/wards/10/counts.json?service_code=4fd3b167e750846744000005&count=5"
        // {
        //   "2013-06-06": 2,
        //   "2013-06-07": 4,
        //   "2013-06-09": 5,
        //   "2013-06-10": 6,
        //   "2013-06-12": 23
        // }
        //
	vars := mux.Vars(request)
	ward_id := vars["id"]
	params := request.URL.Query()

	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// determine date range. default is last 7 days.
	days, _ := strconv.Atoi(params["count"][0])
	since := time.Now().AddDate(0, 0, -(days - 1))
	log.Printf("fetching since %s", since)

	log.Printf("fetching counts for ward %s code %s for past %d days", ward_id, params["service_code"][0], days)

	rows, err := db.Query("SELECT COUNT(*), DATE(requested_datetime) as requested_date FROM service_requests WHERE ward = $1 AND duplicate IS NULL AND service_code = $2 AND requested_datetime >= $3::date GROUP BY DATE(requested_datetime) ORDER BY requested_date;", string(ward_id), params["service_code"][0], since)
	if err != nil {
		log.Fatal("error fetching data for WardCountsHandler", err)
	}

	type WardCount struct {
		Requested_date time.Time
		Count          int
	}

	var counts []WardCount
	for rows.Next() {
		wc := WardCount{}
		if err := rows.Scan(&wc.Count, &wc.Requested_date); err != nil {
			log.Print("error reading row of ward count", err)
		}

		// trunc the requested time to just date
		counts = append(counts, wc)
	}

	resp := make(map[string]int)

	for _, c := range counts {
		key := c.Requested_date.Format("2006-01-02")
		log.Print("key: ", key)
		resp[key] = c.Count
	}

	jsn, _ := json.MarshalIndent(resp, "", "  ")
	response.Write(jsn)
}

func WardRequestsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return recent service requests

	vars := mux.Vars(request)
	ward_id := vars["id"]

	log.Print("fetch requests for ward ", ward_id)

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT lat,long,ward,police_district,service_request_id,status,service_name,service_code,agency_responsible,address,channel,media_url,requested_datetime,updated_datetime,created_at,updated_at,duplicate,parent_service_request_id,id FROM service_requests WHERE duplicate IS NULL AND ward = $1 ORDER BY updated_at DESC LIMIT 100;", ward_id)

	if err != nil {
		log.Fatal("error fetching data for WardRequestsHandler", err)
	}

	type Open311RequestRow struct {
		Lat, Long                                                                                float64
		Ward, Police_district, Id                                                                                int
		Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url, Duplicate, Parent_service_request_id sql.NullString
		Requested_datetime, Updated_datetime, Created_at, Updated_at                                                                                pq.NullTime // FIXME: should these be proper time objects?
		Extended_attributes                                                                                map[string]interface{}
	}

	var result []Open311RequestRow

	for rows.Next() {
		var row Open311RequestRow
		if err := rows.Scan(&row.Lat, &row.Long, &row.Ward, &row.Police_district,
			&row.Service_request_id, &row.Status, &row.Service_name,
			&row.Service_code, &row.Agency_responsible, &row.Address,
			&row.Channel, &row.Media_url, &row.Requested_datetime,
			&row.Updated_datetime, &row.Created_at, &row.Updated_at,
			&row.Duplicate, &row.Parent_service_request_id,
			&row.Id); err != nil {
			log.Fatal("error reading row", err)
		}

		result = append(result, row)
	}

	jsn, _ := json.MarshalIndent(result, "", "  ")
	response.Write(jsn)
}

func ServicesHandler(response http.ResponseWriter, request *http.Request) {
	// return counts of requests, grouped by service name
	//
	// Sample output:
	//
	// [
	//   {
	//     "Count": 1139,
	//     "Service_code": "4fd3b167e750846744000005",
	//     "Service_name": "Graffiti Removal"
	//   },
	//   {
	//     "Count": 25,
	//     "Service_code": "4fd6e4ece750840569000019",
	//     "Service_name": "Restaurant Complaint"
	//   },
	//
	//  ... snip ...
	//
	// ]

	type ServicesCount struct {
		Count        int
		Service_code string
		Service_name string
	}

	var services []ServicesCount

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT COUNT(*), service_code, service_name FROM service_requests WHERE duplicate IS NULL GROUP BY service_code,service_name;")

	if err != nil {
		log.Fatal("error fetching data for ServicesHandler", err)
	}

	for rows.Next() {
		var count int
		var service_code, service_name string

		if err := rows.Scan(&count, &service_code, &service_name); err != nil {
			log.Fatal("error reading row", err)
		}

		row := ServicesCount{Count: count, Service_code: service_code, Service_name: service_name}
		services = append(services, row)
	}

	jsn, _ := json.MarshalIndent(services, "", "  ")
	response.Write(jsn)
}

func HealthCheckHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Add("Content-type", "application/json")

	type HealthCheck struct {
		Count    int
		Database bool
		Healthy  bool
	}

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	health_check := HealthCheck{}

	health_check.Database = db.Ping() == nil

	rows, _ := db.Query("SELECT COUNT(*) FROM service_requests;")
	for rows.Next() {
		if err := rows.Scan(&health_check.Count); err != nil {
			log.Fatal("error fetching count", err)
		}
	}

	// calculate overall health
	health_check.Healthy = health_check.Count > 0 && health_check.Database

	log.Printf("health_check: %+v", health_check)
	if !health_check.Healthy {
		log.Printf("health_check failed")
	}
	jsn, _ := json.Marshal(health_check)
	response.Write(jsn)
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package api

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/gorilla/handlers"

	"github.com/rs/cors"

	"github.com/ava-labs/avalanchego/api/auth"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/engine/avalanche"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/engine/snowman"
	"github.com/ava-labs/avalanchego/utils/logging"
)

const (
	baseURL               = "/ext"
	serverShutdownTimeout = 10 * time.Second
)

var (
	errUnknownLockOption = errors.New("invalid lock options")
)

// Server maintains the HTTP router
type Server struct {
	// log this server writes to
	log logging.Logger
	// generates new logs for chains to write to
	factory logging.Factory
	// Maps endpoints to handlers
	router *router
	// Listens for HTTP traffic on this address
	listenAddress string
	// Handles authorization. Must be non-nil after initialization, even if
	// token authorization is off.
	auth *auth.Auth

	// http server
	srv *http.Server
}

// Initialize creates the API server at the provided host and port
func (s *Server) Initialize(
	log logging.Logger,
	factory logging.Factory,
	host string,
	port uint16,
	authEnabled bool,
	authPassword string,
) error {
	s.log = log
	s.factory = factory
	s.listenAddress = fmt.Sprintf("%s:%d", host, port)
	s.router = newRouter()
	s.auth = &auth.Auth{Enabled: authEnabled}
	if err := s.auth.Password.Set(authPassword); err != nil {
		return err
	}
	if !authEnabled {
		return nil
	}

	// only create auth service if token authorization is required
	s.log.Info("API authorization is enabled. Auth tokens must be passed in the header of API requests, except requests to the auth service.")
	authService := auth.NewService(s.log, s.auth)
	return s.AddRoute(authService, &sync.RWMutex{}, auth.Endpoint, "", s.log)

}

// Dispatch starts the API server
func (s *Server) Dispatch() error {
	listener, err := net.Listen("tcp", s.listenAddress)
	if err != nil {
		return err
	}
	s.log.Info("HTTP API server listening on %q", s.listenAddress)
	handler := cors.Default().Handler(s.router)
	handler = s.auth.WrapHandler(handler)
	s.srv = &http.Server{Handler: handler}
	return s.srv.Serve(listener)
}

// DispatchTLS starts the API server with the provided TLS certificate
func (s *Server) DispatchTLS(certFile, keyFile string) error {
	listener, err := net.Listen("tcp", s.listenAddress)
	if err != nil {
		return err
	}
	s.log.Info("HTTPS API server listening on %q", s.listenAddress)
	handler := cors.Default().Handler(s.router)
	handler = s.auth.WrapHandler(handler)
	return http.ServeTLS(listener, handler, certFile, keyFile)
}

// RegisterChain registers the API endpoints associated with this chain. That is,
// add <route, handler> pairs to server so that API calls can be made to the VM.
// This method runs in a goroutine to avoid a deadlock in the event that the caller
// holds the engine's context lock. Namely, this could happen when the P-Chain is
// creating a new chain and holds the P-Chain's lock when this function is held,
// and at the same time the server's lock is held due to an API call and is trying
// to grab the P-Chain's lock.
func (s *Server) RegisterChain(chainName string, ctx *snow.Context, engineIntf interface{}) {
	go func() {
		var (
			ctx      *snow.Context
			handlers map[string]*common.HTTPHandler
			err      error
		)

		switch engine := engineIntf.(type) {
		case snowman.Engine:
			ctx.Lock.Lock()
			handlers, err = engine.GetVM().CreateHandlers()
			ctx.Lock.Unlock()
		case avalanche.Engine:
			ctx.Lock.Lock()
			handlers, err = engine.GetVM().CreateHandlers()
			ctx.Lock.Unlock()
		default:
			s.log.Error("engine has unexpected type %T", engineIntf)
			return
		}
		if err != nil {
			s.log.Error("failed to create %s handlers: %s", chainName, err)
			return
		}

		httpLogger, err := s.factory.MakeChain(chainName, "http")
		if err != nil {
			s.log.Error("failed to create new http logger: %s", err)
		}

		chainID := ctx.ChainID
		s.log.Verbo("About to add API endpoints for chain with ID %s", chainID)
		// all subroutes to a chain begin with "bc/<the chain's ID>"
		defaultEndpoint := "bc/" + chainID.String()

		// Register each endpoint
		for extension, handler := range handlers {
			// Validate that the route being added is valid
			// e.g. "/foo" and "" are ok but "\n" is not
			_, err := url.ParseRequestURI(extension)
			if extension != "" && err != nil {
				s.log.Error("could not add route to chain's API handler because route is malformed: %s", err)
				continue
			}
			if err := s.AddChainRoute(handler, ctx, defaultEndpoint, extension, httpLogger); err != nil {
				s.log.Error("error adding route: %s", err)
			}
		}
	}()
}

// AddChainRoute registers a route to a chain's handler
func (s *Server) AddChainRoute(handler *common.HTTPHandler, ctx *snow.Context, base, endpoint string, loggingWriter io.Writer) error {
	url := fmt.Sprintf("%s/%s", baseURL, base)
	s.log.Info("adding route %s%s", url, endpoint)
	// Apply logging middleware
	h := handlers.CombinedLoggingHandler(loggingWriter, handler.Handler)
	// Apply middleware to grab/release chain's lock before/after calling API method
	h, err := lockMiddleware(h, handler.LockOptions, &ctx.Lock)
	if err != nil {
		return err
	}
	// Apply middleware to reject calls to the handler before the chain finishes bootstrapping
	h = rejectMiddleware(h, ctx)
	return s.router.AddRouter(url, endpoint, h)
}

// AddRoute registers a route to a handler.
func (s *Server) AddRoute(handler *common.HTTPHandler, lock *sync.RWMutex, base, endpoint string, loggingWriter io.Writer) error {
	url := fmt.Sprintf("%s/%s", baseURL, base)
	s.log.Info("adding route %s%s", url, endpoint)
	// Apply logging middleware
	h := handlers.CombinedLoggingHandler(loggingWriter, handler.Handler)
	// Apply middleware to grab/release chain's lock before/after calling API method
	h, err := lockMiddleware(h, handler.LockOptions, lock)
	if err != nil {
		return err
	}
	return s.router.AddRouter(url, endpoint, h)
}

// Wraps a handler by grabbing and releasing a lock before calling the handler.
func lockMiddleware(handler http.Handler, lockOption common.LockOption, lock *sync.RWMutex) (http.Handler, error) {
	switch lockOption {
	case common.WriteLock:
		return middlewareHandler{
			before:  lock.Lock,
			after:   lock.Unlock,
			handler: handler,
		}, nil
	case common.ReadLock:
		return middlewareHandler{
			before:  lock.RLock,
			after:   lock.RUnlock,
			handler: handler,
		}, nil
	case common.NoLock:
		return handler, nil
	default:
		return nil, errUnknownLockOption
	}
}

// Reject middleware wraps a handler. If the chain that the context describes is
// not done bootstrapping, writes back an error.
func rejectMiddleware(handler http.Handler, ctx *snow.Context) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // If chain isn't done bootstrapping, ignore API calls
		if !ctx.IsBootstrapped() {
			w.WriteHeader(http.StatusServiceUnavailable)
			// Doesn't matter if there's an error while writing. They'll get the StatusServiceUnavailable code.
			_, _ = w.Write([]byte("API call rejected because chain is not done bootstrapping"))
		} else {
			handler.ServeHTTP(w, r)
		}
	})
}

// AddAliases registers aliases to the server
func (s *Server) AddAliases(endpoint string, aliases ...string) error {
	url := fmt.Sprintf("%s/%s", baseURL, endpoint)
	endpoints := make([]string, len(aliases))
	for i, alias := range aliases {
		endpoints[i] = fmt.Sprintf("%s/%s", baseURL, alias)
	}
	return s.router.AddAlias(url, endpoints...)
}

// AddAliasesWithReadLock registers aliases to the server assuming the http read
// lock is currently held.
func (s *Server) AddAliasesWithReadLock(endpoint string, aliases ...string) error {
	// This is safe, as the read lock doesn't actually need to be held once the
	// http handler is called. However, it is unlocked later, so this function
	// must end with the lock held.
	s.router.lock.RUnlock()
	defer s.router.lock.RLock()

	return s.AddAliases(endpoint, aliases...)
}

// Call ...
func (s *Server) Call(
	writer http.ResponseWriter,
	method,
	base,
	endpoint string,
	body io.Reader,
	headers map[string]string,
) error {
	url := fmt.Sprintf("%s/vm/%s", baseURL, base)

	handler, err := s.router.GetHandler(url, endpoint)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", "*", body)
	if err != nil {
		return err
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	handler.ServeHTTP(writer, req)

	return nil
}

// Shutdown this server
func (s *Server) Shutdown() error {
	if s.srv == nil {
		return nil
	}
	ctx, cancel := context.WithTimeout(context.Background(), serverShutdownTimeout)
	defer cancel()
	return s.srv.Shutdown(ctx)
}
<commit_msg>fix bug where context was over-written<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package api

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/gorilla/handlers"

	"github.com/rs/cors"

	"github.com/ava-labs/avalanchego/api/auth"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/engine/avalanche"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/engine/snowman"
	"github.com/ava-labs/avalanchego/utils/logging"
)

const (
	baseURL               = "/ext"
	serverShutdownTimeout = 10 * time.Second
)

var (
	errUnknownLockOption = errors.New("invalid lock options")
)

// Server maintains the HTTP router
type Server struct {
	// log this server writes to
	log logging.Logger
	// generates new logs for chains to write to
	factory logging.Factory
	// Maps endpoints to handlers
	router *router
	// Listens for HTTP traffic on this address
	listenAddress string
	// Handles authorization. Must be non-nil after initialization, even if
	// token authorization is off.
	auth *auth.Auth

	// http server
	srv *http.Server
}

// Initialize creates the API server at the provided host and port
func (s *Server) Initialize(
	log logging.Logger,
	factory logging.Factory,
	host string,
	port uint16,
	authEnabled bool,
	authPassword string,
) error {
	s.log = log
	s.factory = factory
	s.listenAddress = fmt.Sprintf("%s:%d", host, port)
	s.router = newRouter()
	s.auth = &auth.Auth{Enabled: authEnabled}
	if err := s.auth.Password.Set(authPassword); err != nil {
		return err
	}
	if !authEnabled {
		return nil
	}

	// only create auth service if token authorization is required
	s.log.Info("API authorization is enabled. Auth tokens must be passed in the header of API requests, except requests to the auth service.")
	authService := auth.NewService(s.log, s.auth)
	return s.AddRoute(authService, &sync.RWMutex{}, auth.Endpoint, "", s.log)

}

// Dispatch starts the API server
func (s *Server) Dispatch() error {
	listener, err := net.Listen("tcp", s.listenAddress)
	if err != nil {
		return err
	}
	s.log.Info("HTTP API server listening on %q", s.listenAddress)
	handler := cors.Default().Handler(s.router)
	handler = s.auth.WrapHandler(handler)
	s.srv = &http.Server{Handler: handler}
	return s.srv.Serve(listener)
}

// DispatchTLS starts the API server with the provided TLS certificate
func (s *Server) DispatchTLS(certFile, keyFile string) error {
	listener, err := net.Listen("tcp", s.listenAddress)
	if err != nil {
		return err
	}
	s.log.Info("HTTPS API server listening on %q", s.listenAddress)
	handler := cors.Default().Handler(s.router)
	handler = s.auth.WrapHandler(handler)
	return http.ServeTLS(listener, handler, certFile, keyFile)
}

// RegisterChain registers the API endpoints associated with this chain. That is,
// add <route, handler> pairs to server so that API calls can be made to the VM.
// This method runs in a goroutine to avoid a deadlock in the event that the caller
// holds the engine's context lock. Namely, this could happen when the P-Chain is
// creating a new chain and holds the P-Chain's lock when this function is held,
// and at the same time the server's lock is held due to an API call and is trying
// to grab the P-Chain's lock.
func (s *Server) RegisterChain(chainName string, ctx *snow.Context, engineIntf interface{}) {
	go func() {
		var (
			handlers map[string]*common.HTTPHandler
			err      error
		)

		switch engine := engineIntf.(type) {
		case snowman.Engine:
			ctx.Lock.Lock()
			handlers, err = engine.GetVM().CreateHandlers()
			ctx.Lock.Unlock()
		case avalanche.Engine:
			ctx.Lock.Lock()
			handlers, err = engine.GetVM().CreateHandlers()
			ctx.Lock.Unlock()
		default:
			s.log.Error("engine has unexpected type %T", engineIntf)
			return
		}
		if err != nil {
			s.log.Error("failed to create %s handlers: %s", chainName, err)
			return
		}

		httpLogger, err := s.factory.MakeChain(chainName, "http")
		if err != nil {
			s.log.Error("failed to create new http logger: %s", err)
		}

		chainID := ctx.ChainID
		s.log.Verbo("About to add API endpoints for chain with ID %s", chainID)
		// all subroutes to a chain begin with "bc/<the chain's ID>"
		defaultEndpoint := "bc/" + chainID.String()

		// Register each endpoint
		for extension, handler := range handlers {
			// Validate that the route being added is valid
			// e.g. "/foo" and "" are ok but "\n" is not
			_, err := url.ParseRequestURI(extension)
			if extension != "" && err != nil {
				s.log.Error("could not add route to chain's API handler because route is malformed: %s", err)
				continue
			}
			if err := s.AddChainRoute(handler, ctx, defaultEndpoint, extension, httpLogger); err != nil {
				s.log.Error("error adding route: %s", err)
			}
		}
	}()
}

// AddChainRoute registers a route to a chain's handler
func (s *Server) AddChainRoute(handler *common.HTTPHandler, ctx *snow.Context, base, endpoint string, loggingWriter io.Writer) error {
	url := fmt.Sprintf("%s/%s", baseURL, base)
	s.log.Info("adding route %s%s", url, endpoint)
	// Apply logging middleware
	h := handlers.CombinedLoggingHandler(loggingWriter, handler.Handler)
	// Apply middleware to grab/release chain's lock before/after calling API method
	h, err := lockMiddleware(h, handler.LockOptions, &ctx.Lock)
	if err != nil {
		return err
	}
	// Apply middleware to reject calls to the handler before the chain finishes bootstrapping
	h = rejectMiddleware(h, ctx)
	return s.router.AddRouter(url, endpoint, h)
}

// AddRoute registers a route to a handler.
func (s *Server) AddRoute(handler *common.HTTPHandler, lock *sync.RWMutex, base, endpoint string, loggingWriter io.Writer) error {
	url := fmt.Sprintf("%s/%s", baseURL, base)
	s.log.Info("adding route %s%s", url, endpoint)
	// Apply logging middleware
	h := handlers.CombinedLoggingHandler(loggingWriter, handler.Handler)
	// Apply middleware to grab/release chain's lock before/after calling API method
	h, err := lockMiddleware(h, handler.LockOptions, lock)
	if err != nil {
		return err
	}
	return s.router.AddRouter(url, endpoint, h)
}

// Wraps a handler by grabbing and releasing a lock before calling the handler.
func lockMiddleware(handler http.Handler, lockOption common.LockOption, lock *sync.RWMutex) (http.Handler, error) {
	switch lockOption {
	case common.WriteLock:
		return middlewareHandler{
			before:  lock.Lock,
			after:   lock.Unlock,
			handler: handler,
		}, nil
	case common.ReadLock:
		return middlewareHandler{
			before:  lock.RLock,
			after:   lock.RUnlock,
			handler: handler,
		}, nil
	case common.NoLock:
		return handler, nil
	default:
		return nil, errUnknownLockOption
	}
}

// Reject middleware wraps a handler. If the chain that the context describes is
// not done bootstrapping, writes back an error.
func rejectMiddleware(handler http.Handler, ctx *snow.Context) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // If chain isn't done bootstrapping, ignore API calls
		if !ctx.IsBootstrapped() {
			w.WriteHeader(http.StatusServiceUnavailable)
			// Doesn't matter if there's an error while writing. They'll get the StatusServiceUnavailable code.
			_, _ = w.Write([]byte("API call rejected because chain is not done bootstrapping"))
		} else {
			handler.ServeHTTP(w, r)
		}
	})
}

// AddAliases registers aliases to the server
func (s *Server) AddAliases(endpoint string, aliases ...string) error {
	url := fmt.Sprintf("%s/%s", baseURL, endpoint)
	endpoints := make([]string, len(aliases))
	for i, alias := range aliases {
		endpoints[i] = fmt.Sprintf("%s/%s", baseURL, alias)
	}
	return s.router.AddAlias(url, endpoints...)
}

// AddAliasesWithReadLock registers aliases to the server assuming the http read
// lock is currently held.
func (s *Server) AddAliasesWithReadLock(endpoint string, aliases ...string) error {
	// This is safe, as the read lock doesn't actually need to be held once the
	// http handler is called. However, it is unlocked later, so this function
	// must end with the lock held.
	s.router.lock.RUnlock()
	defer s.router.lock.RLock()

	return s.AddAliases(endpoint, aliases...)
}

// Call ...
func (s *Server) Call(
	writer http.ResponseWriter,
	method,
	base,
	endpoint string,
	body io.Reader,
	headers map[string]string,
) error {
	url := fmt.Sprintf("%s/vm/%s", baseURL, base)

	handler, err := s.router.GetHandler(url, endpoint)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", "*", body)
	if err != nil {
		return err
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	handler.ServeHTTP(writer, req)

	return nil
}

// Shutdown this server
func (s *Server) Shutdown() error {
	if s.srv == nil {
		return nil
	}
	ctx, cancel := context.WithTimeout(context.Background(), serverShutdownTimeout)
	defer cancel()
	return s.srv.Shutdown(ctx)
}
<|endoftext|>


<commit_before>package apiv1

import (
	"errors"
	"os"
	"strconv"
	)

type GetStoragePoolsResp_entries struct {
	Entries `json:"entries"`
	Content []struct {
		StoragePoolName string `json:"i$ElementName"`
		InstanceName string `json:"i$InstanceID"`
		}
	}
//Get a list of all Storage Pools associated with SymmID
func (smis *SMIS) GetStoragePools(sid string) (resp *GetStoragePoolsResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::SymmStorageSystem,Name::" + sid + "/relationships/CIM_StoragePool", nil, &resp)
	return resp,err
}

//Get a list of Storage Volumes to/associated with a Device Masking Group
func (smis *SMIS) GetDeviceMaskingGroups(sid string) (resp *GetDeviceMaskingGroupsResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::Symm_StorageSystem,Name::" + sid + "/relationships/CIM_DeviceMaskingGroup", nil, &resp)
	return resp,err
}

//Get a list of Storage Volumes on/associated with SymmID
func (smis *SMIS) GetStorageVolumes(sid string) (resp *GetStorageVolumesResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::Symm_StorageSystem,Name::" + sid + "/relationships/CIM_StorageVolume", nil, &resp)
	return resp,err
}

type PostVolumesReq struct {

}

type PostVolumesContent struct {

	ElementType string `json: "ElementType"`
	Size string `json: "Size"`
	ElementName string `json: "ElementName"`
	EMCNumberOfDevices string `json: "EMCNumberOfDevices"`
	Type string `json: "@type"`
}
//Add goal struct later

type PostVolumesResp struct {

}

//Create a Storage Volume
func (smis *SMIS) PostVolumes(req *PostVolumesReq, sid string) (resp *PostVolumesResp, err error){
	err = smis.query("POST","/ecom/edaa/root/emc/instances/Symm_StorageConfigurationService/CreationClassName::Symm_StorageConfigurationService,Name::EMCStorageConfigurationService,SystemCreationClassName::Symm_StorageSystem,SystemName::" + sid + "/action/CreateOrModifyElementFromStoragePool", req, &resp)
	return resp,err
}

<commit_msg>Added Post Device Masking & Initiator Group and structs<commit_after>package apiv1

import (
	"errors"
	"os"
	"strconv"
	)

type GetStoragePoolsResp struct {
	Entries `json:"entries"`{
		Content []struct {
			StoragePoolName string `json:"i$ElementName"`
			InstanceName string `json:"i$InstanceID"`
		} `json:"content"`
	} `json:"entries"`
}

//Get a list of all Storage Pools associated with SymmID
func (smis *SMIS) GetStoragePools(sid string) (resp *GetStoragePoolsResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::SymmStorageSystem,Name::" + sid + "/relationships/CIM_StoragePool", nil, &resp)
	return resp,err
}

type GetDeviceMaskingGroupsResp struct {
	Entries `json:"entries"`{
		Content []struct {
			DeviceMaskingGroupName string `json:"i$ElementName"`
			InstanceName string `json:"i$InstanceID"`
		} `json:"content"`
	} `json:"entries"`
}

//Get a list of Device Masking Groups associated with SymmID
func (smis *SMIS) GetDeviceMaskingGroups(sid string) (resp *GetDeviceMaskingGroupsResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::Symm_StorageSystem,Name::" + sid + "/relationships/CIM_DeviceMaskingGroup", nil, &resp)
	return resp,err
}

type GetStorageVolumesResp struct {
	Entries `json:"entries"`{
		Content []struct {
			DeviceName string `json:"i$DeviceID"`
		} `json:"content"`
	} `json:"entries"`
}

//Get a list of Storage Volumes on/associated with SymmID
func (smis *SMIS) GetStorageVolumes(sid string) (resp *GetStorageVolumesResp, err error){
	err = smis.query("GET","/ecom/edaa/root/emc/instances/Symm_StorageSystem/CreationClassName::Symm_StorageSystem,Name::" + sid + "/relationships/CIM_StorageVolume", nil, &resp)
	return resp,err
}

type PostDeviceMaskingGroupsContent struct {
	Content []struct {
		GroupName string `json: "GroupName"`
		Type int `json: "Type"`
		//Type 2 for Initator Group, Type 4 for Device Masking Group
		@Type string `json: "@type"`
	} `json:"content"`
}

//Create a Device Masking Group (Same method call for Create an Initiator Group, but different Type in Content)
func (smis *SMIS) PostDeviceMaskingGroups (req *PostVolumesReq, sid string) (resp *PostVolumesResp, err error){
	err = smis.query("POST","/ecom/edaa/root/emc/instances/Symm_ControllerConfigurationService/CreationClassName::Symm_ControllerConfigurationService,Name::EMCControllerConfigurationService,SystemCreationClassName::Symm_StorageSystem,SystemName::" + sid + "/action/CreateGroup", req, &resp)
	return resp,err
}

type PostVolumesReq struct {

}

type PostVolumesContent struct {
	Content []struct {
		ElementType string `json: "ElementType"`
		Size string `json: "Size"`
		ElementName string `json: "ElementName"`
		EMCNumberOfDevices string `json: "EMCNumberOfDevices"`
		@Type string `json: "@type"`
		//Add goal stuct later {@type, InstanceID}
	} `json:"content"`
}

type PostVolumesResp struct {

}

//Create a Storage Volume
func (smis *SMIS) PostVolumes(req *PostVolumesReq, sid string) (resp *PostVolumesResp, err error){
	err = smis.query("POST","/ecom/edaa/root/emc/instances/Symm_StorageConfigurationService/CreationClassName::Symm_StorageConfigurationService,Name::EMCStorageConfigurationService,SystemCreationClassName::Symm_StorageSystem,SystemName::" + sid + "/action/CreateOrModifyElementFromStoragePool", req, &resp)
	return resp,err
}

<|endoftext|>


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

package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string

// This represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "clira",
	Short: "A JIRA CLI interface",
	Long:  `clira is a CLI JIRA interface enabling user to list tickets from JIRA.`,
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	RootCmd.PersistentFlags().StringVar(
		&cfgFile,
		"config",
		"$HOME/.clira.yaml",
		"config file")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".clira") // name of config file (without extension)
	viper.AddConfigPath("$HOME")  // adding home directory as first search path
	viper.AutomaticEnv()          // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
<commit_msg>Fix bug when not specifying config file<commit_after>// Copyright © 2016 Ignasi Fosch
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "clira",
	Short: "A JIRA CLI interface",
	Long:  `clira is a CLI JIRA interface enabling user to list tickets from JIRA.`,
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	RootCmd.PersistentFlags().StringVar(
		&cfgFile,
		"config",
		"",
		"config file")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".clira") // name of config file (without extension)
	viper.AddConfigPath("$HOME")  // adding home directory as first search path
	viper.AutomaticEnv()          // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	} else {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package clocky

import (
	"fmt"
	"io"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"
	"template"
	"xml"

	"appengine"
	"appengine/memcache"
)

// WindChill returns the Celsius wind chill (2001 North American
// formula) for a given air temperature in degrees Celsius and a wind
// speed in km/h.
func WindChill(temp, speed float64) *float64 {
	if temp > 10 || speed <= 4.8 {
		return nil
	}
	chill := 13.12 + 0.6215*temp - 11.37*math.Pow(speed, 0.16) + 0.3965*temp*math.Pow(speed, 0.16)
	return &chill
}

func Cardinal(degrees int) string {
	switch {
	case degrees < 0:
		break
	case degrees < 23:
		return "N"
	case degrees < 68:
		return "NW"
	case degrees < 113:
		return "W"
	case degrees < 158:
		return "SW"
	case degrees < 203:
		return "S"
	case degrees < 248:
		return "SE"
	case degrees < 293:
		return "E"
	case degrees < 338:
		return "NE"
	case degrees < 360:
		return "N"
	}
	return ""
}

func Conditions(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "conditions")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	var dir string
	var speed, temp, chill *float64

	for _, line := range strings.Split(string(item.Value), "\n") {
		if len(line) != 116 || line[0] == '#' {
			continue
		}
		// FTPC1 is a C-MAN automated buoy near Crissy Field.
		if line[:5] != "FTPC1" {
			continue
		}
		if n, err := strconv.Atoi(strings.TrimSpace(line[40:43])); err != nil || n < 0 || n > 359 {
			c.Errorf("weather: bad wind direction in %q", line)
		} else {
			dir = Cardinal(n)
		}
		if n, err := strconv.Atof64(strings.TrimSpace(line[44:49])); err != nil {
			c.Errorf("weather: bad wind speed in %q", line)
		} else {
			n *= 3.6 // m/s to km/h
			speed = &n
		}
		if n, err := strconv.Atof64(strings.TrimSpace(line[87:92])); err != nil {
			c.Errorf("weather: bad temp in %q", line)
		} else {
			temp = &n
		}
		break
	}
	if temp != nil && speed != nil {
		chill = WindChill(*temp, *speed)
	}

	io.WriteString(w, `<div class=header>`)
	if temp != nil {
		// Don't round this, since we are using the value
		// directly from the data, not a converted value like
		// wind speed or a derived value like wind chill.
		fmt.Fprintf(w, `<span class=larger>%.1f°</span> `, *temp)
	}
	switch {
	case speed == nil:
		// Output nothing.
	case chill != nil && *chill < *temp-1:
		fmt.Fprintf(w, `wind chill %.1f°`, *chill+0.05)
	case *speed > 1:
		fmt.Fprintf(w, ` %s wind %d km/h`, dir, int(*speed+0.5))
	default:
		io.WriteString(w, `wind calm`)
	}
	io.WriteString(w, `</div>`)
}

var (
	nbspRegexp   = regexp.MustCompile(` [0-9]+\.`)
	thinspRegexp = regexp.MustCompile(`[0-9] (am|pm|km/h)`)
)

func Forecast(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "forecast")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	data := struct {
		Data []struct {
			Type       string `xml:"attr"`
			TimeLayout []struct {
				LayoutKey      string `xml:"layout-key"`
				StartValidTime []struct {
					PeriodName string `xml:"attr"`
				}
			}
			Parameters struct {
				WordedForecast struct {
					TimeLayout string   `xml:"attr"`
					Text       []string `xml:"name>text"`
				}
			}
		}
	}{}
	p := xml.NewParser(strings.NewReader(string(item.Value)))
	// NWS serves XML in ISO-8859-1 for no reason; the data is really ASCII.
	p.CharsetReader = func(charset string, input io.Reader) (io.Reader, os.Error) {
		return input, nil
	}
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%s", err)
		return
	}

	io.WriteString(w, `<div class=smaller style="text-align: left">`)
	for _, d := range data.Data {
		if d.Type != "forecast" {
			continue
		}
		var periods []string
		for _, tl := range d.TimeLayout {
			if tl.LayoutKey != d.Parameters.WordedForecast.TimeLayout {
				continue
			}
			for _, svt := range tl.StartValidTime {
				pn := svt.PeriodName
				pn = strings.Replace(pn, " Morning", " morning", -1)
				pn = strings.Replace(pn, " Afternoon", " afternoon", -1)
				pn = strings.Replace(pn, " Night", " night", -1)
				periods = append(periods, pn)
			}
		}
		texts := d.Parameters.WordedForecast.Text
		if len(texts) != len(periods) {
			c.Errorf("weather: len(texts) = %d, len(periods) = %d",
				len(texts), len(periods))
			continue
		}
		if len(texts) > 4 {
			texts = texts[:4]
		}
		for i, text := range texts {
			io.WriteString(w, `<div style="margin-bottom: 8px"><span class=header>`)
			template.HTMLEscape(w, []byte(periods[i]))
			io.WriteString(w, `:</span> `)

			spaceSubs := make(map[int]string)
			matches := nbspRegexp.FindAllStringIndex(text, -1)
			if len(matches) > 0 {
				for i := 0; i < len(matches[0]); i += 2 {
					spaceSubs[matches[0][i]] = " "
				}
			}
			matches = thinspRegexp.FindAllStringIndex(text, -1)
			if len(matches) > 0 {
				for i := 0; i < len(matches[0]); i += 2 {
					spaceSubs[matches[0][i]+1] = `<span style="white-space: nowrap"> </span>`
				}
			}
			for i, ch := range text {
				sub, ok := spaceSubs[i]
				if ok {
					io.WriteString(w, sub)
				} else {
					io.WriteString(w, string(ch))
				}
			}

			io.WriteString(w, `</div>`)
		}
	}
	io.WriteString(w, `</div>`)
}
<commit_msg>Tweak line breaks in weather forecast.<commit_after>package clocky

import (
	"fmt"
	"io"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"
	"template"
	"xml"

	"appengine"
	"appengine/memcache"
)

// WindChill returns the Celsius wind chill (2001 North American
// formula) for a given air temperature in degrees Celsius and a wind
// speed in km/h.
func WindChill(temp, speed float64) *float64 {
	if temp > 10 || speed <= 4.8 {
		return nil
	}
	chill := 13.12 + 0.6215*temp - 11.37*math.Pow(speed, 0.16) + 0.3965*temp*math.Pow(speed, 0.16)
	return &chill
}

func Cardinal(degrees int) string {
	switch {
	case degrees < 0:
		break
	case degrees < 23:
		return "N"
	case degrees < 68:
		return "NW"
	case degrees < 113:
		return "W"
	case degrees < 158:
		return "SW"
	case degrees < 203:
		return "S"
	case degrees < 248:
		return "SE"
	case degrees < 293:
		return "E"
	case degrees < 338:
		return "NE"
	case degrees < 360:
		return "N"
	}
	return ""
}

func Conditions(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "conditions")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	var dir string
	var speed, temp, chill *float64

	for _, line := range strings.Split(string(item.Value), "\n") {
		if len(line) != 116 || line[0] == '#' {
			continue
		}
		// FTPC1 is a C-MAN automated buoy near Crissy Field.
		if line[:5] != "FTPC1" {
			continue
		}
		if n, err := strconv.Atoi(strings.TrimSpace(line[40:43])); err != nil || n < 0 || n > 359 {
			c.Errorf("weather: bad wind direction in %q", line)
		} else {
			dir = Cardinal(n)
		}
		if n, err := strconv.Atof64(strings.TrimSpace(line[44:49])); err != nil {
			c.Errorf("weather: bad wind speed in %q", line)
		} else {
			n *= 3.6 // m/s to km/h
			speed = &n
		}
		if n, err := strconv.Atof64(strings.TrimSpace(line[87:92])); err != nil {
			c.Errorf("weather: bad temp in %q", line)
		} else {
			temp = &n
		}
		break
	}
	if temp != nil && speed != nil {
		chill = WindChill(*temp, *speed)
	}

	io.WriteString(w, `<div class=header>`)
	if temp != nil {
		// Don't round this, since we are using the value
		// directly from the data, not a converted value like
		// wind speed or a derived value like wind chill.
		fmt.Fprintf(w, `<span class=larger>%.1f°</span> `, *temp)
	}
	switch {
	case speed == nil:
		// Output nothing.
	case chill != nil && *chill < *temp-1:
		fmt.Fprintf(w, `wind chill %.1f°`, *chill+0.05)
	case *speed > 1:
		fmt.Fprintf(w,
			` %s wind <span style="white-space: nowrap">%d km/\u2060h</span>`,
			dir, int(*speed+0.5))
	default:
		io.WriteString(w, `wind calm`)
	}
	io.WriteString(w, `</div>`)
}

var (
	nbspRegexp   = regexp.MustCompile(` [0-9]+\.`)
	thinspRegexp = regexp.MustCompile(`[0-9] (am|pm|km/h)`)
)

func Forecast(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "forecast")
	if err != nil {
		c.Errorf("%s", err)
		return
	}

	data := struct {
		Data []struct {
			Type       string `xml:"attr"`
			TimeLayout []struct {
				LayoutKey      string `xml:"layout-key"`
				StartValidTime []struct {
					PeriodName string `xml:"attr"`
				}
			}
			Parameters struct {
				WordedForecast struct {
					TimeLayout string   `xml:"attr"`
					Text       []string `xml:"name>text"`
				}
			}
		}
	}{}
	p := xml.NewParser(strings.NewReader(string(item.Value)))
	// NWS serves XML in ISO-8859-1 for no reason; the data is really ASCII.
	p.CharsetReader = func(charset string, input io.Reader) (io.Reader, os.Error) {
		return input, nil
	}
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%s", err)
		return
	}

	io.WriteString(w, `<div class=smaller style="text-align: left">`)
	for _, d := range data.Data {
		if d.Type != "forecast" {
			continue
		}
		var periods []string
		for _, tl := range d.TimeLayout {
			if tl.LayoutKey != d.Parameters.WordedForecast.TimeLayout {
				continue
			}
			for _, svt := range tl.StartValidTime {
				pn := svt.PeriodName
				pn = strings.Replace(pn, " Morning", " morning", -1)
				pn = strings.Replace(pn, " Afternoon", " afternoon", -1)
				pn = strings.Replace(pn, " Night", " night", -1)
				periods = append(periods, pn)
			}
		}
		texts := d.Parameters.WordedForecast.Text
		if len(texts) != len(periods) {
			c.Errorf("weather: len(texts) = %d, len(periods) = %d",
				len(texts), len(periods))
			continue
		}
		if len(texts) > 4 {
			texts = texts[:4]
		}
		for i, text := range texts {
			io.WriteString(w, `<div style="margin-bottom: 8px"><span class=header>`)
			template.HTMLEscape(w, []byte(periods[i]))
			io.WriteString(w, `:</span> `)

			text = strings.Replace(text, "km/h", "km/\u2060h")
			spaceSubs := make(map[int]string)
			matches := nbspRegexp.FindAllStringIndex(text, -1)
			if len(matches) > 0 {
				for i := 0; i < len(matches[0]); i += 2 {
					spaceSubs[matches[0][i]] = " "
				}
			}
			matches = thinspRegexp.FindAllStringIndex(text, -1)
			if len(matches) > 0 {
				for i := 0; i < len(matches[0]); i += 2 {
					spaceSubs[matches[0][i]+1] = `<span style="white-space: nowrap"> </span>`
				}
			}
			for i, ch := range text {
				sub, ok := spaceSubs[i]
				if ok {
					io.WriteString(w, sub)
				} else {
					io.WriteString(w, string(ch))
				}
			}

			io.WriteString(w, `</div>`)
		}
	}
	io.WriteString(w, `</div>`)
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"fmt"
	"strings"

	"gopkg.in/guregu/null.v3"

	"github.com/kelseyhightower/envconfig"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/consts"
	"github.com/loadimpact/k6/loader"
	"github.com/loadimpact/k6/stats/cloud"
	"github.com/loadimpact/k6/stats/csv"
	"github.com/loadimpact/k6/stats/datadog"
	"github.com/loadimpact/k6/stats/influxdb"
	jsonc "github.com/loadimpact/k6/stats/json"
	"github.com/loadimpact/k6/stats/kafka"
	"github.com/loadimpact/k6/stats/statsd"
	"github.com/loadimpact/k6/stats/statsd/common"
	"github.com/pkg/errors"
	"github.com/spf13/afero"
)

const (
	collectorInfluxDB = "influxdb"
	collectorJSON     = "json"
	collectorKafka    = "kafka"
	collectorCloud    = "cloud"
	collectorStatsD   = "statsd"
	collectorDatadog  = "datadog"
	collectorCSV      = "csv"
)

func parseCollector(s string) (t, arg string) {
	parts := strings.SplitN(s, "=", 2)
	switch len(parts) {
	case 0:
		return "", ""
	case 1:
		return parts[0], ""
	default:
		return parts[0], parts[1]
	}
}

func newCollector(
	collectorName, arg string, src *loader.SourceData, conf Config, executionPlan []lib.ExecutionStep,
) (lib.Collector, error) {
	getCollector := func() (lib.Collector, error) {
		switch collectorName {
		case collectorJSON:
			return jsonc.New(afero.NewOsFs(), arg)
		case collectorInfluxDB:
			config := influxdb.NewConfig().Apply(conf.Collectors.InfluxDB)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			urlConfig, err := influxdb.ParseURL(arg)
			if err != nil {
				return nil, err
			}
			config = config.Apply(urlConfig)
			return influxdb.New(config)
		case collectorCloud:
			config := cloud.NewConfig().Apply(conf.Collectors.Cloud)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				config.Name = null.StringFrom(arg)
			}
			return cloud.New(config, src, conf.Options, executionPlan, consts.Version)
		case collectorKafka:
			config := kafka.NewConfig().Apply(conf.Collectors.Kafka)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				cmdConfig, err := kafka.ParseArg(arg)
				if err != nil {
					return nil, err
				}
				config = config.Apply(cmdConfig)
			}
			return kafka.New(config)
		case collectorStatsD:
			config := common.NewConfig().Apply(conf.Collectors.StatsD)
			if err := envconfig.Process("k6_statsd", &config); err != nil {
				return nil, err
			}
			return statsd.New(config)
		case collectorDatadog:
			config := datadog.NewConfig().Apply(conf.Collectors.Datadog)
			if err := envconfig.Process("k6_datadog", &config); err != nil {
				return nil, err
			}
			return datadog.New(config)
		case collectorCSV:
			config := csv.NewConfig().Apply(conf.Collectors.CSV)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				cmdConfig, err := csv.ParseArg(arg)
				if err != nil {
					return nil, err
				}
				config = config.Apply(cmdConfig)
			}
			return csv.New(afero.NewOsFs(), conf.SystemTags, config)
		default:
			return nil, errors.Errorf("unknown output type: %s", collectorName)
		}
	}

	collector, err := getCollector()
	if err != nil {
		return collector, err
	}

	// Check if all required tags are present
	missingRequiredTags := []string{}
	for reqTag := range collector.GetRequiredSystemTags() {
		if !conf.SystemTags[reqTag] {
			missingRequiredTags = append(missingRequiredTags, reqTag)
		}
	}
	if len(missingRequiredTags) > 0 {
		return collector, fmt.Errorf(
			"the specified collector '%s' needs the following system tags enabled: %s",
			collectorName,
			strings.Join(missingRequiredTags, ", "),
		)
	}

	return collector, nil
}
<commit_msg>Silence a linter error<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"fmt"
	"strings"

	"gopkg.in/guregu/null.v3"

	"github.com/kelseyhightower/envconfig"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/consts"
	"github.com/loadimpact/k6/loader"
	"github.com/loadimpact/k6/stats/cloud"
	"github.com/loadimpact/k6/stats/csv"
	"github.com/loadimpact/k6/stats/datadog"
	"github.com/loadimpact/k6/stats/influxdb"
	jsonc "github.com/loadimpact/k6/stats/json"
	"github.com/loadimpact/k6/stats/kafka"
	"github.com/loadimpact/k6/stats/statsd"
	"github.com/loadimpact/k6/stats/statsd/common"
	"github.com/pkg/errors"
	"github.com/spf13/afero"
)

const (
	collectorInfluxDB = "influxdb"
	collectorJSON     = "json"
	collectorKafka    = "kafka"
	collectorCloud    = "cloud"
	collectorStatsD   = "statsd"
	collectorDatadog  = "datadog"
	collectorCSV      = "csv"
)

func parseCollector(s string) (t, arg string) {
	parts := strings.SplitN(s, "=", 2)
	switch len(parts) {
	case 0:
		return "", ""
	case 1:
		return parts[0], ""
	default:
		return parts[0], parts[1]
	}
}

//TODO: totally refactor this...
//nolint:funlen
func newCollector(
	collectorName, arg string, src *loader.SourceData, conf Config, executionPlan []lib.ExecutionStep,
) (lib.Collector, error) {
	getCollector := func() (lib.Collector, error) {
		switch collectorName {
		case collectorJSON:
			return jsonc.New(afero.NewOsFs(), arg)
		case collectorInfluxDB:
			config := influxdb.NewConfig().Apply(conf.Collectors.InfluxDB)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			urlConfig, err := influxdb.ParseURL(arg)
			if err != nil {
				return nil, err
			}
			config = config.Apply(urlConfig)
			return influxdb.New(config)
		case collectorCloud:
			config := cloud.NewConfig().Apply(conf.Collectors.Cloud)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				config.Name = null.StringFrom(arg)
			}
			return cloud.New(config, src, conf.Options, executionPlan, consts.Version)
		case collectorKafka:
			config := kafka.NewConfig().Apply(conf.Collectors.Kafka)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				cmdConfig, err := kafka.ParseArg(arg)
				if err != nil {
					return nil, err
				}
				config = config.Apply(cmdConfig)
			}
			return kafka.New(config)
		case collectorStatsD:
			config := common.NewConfig().Apply(conf.Collectors.StatsD)
			if err := envconfig.Process("k6_statsd", &config); err != nil {
				return nil, err
			}
			return statsd.New(config)
		case collectorDatadog:
			config := datadog.NewConfig().Apply(conf.Collectors.Datadog)
			if err := envconfig.Process("k6_datadog", &config); err != nil {
				return nil, err
			}
			return datadog.New(config)
		case collectorCSV:
			config := csv.NewConfig().Apply(conf.Collectors.CSV)
			if err := envconfig.Process("k6", &config); err != nil {
				return nil, err
			}
			if arg != "" {
				cmdConfig, err := csv.ParseArg(arg)
				if err != nil {
					return nil, err
				}
				config = config.Apply(cmdConfig)
			}
			return csv.New(afero.NewOsFs(), conf.SystemTags, config)
		default:
			return nil, errors.Errorf("unknown output type: %s", collectorName)
		}
	}

	collector, err := getCollector()
	if err != nil {
		return collector, err
	}

	// Check if all required tags are present
	missingRequiredTags := []string{}
	for reqTag := range collector.GetRequiredSystemTags() {
		if !conf.SystemTags[reqTag] {
			missingRequiredTags = append(missingRequiredTags, reqTag)
		}
	}
	if len(missingRequiredTags) > 0 {
		return collector, fmt.Errorf(
			"the specified collector '%s' needs the following system tags enabled: %s",
			collectorName,
			strings.Join(missingRequiredTags, ", "),
		)
	}

	return collector, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"testing"
)

func TestCityClean(t *testing.T) {
	t.Log("Cleaning city value... (expected result: 'new-york')")
	cleanCity := cityClean("New York")

	if cleanCity != "new-york" {
		t.Errorf("Expected result of 'new-york' but it was %s instead.", cleanCity)
	}
}

func TestEventDataPath(t *testing.T) {
	t.Log("Testing eventDataPath function... (expected result: '/Users/mattstratton/src/devopsdays-web/data/events/2018-new-york.yml')")
	testDataPath := eventDataPath(webdir, "New York", "2018")
	if testDataPath != "/Users/mattstratton/src/devopsdays-web/data/events/2018-new-york.yml" {
		t.Errorf("Expected result of '/Users/mattstratton/src/devopsdays-web/data/events/2018-new-york.yml' but it was %s instead.", testDataPath)
	}
}

func TestEventContentPath(t *testing.T) {
	t.Log("Testing eventContentPath function... (expected result: '/Users/mattstratton/src/devopsdays-web/content/events/2018-new-york')")
	testContentPath := eventContentPath(webdir, "New York", "2018")
	if testContentPath != "/Users/mattstratton/src/devopsdays-web/content/events/2018-new-york" {
		t.Errorf("Expected result of '/Users/mattstratton/src/devopsdays-web/content/events/2018-new-york' but it was %s instead.", testContentPath)
	}
}

func TestValidateField(t *testing.T) {
	t.Log("Testing validateField() function")
	if v := validateField("Chicago", "city"); v != true {
		t.Error("Valid city did not pass validation test in validateField")
	}
	if v := validateField("3yl0RmG1wU8q5TeDPKZEsNU3E54nyYf5MNhGhzqcxhoLJkeckXCa1saWCPM24YhwIteGEUjLW8S715WkoDvt3vFsMaVeYXCUZWNL", "city"); v == true {
		t.Error("Invalid city passed validation test in validateField")
	}
	if v := validateField("2017", "year"); v != true {
		t.Error("Valid year did not pass validation test in validateField")
	}
	if v := validateField("19008", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("2011", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("2031", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("devopsdays", "twitter"); v != true {
		t.Error("Valid twitter did not pass validation test in validateField")
	}
	if v := validateField("devops days", "twitter"); v == true {
		t.Error("Invalid twitter passed validation test in validateField")
	}
}
<commit_msg>Fix tests<commit_after>package cmd

import (
	"testing"
)

func TestCityClean(t *testing.T) {
	t.Log("Cleaning city value... (expected result: 'new-york')")
	cleanCity := cityClean("New York")

	if cleanCity != "new-york" {
		t.Errorf("Expected result of 'new-york' but it was %s instead.", cleanCity)
	}
}

func TestEventDataPath(t *testing.T) {
	t.Log("Testing eventDataPath function... (expected result: '/Users/mattstratton/src/devopsdays-web/data/events/2018-new-york.yml')")
	testDataPath := eventDataPath(webdir, "New York", "2018")
	if testDataPath != webdir+"/data/events/2018-new-york.yml" {
		t.Errorf("Expected result of '/Users/mattstratton/src/devopsdays-web/data/events/2018-new-york.yml' but it was %s instead.", testDataPath)
	}
}

func TestEventContentPath(t *testing.T) {
	t.Log("Testing eventContentPath function... (expected result: '/Users/mattstratton/src/devopsdays-web/content/events/2018-new-york')")
	testContentPath := eventContentPath(webdir, "New York", "2018")
	if testContentPath != webdir+"/content/events/2018-new-york" {
		t.Errorf("Expected result of '/Users/mattstratton/src/devopsdays-web/content/events/2018-new-york' but it was %s instead.", testContentPath)
	}
}

func TestValidateField(t *testing.T) {
	t.Log("Testing validateField() function")
	if v := validateField("Chicago", "city"); v != true {
		t.Error("Valid city did not pass validation test in validateField")
	}
	if v := validateField("3yl0RmG1wU8q5TeDPKZEsNU3E54nyYf5MNhGhzqcxhoLJkeckXCa1saWCPM24YhwIteGEUjLW8S715WkoDvt3vFsMaVeYXCUZWNL", "city"); v == true {
		t.Error("Invalid city passed validation test in validateField")
	}
	if v := validateField("2017", "year"); v != true {
		t.Error("Valid year did not pass validation test in validateField")
	}
	if v := validateField("19008", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("2011", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("2031", "year"); v == true {
		t.Error("Invalid year passed validation test in validateField")
	}
	if v := validateField("devopsdays", "twitter"); v != true {
		t.Error("Valid twitter did not pass validation test in validateField")
	}
	if v := validateField("devops days", "twitter"); v == true {
		t.Error("Invalid twitter passed validation test in validateField")
	}
}
<|endoftext|>


<commit_before>package main

import "io"

type DeviceConfig interface {
	Name() string
	LoadJSON(io.Reader) error
	Save() error
	ToJSON(io.Writer) error
}
<commit_msg>Add a dummy main function<commit_after>package main

import (
	"fmt"
	"io"
)

type DeviceConfig interface {
	Name() string
	LoadJSON(io.Reader) error
	Save() error
	ToJSON(io.Writer) error
}

func main() {
	fmt.Println("hello box")
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/charmbracelet/glamour"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var issueShowCmd = &cobra.Command{
	Use:        "show [remote] <id>",
	Aliases:    []string{"get"},
	ArgAliases: []string{"s"},
	Short:      "Describe an issue",
	Long:       ``,
	Run: func(cmd *cobra.Command, args []string) {
		rn, issueNum, err := parseArgs(args)
		if err != nil {
			log.Fatal(err)
		}

		issue, err := lab.IssueGet(rn, int(issueNum))
		if err != nil {
			log.Fatal(err)
		}

		glamour_e, _ := cmd.Flags().GetBool("glamour")

		printIssue(issue, rn, glamour_e)

		showComments, _ := cmd.Flags().GetBool("comments")
		if showComments {
			discussions, err := lab.IssueListDiscussions(rn, int(issueNum))
			if err != nil {
				log.Fatal(err)
			}

			printDiscussions(discussions)
		}
	},
}

func printIssue(issue *gitlab.Issue, project string, glamour_enabled bool) {
	milestone := "None"
	timestats := "None"
	dueDate := "None"
	state := map[string]string{
		"opened": "Open",
		"closed": "Closed",
	}[issue.State]
	if issue.Milestone != nil {
		milestone = issue.Milestone.Title
	}
	if issue.TimeStats != nil && issue.TimeStats.HumanTimeEstimate != "" &&
		issue.TimeStats.HumanTotalTimeSpent != "" {
		timestats = fmt.Sprintf(
			"Estimated %s, Spent %s",
			issue.TimeStats.HumanTimeEstimate,
			issue.TimeStats.HumanTotalTimeSpent)
	}
	if issue.DueDate != nil {
		dueDate = time.Time(*issue.DueDate).String()
	}
	assignees := make([]string, len(issue.Assignees))
	if len(issue.Assignees) > 0 && issue.Assignees[0].Username != "" {
		for i, a := range issue.Assignees {
			assignees[i] = a.Username
		}
	}

	if glamour_enabled {
		r, _ := glamour.NewTermRenderer(
			glamour.WithStandardStyle("dark"),
		)

		issue.Description, _ = r.Render(issue.Description)
	}

	fmt.Printf(`
#%d %s
===================================
%s
-----------------------------------
Project: %s
Status: %s
Assignees: %s
Author: %s
Milestone: %s
Due Date: %s
Time Stats: %s
Labels: %s
WebURL: %s
`,
		issue.IID, issue.Title, issue.Description, project, state, strings.Join(assignees, ", "),
		issue.Author.Username, milestone, dueDate, timestats,
		strings.Join(issue.Labels, ", "), issue.WebURL,
	)
}

func printDiscussions(discussions []*gitlab.Discussion) {
	// for available fields, see
	// https://godoc.org/github.com/xanzy/go-gitlab#Note
	// https://godoc.org/github.com/xanzy/go-gitlab#Discussion
	for _, discussion := range discussions {
		for i, note := range discussion.Notes {

			// skip system notes
			if note.System {
				continue
			}

			indentHeader, indentNote := "", ""
			commented := "commented"

			if !discussion.IndividualNote {
				indentNote = "    "

				if i == 0 {
					commented = "started a discussion"
				} else {
					indentHeader = "    "
				}
			}

			fmt.Printf(`
%s-----------------------------------
%s%s %s at %s

%s%s
`,
				indentHeader,
				indentHeader, note.Author.Username, commented, time.Time(*note.CreatedAt).String(),
				indentNote, note.Body)
		}
	}
}

func init() {
	issueShowCmd.MarkZshCompPositionalArgumentCustom(1, "__lab_completion_remote")
	issueShowCmd.MarkZshCompPositionalArgumentCustom(2, "__lab_completion_issue $words[2]")
	issueShowCmd.MarkZshCompPositionalArgumentCustom(1, "__lab_completion_issue")
	issueShowCmd.Flags().BoolP("comments", "c", false, "Show comments for the issue")
	issueShowCmd.Flags().BoolP("glamour", "g", false, "Use glamour to print the issue description")
	issueCmd.AddCommand(issueShowCmd)
}
<commit_msg>Glamour Support can now be disabled with -G instead of enabled with -g<commit_after>package cmd

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/charmbracelet/glamour"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var issueShowCmd = &cobra.Command{
	Use:        "show [remote] <id>",
	Aliases:    []string{"get"},
	ArgAliases: []string{"s"},
	Short:      "Describe an issue",
	Long:       ``,
	Run: func(cmd *cobra.Command, args []string) {
		rn, issueNum, err := parseArgs(args)
		if err != nil {
			log.Fatal(err)
		}

		issue, err := lab.IssueGet(rn, int(issueNum))
		if err != nil {
			log.Fatal(err)
		}

		glamour_disabled, _ := cmd.Flags().GetBool("no-glamour")

		printIssue(issue, rn, !glamour_disabled)

		showComments, _ := cmd.Flags().GetBool("comments")
		if showComments {
			discussions, err := lab.IssueListDiscussions(rn, int(issueNum))
			if err != nil {
				log.Fatal(err)
			}

			printDiscussions(discussions)
		}
	},
}

func printIssue(issue *gitlab.Issue, project string, glamour_enabled bool) {
	milestone := "None"
	timestats := "None"
	dueDate := "None"
	state := map[string]string{
		"opened": "Open",
		"closed": "Closed",
	}[issue.State]
	if issue.Milestone != nil {
		milestone = issue.Milestone.Title
	}
	if issue.TimeStats != nil && issue.TimeStats.HumanTimeEstimate != "" &&
		issue.TimeStats.HumanTotalTimeSpent != "" {
		timestats = fmt.Sprintf(
			"Estimated %s, Spent %s",
			issue.TimeStats.HumanTimeEstimate,
			issue.TimeStats.HumanTotalTimeSpent)
	}
	if issue.DueDate != nil {
		dueDate = time.Time(*issue.DueDate).String()
	}
	assignees := make([]string, len(issue.Assignees))
	if len(issue.Assignees) > 0 && issue.Assignees[0].Username != "" {
		for i, a := range issue.Assignees {
			assignees[i] = a.Username
		}
	}

	if glamour_enabled {
		r, _ := glamour.NewTermRenderer(
			glamour.WithStandardStyle("dark"),
		)

		issue.Description, _ = r.Render(issue.Description)
	}

	fmt.Printf(`
#%d %s
===================================
%s
-----------------------------------
Project: %s
Status: %s
Assignees: %s
Author: %s
Milestone: %s
Due Date: %s
Time Stats: %s
Labels: %s
WebURL: %s
`,
		issue.IID, issue.Title, issue.Description, project, state, strings.Join(assignees, ", "),
		issue.Author.Username, milestone, dueDate, timestats,
		strings.Join(issue.Labels, ", "), issue.WebURL,
	)
}

func printDiscussions(discussions []*gitlab.Discussion) {
	// for available fields, see
	// https://godoc.org/github.com/xanzy/go-gitlab#Note
	// https://godoc.org/github.com/xanzy/go-gitlab#Discussion
	for _, discussion := range discussions {
		for i, note := range discussion.Notes {

			// skip system notes
			if note.System {
				continue
			}

			indentHeader, indentNote := "", ""
			commented := "commented"

			if !discussion.IndividualNote {
				indentNote = "    "

				if i == 0 {
					commented = "started a discussion"
				} else {
					indentHeader = "    "
				}
			}

			fmt.Printf(`
%s-----------------------------------
%s%s %s at %s

%s%s
`,
				indentHeader,
				indentHeader, note.Author.Username, commented, time.Time(*note.CreatedAt).String(),
				indentNote, note.Body)
		}
	}
}

func init() {
	issueShowCmd.MarkZshCompPositionalArgumentCustom(1, "__lab_completion_remote")
	issueShowCmd.MarkZshCompPositionalArgumentCustom(2, "__lab_completion_issue $words[2]")
	issueShowCmd.MarkZshCompPositionalArgumentCustom(1, "__lab_completion_issue")
	issueShowCmd.Flags().BoolP("comments", "c", false, "Show comments for the issue")
	issueShowCmd.Flags().BoolP("no-glamour", "G", false, "Don't use glamour to print the issue description")
	issueCmd.AddCommand(issueShowCmd)
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/garyburd/redigo/redis"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/middleware"
	"github.com/hellofresh/janus/pkg/store"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

var (
	err          error
	globalConfig *config.Specification
	accessor     *middleware.DatabaseAccessor
	storage      store.Store
	statsdClient *statsd.Client
)

// initializes the global configuration
func init() {
	globalConfig, err = config.LoadEnv()
	if nil != err {
		log.Panic(err.Error())
	}
}

// initializes the basic configuration for the log wrapper
func init() {
	level, err := log.ParseLevel(strings.ToLower(globalConfig.LogLevel))
	if err != nil {
		log.Error("Error getting level", err)
	}

	log.SetLevel(level)
	log.SetFormatter(&logrus_logstash.LogstashFormatter{
		Type:            globalConfig.Application.Name,
		TimestampFormat: time.RFC3339Nano,
	})
}

// initializes a DB connection
func init() {
	accessor, err = middleware.InitDB(globalConfig.Database.DSN)
	if err != nil {
		log.Fatalf("Couldn't connect to the mongodb database: %s", err.Error())
	}
}

// initializes a storage
func init() {
	// Create a Redis pool.
	pool := &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial:        func() (redis.Conn, error) { return redis.Dial("tcp", globalConfig.StorageDSN) },
	}

	dsn := globalConfig.StorageDSN
	log.Debugf("Trying to connect to redis pool: %s", dsn)
	storage, err = store.NewRedisStore(pool)
	if err != nil {
		log.Fatalf("Couldn't connect to the redis pool: %s", err.Error())
	}
}

// initializes new StatsD client if it enabled
func init() {
	var options []statsd.Option
	statsdConfig := globalConfig.Statsd

	log.Debugf("Trying to connect to statsd instance: %s", statsdConfig.DSN)
	if len(statsdConfig.DSN) == 0 {
		log.Debug("Statsd DSN not provided, client will be muted")
		options = append(options, statsd.Mute(true))
	} else {
		options = append(options, statsd.Address(statsdConfig.DSN))
	}

	if len(statsdConfig.Prefix) > 0 {
		options = append(options, statsd.Prefix(statsdConfig.Prefix))
	}

	statsdClient, err = statsd.New(options...)
	if err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"dsn":    statsdConfig.DSN,
				"prefix": statsdConfig.Prefix,
			}).Warning("An error occurred while connecting to StatsD. Client will be muted.")
	}
}
<commit_msg>Changed to DialURL to support #86<commit_after>package main

import (
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/garyburd/redigo/redis"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/middleware"
	"github.com/hellofresh/janus/pkg/store"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

var (
	err          error
	globalConfig *config.Specification
	accessor     *middleware.DatabaseAccessor
	storage      store.Store
	statsdClient *statsd.Client
)

// initializes the global configuration
func init() {
	globalConfig, err = config.LoadEnv()
	if nil != err {
		log.Panic(err.Error())
	}
}

// initializes the basic configuration for the log wrapper
func init() {
	level, err := log.ParseLevel(strings.ToLower(globalConfig.LogLevel))
	if err != nil {
		log.Error("Error getting level", err)
	}

	log.SetLevel(level)
	log.SetFormatter(&logrus_logstash.LogstashFormatter{
		Type:            globalConfig.Application.Name,
		TimestampFormat: time.RFC3339Nano,
	})
}

// initializes a DB connection
func init() {
	accessor, err = middleware.InitDB(globalConfig.Database.DSN)
	if err != nil {
		log.Fatalf("Couldn't connect to the mongodb database: %s", err.Error())
	}
}

// initializes a storage
func init() {
	// Create a Redis pool.
	pool := &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial:        func() (redis.Conn, error) { return redis.DialURL(globalConfig.StorageDSN) },
	}

	dsn := globalConfig.StorageDSN
	log.Debugf("Trying to connect to redis pool: %s", dsn)
	storage, err = store.NewRedisStore(pool)
	if err != nil {
		log.Fatalf("Couldn't connect to the redis pool: %s", err.Error())
	}
}

// initializes new StatsD client if it enabled
func init() {
	var options []statsd.Option
	statsdConfig := globalConfig.Statsd

	log.Debugf("Trying to connect to statsd instance: %s", statsdConfig.DSN)
	if len(statsdConfig.DSN) == 0 {
		log.Debug("Statsd DSN not provided, client will be muted")
		options = append(options, statsd.Mute(true))
	} else {
		options = append(options, statsd.Address(statsdConfig.DSN))
	}

	if len(statsdConfig.Prefix) > 0 {
		options = append(options, statsd.Prefix(statsdConfig.Prefix))
	}

	statsdClient, err = statsd.New(options...)
	if err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"dsn":    statsdConfig.DSN,
				"prefix": statsdConfig.Prefix,
			}).Warning("An error occurred while connecting to StatsD. Client will be muted.")
	}
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"regexp"

	"github.com/spf13/cobra"
	"github.com/google/go-github/github"
)

// rLatestUrlCmd represents the rLatestUrl command
var rLatestUrlCmd = &cobra.Command{
	Use:   "rLatestUrl",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: func(cmd *cobra.Command, args []string) {
		re := regexp.MustCompile(ghRegex)

		client := github.NewClient(nil)
		opt := &github.ListOptions{}
		releases, _, err := client.Repositories.ListReleases(ghOrg, ghRepo, opt)
		if err != nil {
			fmt.Println(err)
		}
		var cnt int
		cnt = 0
		for _, release := range releases {
			preRelease := *release.Prerelease
			draft := *release.Draft
			if ! (preRelease || draft) {
				cnt = cnt + 1
				for _, asset := range release.Assets {
					if ghLimit == 0 || cnt < ghLimit {
						if re.MatchString(*asset.Name) {
							fmt.Println(*asset.BrowserDownloadURL)
						}
					}
				}
			}
		}
	},
}

func init() {
	RootCmd.AddCommand(rLatestUrlCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// rLatestUrlCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// rLatestUrlCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

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

package cmd

import (
	"fmt"
	"regexp"

	"github.com/spf13/cobra"
	"github.com/google/go-github/github"
)

// rLatestUrlCmd represents the rLatestUrl command
var rLatestUrlCmd = &cobra.Command{
	Use:   "rLatestUrl",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: func(cmd *cobra.Command, args []string) {
		re := regexp.MustCompile(ghRegex)

		client := github.NewClient(nil)
		opt := &github.ListOptions{}
		releases, _, err := client.Repositories.ListReleases(ghOrg, ghRepo, opt)
		if err != nil {
			fmt.Println(err)
		}
		var cnt int
		cnt = 0
		for _, release := range releases {
			preRelease := *release.Prerelease
			draft := *release.Draft
			if ghLimit == 0 || cnt < ghLimit {
				cnt = cnt + 1
				if ! (preRelease || draft) {
					for _, asset := range release.Assets {
						if re.MatchString(*asset.Name) {
							fmt.Println(*asset.BrowserDownloadURL)
						}
					}
				}
			}
		}
	},
}

func init() {
	RootCmd.AddCommand(rLatestUrlCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// rLatestUrlCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// rLatestUrlCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/cmd"
	"github.com/globocom/tsuru/cmd/tsuru-base"
	"io/ioutil"
	"launchpad.net/gnuflag"
	"net/http"
)

type AppCreate struct {
	cmd.Command
	memory int
	swap   int
	fs     *gnuflag.FlagSet
}

func (c *AppCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "app-create",
		Usage:   "app-create <appname> <platform> [--memory/-m memory_in_mb] [--swap/-s swap_in_mb]",
		Desc:    "create a new app.",
		MinArgs: 2,
	}
}

func (c *AppCreate) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		infoMessage := "The maximum amount of memory reserved to each container for this app"
		c.fs = gnuflag.NewFlagSet("", gnuflag.ExitOnError)
		c.fs.IntVar(&c.memory, "memory", 0, infoMessage)
		c.fs.IntVar(&c.memory, "m", 0, infoMessage)
		infoMessage = "The maximum amount of swap reserved to each container for this app"
		c.fs = gnuflag.NewFlagSet("", gnuflag.ExitOnError)
		c.fs.IntVar(&c.swap, "swap", 0, infoMessage)
		c.fs.IntVar(&c.swap, "s", 0, infoMessage)
	}
	return c.fs
}

func (c *AppCreate) Run(context *cmd.Context, client *cmd.Client) error {
	appName := context.Args[0]
	platform := context.Args[1]
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s","platform":"%s","memory":"%d","swap":"%d"}`, appName, platform, c.memory, c.swap))
	url, err := cmd.GetURL("/apps")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, b)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, "App %q is being created!\n", appName)
	fmt.Fprintln(context.Stdout, "Use app-info to check the status of the app and its units.")
	fmt.Fprintf(context.Stdout, "Your repository for %q project is %q\n", appName, out["repository_url"])
	return nil
}

type AppRemove struct {
	tsuru.GuessingCommand
	yes bool
	fs  *gnuflag.FlagSet
}

func (c *AppRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "app-remove",
		Usage: "app-remove [--app appname] [--assume-yes]",
		Desc: `removes an app.

If you don't provide the app name, tsuru will try to guess it.`,
		MinArgs: 0,
	}
}

func (c *AppRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	var answer string
	if !c.yes {
		fmt.Fprintf(context.Stdout, `Are you sure you want to remove app "%s"? (y/n) `, appName)
		fmt.Fscanf(context.Stdin, "%s", &answer)
		if answer != "y" {
			fmt.Fprintln(context.Stdout, "Abort.")
			return nil
		}
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, `App "%s" successfully removed!`+"\n", appName)
	return nil
}

func (c *AppRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		c.fs.BoolVar(&c.yes, "assume-yes", false, "Don't ask for confirmation, just remove the app.")
		c.fs.BoolVar(&c.yes, "y", false, "Don't ask for confirmation, just remove the app.")
	}
	return c.fs
}

type UnitAdd struct {
	tsuru.GuessingCommand
}

func (c *UnitAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-add",
		Usage:   "unit-add <# of units> [--app appname]",
		Desc:    "add new units to an app.",
		MinArgs: 1,
	}
}

func (c *UnitAdd) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("PUT", url, bytes.NewBufferString(context.Args[0]))
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully added!")
	return nil
}

type UnitRemove struct {
	tsuru.GuessingCommand
}

func (c *UnitRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-remove",
		Usage:   "unit-remove <# of units> [--app appname]",
		Desc:    "remove units from an app.",
		MinArgs: 1,
	}
}

func (c *UnitRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	body := bytes.NewBufferString(context.Args[0])
	request, err := http.NewRequest("DELETE", url, body)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully removed!")
	return nil
}
<commit_msg>Fix small bug<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/cmd"
	"github.com/globocom/tsuru/cmd/tsuru-base"
	"io/ioutil"
	"launchpad.net/gnuflag"
	"net/http"
)

type AppCreate struct {
	cmd.Command
	memory int
	swap   int
	fs     *gnuflag.FlagSet
}

func (c *AppCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "app-create",
		Usage:   "app-create <appname> <platform> [--memory/-m memory_in_mb] [--swap/-s swap_in_mb]",
		Desc:    "create a new app.",
		MinArgs: 2,
	}
}

func (c *AppCreate) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		infoMessage := "The maximum amount of memory reserved to each container for this app"
		c.fs = gnuflag.NewFlagSet("", gnuflag.ExitOnError)
		c.fs.IntVar(&c.memory, "memory", 0, infoMessage)
		c.fs.IntVar(&c.memory, "m", 0, infoMessage)
		infoMessage = "The maximum amount of swap reserved to each container for this app"
		c.fs.IntVar(&c.swap, "swap", 0, infoMessage)
		c.fs.IntVar(&c.swap, "s", 0, infoMessage)
	}
	return c.fs
}

func (c *AppCreate) Run(context *cmd.Context, client *cmd.Client) error {
	appName := context.Args[0]
	platform := context.Args[1]
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s","platform":"%s","memory":"%d","swap":"%d"}`, appName, platform, c.memory, c.swap))
	url, err := cmd.GetURL("/apps")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, b)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, "App %q is being created!\n", appName)
	fmt.Fprintln(context.Stdout, "Use app-info to check the status of the app and its units.")
	fmt.Fprintf(context.Stdout, "Your repository for %q project is %q\n", appName, out["repository_url"])
	return nil
}

type AppRemove struct {
	tsuru.GuessingCommand
	yes bool
	fs  *gnuflag.FlagSet
}

func (c *AppRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "app-remove",
		Usage: "app-remove [--app appname] [--assume-yes]",
		Desc: `removes an app.

If you don't provide the app name, tsuru will try to guess it.`,
		MinArgs: 0,
	}
}

func (c *AppRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	var answer string
	if !c.yes {
		fmt.Fprintf(context.Stdout, `Are you sure you want to remove app "%s"? (y/n) `, appName)
		fmt.Fscanf(context.Stdin, "%s", &answer)
		if answer != "y" {
			fmt.Fprintln(context.Stdout, "Abort.")
			return nil
		}
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, `App "%s" successfully removed!`+"\n", appName)
	return nil
}

func (c *AppRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		c.fs.BoolVar(&c.yes, "assume-yes", false, "Don't ask for confirmation, just remove the app.")
		c.fs.BoolVar(&c.yes, "y", false, "Don't ask for confirmation, just remove the app.")
	}
	return c.fs
}

type UnitAdd struct {
	tsuru.GuessingCommand
}

func (c *UnitAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-add",
		Usage:   "unit-add <# of units> [--app appname]",
		Desc:    "add new units to an app.",
		MinArgs: 1,
	}
}

func (c *UnitAdd) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("PUT", url, bytes.NewBufferString(context.Args[0]))
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully added!")
	return nil
}

type UnitRemove struct {
	tsuru.GuessingCommand
}

func (c *UnitRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-remove",
		Usage:   "unit-remove <# of units> [--app appname]",
		Desc:    "remove units from an app.",
		MinArgs: 1,
	}
}

func (c *UnitRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	body := bytes.NewBufferString(context.Args[0])
	request, err := http.NewRequest("DELETE", url, body)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully removed!")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/penberg/ustat"
	"gopkg.in/urfave/cli.v1"
	"os"
	"os/signal"
	"regexp"
	"syscall"
	"time"
)

func main() {
	app := cli.NewApp()
	app.Name = "ustat"
	app.Usage = "Unified system statistics collector"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "c,cpu",
			Usage: "Enable CPU stats collection",
		},
		cli.BoolFlag{
			Name:  "i,int",
			Usage: "Enable interrupt stats collection",
		},
		cli.BoolFlag{
			Name:  "n,net",
			Usage: "Enable network stats collection",
		},
		cli.BoolFlag{
			Name:  "d,disk",
			Usage: "Enable disk stats collection",
		},
		cli.StringFlag{
			Name:  "o,output",
			Usage: "Write output to a file",
		},
		cli.StringFlag{
			Name:  "grep",
			Usage: "Regular expression patter for filtering stats",
		},
	}
	app.Action = func(ctx *cli.Context) error {
		var stats []*ustat.Stat
		if ctx.Bool("cpu") {
			stats = append(stats, ustat.NewCPUsStat())
		}
		if ctx.Bool("int") {
			stats = append(stats, ustat.NewInterruptsStat())
		}
		if ctx.Bool("net") {
			stats = append(stats, ustat.NewNetStat())
		}
		if ctx.Bool("disk") {
			stats = append(stats, ustat.NewDiskStat())
		}
		output := os.Stdout
		outputPath := ctx.String("output")
		if outputPath != "" {
			file, err := os.Create(outputPath)
			if err != nil {
				return cli.NewExitError(fmt.Sprintf("Unable to open file: %v", err), 2)
			}
			defer file.Close()
			output = file
		}
		pattern := ctx.String("grep")
		if len(stats) == 0 {
			return cli.NewExitError("No stats enabled", 1)
		}
		fmt.Fprintf(output, "# This file has been generated by ustat.\n")
		fmt.Fprintf(output, "#\n")
		fmt.Fprintf(output, "# Column descriptions:\n")
		for _, stat := range stats {
			for _, description := range stat.Descriptions {
				if pattern != "" {
					matched, err := regexp.MatchString(pattern, description)
					if err != nil || !matched {
						continue
					}
				}
				fmt.Fprintf(output, "# %s\n", description)
			}
		}
		for _, stat := range stats {
			for _, name := range stat.Names {
				if pattern != "" {
					matched, err := regexp.MatchString(pattern, name)
					if err != nil || !matched {
						continue
					}
				}
				fmt.Fprintf(output, "%s\t", name)
			}
		}
		fmt.Fprintln(output, "")
		sigs := make(chan os.Signal, 1)
		done := make(chan bool, 1)
		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
		go func() {
			sig := <-sigs
			fmt.Println()
			fmt.Println(sig)
			done <- true
		}()
		ticker := time.NewTicker(time.Second * 1)
		go func() {
			for _ = range ticker.C {
				for _, stat := range stats {
					values := stat.Collector.Collect()
					for idx, value := range values {
						if pattern != "" {
							matched, err := regexp.MatchString(pattern, stat.Names[idx])
							if err != nil || !matched {
								continue
							}
						}
						fmt.Fprintf(output, "%d\t", value)
					}
				}
				fmt.Fprintln(output, "")
			}
		}()
		<-done

		return nil
	}
	app.Run(os.Args)
}
<commit_msg>Add '--delimiter' command line option<commit_after>package main

import (
	"fmt"
	"github.com/penberg/ustat"
	"gopkg.in/urfave/cli.v1"
	"os"
	"os/signal"
	"regexp"
	"syscall"
	"time"
)

func main() {
	app := cli.NewApp()
	app.Name = "ustat"
	app.Usage = "Unified system statistics collector"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "c,cpu",
			Usage: "Enable CPU stats collection",
		},
		cli.BoolFlag{
			Name:  "i,int",
			Usage: "Enable interrupt stats collection",
		},
		cli.BoolFlag{
			Name:  "n,net",
			Usage: "Enable network stats collection",
		},
		cli.BoolFlag{
			Name:  "d,disk",
			Usage: "Enable disk stats collection",
		},
		cli.StringFlag{
			Name:  "o,output",
			Usage: "Write output to a file",
		},
		cli.StringFlag{
			Name:  "delimiter",
			Usage: "Delimiter used in the output file",
			Value: "\t",
		},
		cli.StringFlag{
			Name:  "grep",
			Usage: "Regular expression patter for filtering stats",
		},
	}
	app.Action = func(ctx *cli.Context) error {
		var stats []*ustat.Stat
		if ctx.Bool("cpu") {
			stats = append(stats, ustat.NewCPUsStat())
		}
		if ctx.Bool("int") {
			stats = append(stats, ustat.NewInterruptsStat())
		}
		if ctx.Bool("net") {
			stats = append(stats, ustat.NewNetStat())
		}
		if ctx.Bool("disk") {
			stats = append(stats, ustat.NewDiskStat())
		}
		output := os.Stdout
		outputPath := ctx.String("output")
		if outputPath != "" {
			file, err := os.Create(outputPath)
			if err != nil {
				return cli.NewExitError(fmt.Sprintf("Unable to open file: %v", err), 2)
			}
			defer file.Close()
			output = file
		}
		pattern := ctx.String("grep")
		if len(stats) == 0 {
			return cli.NewExitError("No stats enabled", 1)
		}
		delimiter := ctx.String("delimiter")
		fmt.Fprintf(output, "# This file has been generated by ustat.\n")
		fmt.Fprintf(output, "#\n")
		fmt.Fprintf(output, "# Column descriptions:\n")
		for _, stat := range stats {
			for _, description := range stat.Descriptions {
				if pattern != "" {
					matched, err := regexp.MatchString(pattern, description)
					if err != nil || !matched {
						continue
					}
				}
				fmt.Fprintf(output, "# %s\n", description)
			}
		}
		for _, stat := range stats {
			for _, name := range stat.Names {
				if pattern != "" {
					matched, err := regexp.MatchString(pattern, name)
					if err != nil || !matched {
						continue
					}
				}
				fmt.Fprintf(output, "%s%s", name, delimiter)
			}
		}
		fmt.Fprintln(output, "")
		sigs := make(chan os.Signal, 1)
		done := make(chan bool, 1)
		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
		go func() {
			sig := <-sigs
			fmt.Println()
			fmt.Println(sig)
			done <- true
		}()
		ticker := time.NewTicker(time.Second * 1)
		go func() {
			for _ = range ticker.C {
				for _, stat := range stats {
					values := stat.Collector.Collect()
					for idx, value := range values {
						if pattern != "" {
							matched, err := regexp.MatchString(pattern, stat.Names[idx])
							if err != nil || !matched {
								continue
							}
						}
						fmt.Fprintf(output, "%d%s", value, delimiter)
					}
				}
				fmt.Fprintln(output, "")
			}
		}()
		<-done

		return nil
	}
	app.Run(os.Args)
}
<|endoftext|>