<commit_before>package whiplash

import (
	"log"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

// These are our Svc types, which are basically the types of ceph
// daemons.
const (
	MON = iota
	RGW
	OSD
)

var (
	// this is the list of admin socket commands we know
	cephcmds = map[string][]byte{"version": []byte("{\"prefix\": \"version\"}\000")}
)

// Svc represents a Ceph service
type Svc struct {
	// Type is the service/daemon type: MON, RGW, OSD
	Type int

	// Sock is the admin socket for the service
	Sock string

	// Host is the machine where the service runs
	Host string

	// Reporting shows if a service is contactable and responsive
	Reporting bool

	// Err holds the error (if any) from the Ping() check
	Err error

	// Version is the Ceph version of the service.
	Version string

	// Resp receives response data from Query()
	Resp []byte

	// b0 is where we read the message length into
	b0 []byte
	// mlen is the unpacked length from b0
	mlen int32
	// mread is the number of bytes read in the message so far
	mread int32
	// b1 is the buffer we read into from the network
	b1 []byte
	// b2 accumulates data from b1
	b2 []byte
}

type cephVersion struct {
	version string
}

// getCephServices examines wlc.CephConf and populates wlc.Svcs
func (wlc *WLConfig) getCephServices() {
	wlc.Svcs = make(map[string]*Svc)
	// iterate over CephConf, adding OSDs and RGWs
	for k, m := range wlc.CephConf {
		s := &Svc{b0: make([]byte, 4)}
		switch {
		case strings.HasPrefix(k, "osd."):
			s.Type = OSD
			s.Host = m["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
			s.Ping()
			wlc.Svcs[k] = s
		case strings.HasPrefix(k, "client.radosgw"):
			s.Type = RGW
			s.Host = os.Getenv("HOSTNAME")
			if rsp, ok := m["rgw socket path"]; ok {
				s.Sock = rsp
			} else {
				s.Sock = strings.Replace(m["admin socket"], "$name", k, 1)
			}
			s.Ping()
			wlc.Svcs[k] = s
		}
	}
	// if we get down here and Svcs is empty, we're on a monitor
	if len(wlc.Svcs) == 0 {
		k := "mon." + os.Getenv("HOSTNAME")
		s := &Svc{Type: MON, Host: wlc.CephConf[k]["host"], b1: make([]byte, 64)}
		s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		s.Ping()
		wlc.Svcs[k] = s
	}
}

// Ping sends a version request to a Ceph service. It acts as the test
// for whether a service is reporting. When successful, it sets
// Reporting to 'true' and sets the service's Version. When it fails,
// Reporting is set to 'false', and Err is set to the returned error.
func (s *Svc) Ping() {
	err := s.Query("version")
	if err == nil {
		s.Reporting = true
		s.Err = nil
		vs := &cephVersion{}
		err = json.Unmarshal(s.Resp, vs)
		if err == nil {
			s.Version = vs.version
		}
	} else {
		s.Reporting = false
		s.Err = err
	}
}

// Query sends a request to a Ceph service and reads the result.
func (s *Svc) Query(req string) error {
	// make sure we know this command
	cmd, ok := cephcmds[req]
	if !ok {
		return fmt.Errorf("unknown request '%v'\n", req)
	}

	// make the connection
	conn, err := net.Dial("unix", s.Sock)
	if err != nil {
		return fmt.Errorf("could not connect to sock %v: %v\n", s.Sock, err)
	}
	defer conn.Close()

	// send command to the admin socket
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	_, err = conn.Write(cmd)
	if err != nil {
		return fmt.Errorf("could not write to %v: %v\n", s.Sock, err)
	}
	log.Printf("Sent '%v'", string(cmd))

	// zero our byte-collectors and bytes-read counter
	s.b1 = make([]byte, 64)
	s.b2 = s.b2[:0]
	s.mread = 0

	// get the response message length
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	n, err := conn.Read(s.b0)
	if err != nil {
		return fmt.Errorf("could not read message length on %v: %v\n", s.Sock, err)
	}
	if  n != 4 {
		return fmt.Errorf("too few bytes (%v) in message length on %v: %v\n", n, s.Sock, err)
	}
	buf := bytes.NewReader(s.b0)
	err = binary.Read(buf, binary.BigEndian, &s.mlen)
	if err != nil {
		return fmt.Errorf("could not decode message length on %v: %v\n", s.Sock, err)
	}
	log.Printf("Message length %v bytes", s.mlen)

	// and read the message
	for {
		if s.mread == s.mlen {
			break
		}
		if x := s.mlen - s.mread; x < 64 {
			s.b1 = make([]byte, x)
		}
		conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
		n, err := conn.Read(s.b1)
		if err != nil && err.Error() != "EOF" {
			return fmt.Errorf("could not read from %v: %v\n", s.Sock, err)
		}
		s.mread += int32(n)
		s.b2 = append(s.b2, s.b1[:n]...)
	}
	s.Resp = s.b2[:s.mlen - 1]
	return err
}
<commit_msg>wip: looks like netcode is good now but Ping isn't right<commit_after>package whiplash

import (
	"log"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

// These are our Svc types, which are basically the types of ceph
// daemons.
const (
	MON = iota
	RGW
	OSD
)

var (
	// this is the list of admin socket commands we know
	cephcmds = map[string][]byte{"version": []byte("{\"prefix\": \"version\"}\000")}
)

// Svc represents a Ceph service
type Svc struct {
	// Type is the service/daemon type: MON, RGW, OSD
	Type int

	// Sock is the admin socket for the service
	Sock string

	// Host is the machine where the service runs
	Host string

	// Reporting shows if a service is contactable and responsive
	Reporting bool

	// Err holds the error (if any) from the Ping() check
	Err error

	// Version is the Ceph version of the service.
	Version string

	// Resp receives response data from Query()
	Resp []byte

	// b0 is where we read the message length into
	b0 []byte
	// mlen is the unpacked length from b0
	mlen int32
	// mread is the number of bytes read in the message so far
	mread int32
	// b1 is the buffer we read into from the network
	b1 []byte
	// b2 accumulates data from b1
	b2 []byte
}

type cephVersion struct {
	version string
}

// getCephServices examines wlc.CephConf and populates wlc.Svcs
func (wlc *WLConfig) getCephServices() {
	wlc.Svcs = make(map[string]*Svc)
	// iterate over CephConf, adding OSDs and RGWs
	for k, m := range wlc.CephConf {
		s := &Svc{b0: make([]byte, 4)}
		switch {
		case strings.HasPrefix(k, "osd."):
			s.Type = OSD
			s.Host = m["host"]
			s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
			s.Ping()
			wlc.Svcs[k] = s
		case strings.HasPrefix(k, "client.radosgw"):
			s.Type = RGW
			s.Host = os.Getenv("HOSTNAME")
			if rsp, ok := m["rgw socket path"]; ok {
				s.Sock = rsp
			} else {
				s.Sock = strings.Replace(m["admin socket"], "$name", k, 1)
			}
			s.Ping()
			wlc.Svcs[k] = s
		}
	}
	// if we get down here and Svcs is empty, we're on a monitor
	if len(wlc.Svcs) == 0 {
		k := "mon." + os.Getenv("HOSTNAME")
		s := &Svc{Type: MON, Host: wlc.CephConf[k]["host"], b1: make([]byte, 64)}
		s.Sock = strings.Replace(wlc.CephConf["osd"]["admin socket"], "$name", k, 1)
		s.Ping()
		wlc.Svcs[k] = s
	}
}

// Ping sends a version request to a Ceph service. It acts as the test
// for whether a service is reporting. When successful, it sets
// Reporting to 'true' and sets the service's Version. When it fails,
// Reporting is set to 'false', and Err is set to the returned error.
func (s *Svc) Ping() {
	err := s.Query("version")
	if err == nil {
		s.Reporting = true
		s.Err = nil
		vs := &cephVersion{}
		err = json.Unmarshal(s.Resp, vs)
		if err == nil {
			s.Version = vs.version
		}
	} else {
		s.Reporting = false
		s.Err = err
	}
}

// Query sends a request to a Ceph service and reads the result.
func (s *Svc) Query(req string) error {
	// make sure we know this command
	cmd, ok := cephcmds[req]
	if !ok {
		return fmt.Errorf("unknown request '%v'\n", req)
	}

	// make the connection
	conn, err := net.Dial("unix", s.Sock)
	if err != nil {
		return fmt.Errorf("could not connect to sock %v: %v\n", s.Sock, err)
	}
	defer conn.Close()

	// send command to the admin socket
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	_, err = conn.Write(cmd)
	if err != nil {
		return fmt.Errorf("could not write to %v: %v\n", s.Sock, err)
	}
	log.Printf("Sent '%v'", string(cmd))

	// zero our byte-collectors and bytes-read counter
	s.b1 = make([]byte, 64)
	s.b2 = s.b2[:0]
	s.mread = 0

	// get the response message length
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	n, err := conn.Read(s.b0)
	if err != nil {
		return fmt.Errorf("could not read message length on %v: %v\n", s.Sock, err)
	}
	if  n != 4 {
		return fmt.Errorf("too few bytes (%v) in message length on %v: %v\n", n, s.Sock, err)
	}
	buf := bytes.NewReader(s.b0)
	err = binary.Read(buf, binary.BigEndian, &s.mlen)
	if err != nil {
		return fmt.Errorf("could not decode message length on %v: %v\n", s.Sock, err)
	}
	log.Printf("Message length %v bytes", s.mlen)

	// and read the message
	for {
		if s.mread == s.mlen {
			break
		}
		if x := s.mlen - s.mread; x < 64 {
			s.b1 = make([]byte, x)
		}
		conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
		n, err := conn.Read(s.b1)
		if err != nil && err.Error() != "EOF" {
			return fmt.Errorf("could not read from %v: %v\n", s.Sock, err)
		}
		s.mread += int32(n)
		s.b2 = append(s.b2, s.b1[:n]...)
	}
	s.Resp = s.b2[:s.mlen]
	log.Printf("Read '%v'", string(s.Resp))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/caiofilipini/encurtador/url"
)

var (
	logLigado *bool
	porta     *int
	urlBase   string
)

func init() {
	dominio := flag.String("d", "localhost", "domínio")
	porta = flag.Int("p", 8888, "porta")
	logLigado = flag.Bool("l", true, "log ligado/desligado")

	flag.Parse()

	urlBase = fmt.Sprintf("http://%s:%d", *dominio, *porta)
}

type Headers map[string]string

type Redirecionador struct {
	stats chan string
}

func (r *Redirecionador) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	buscarUrlEExecutar(w, req, func(url *url.Url) {
		http.Redirect(w, req, url.Destino, http.StatusMovedPermanently)
		r.stats <- url.Id
	})
}

func Encurtador(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		responderCom(w, http.StatusMethodNotAllowed, Headers{"Allow": "POST"})
		return
	}

	url, nova, err := url.BuscarOuCriarNovaUrl(extrairUrl(r))

	if err != nil {
		responderCom(w, http.StatusBadRequest, nil)
		return
	}

	var status int
	if nova {
		status = http.StatusCreated
	} else {
		status = http.StatusOK
	}

	urlCurta := fmt.Sprintf("%s/r/%s", urlBase, url.Id)

	responderCom(w, status, Headers{
		"Location": urlCurta,
		"Link":     fmt.Sprintf("<%s/api/stats/%s>; rel=\"stats\"", urlBase, url.Id),
	})

	logar("URL %s encurtada com sucesso para %s.", url.Destino, urlCurta)
}

func Visualizador(w http.ResponseWriter, r *http.Request) {
	buscarUrlEExecutar(w, r, func(url *url.Url) {
		json, err := json.Marshal(url.Stats())

		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		responderComJSON(w, string(json))
	})
}

func buscarUrlEExecutar(w http.ResponseWriter, r *http.Request, executor func(*url.Url)) {
	caminho := strings.Split(r.URL.Path, "/")
	id := caminho[len(caminho)-1]

	if url := url.Buscar(id); url != nil {
		executor(url)
	} else {
		http.NotFound(w, r)
	}
}

func responderCom(w http.ResponseWriter, status int, headers Headers) {
	for k, v := range headers {
		w.Header().Set(k, v)
	}
	w.WriteHeader(status)
}

func responderComJSON(w http.ResponseWriter, resposta string) {
	responderCom(w, http.StatusOK, Headers{"Content-Type": "application/json"})
	fmt.Fprintf(w, resposta)
}

func extrairUrl(r *http.Request) string {
	rawBody := make([]byte, r.ContentLength, r.ContentLength)
	r.Body.Read(rawBody)
	return string(rawBody)
}

func registrarEstatisticas(stats chan string) {
	for id := range stats {
		url.RegistrarClick(id)
		logar("Click registrado com sucesso para %s.", id)
	}
}

func logar(formato string, valores ...interface{}) {
	if *logLigado {
		log.Printf(fmt.Sprintf("%s\n", formato), valores...)
	}
}

func main() {
	url.ConfigurarRepositorio(url.NovoRepositorioMemoria())

	stats := make(chan string)
	defer close(stats)
	go registrarEstatisticas(stats)

	http.Handle("/r/", &Redirecionador{stats})
	http.HandleFunc("/api/encurtar", Encurtador)
	http.HandleFunc("/api/stats/", Visualizador)

	logar("Iniciando servidor na porta %d...", *porta)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *porta), nil))
}
<commit_msg>Especificando canal read-only.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/caiofilipini/encurtador/url"
)

var (
	logLigado *bool
	porta     *int
	urlBase   string
)

func init() {
	dominio := flag.String("d", "localhost", "domínio")
	porta = flag.Int("p", 8888, "porta")
	logLigado = flag.Bool("l", true, "log ligado/desligado")

	flag.Parse()

	urlBase = fmt.Sprintf("http://%s:%d", *dominio, *porta)
}

type Headers map[string]string

type Redirecionador struct {
	stats chan string
}

func (r *Redirecionador) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	buscarUrlEExecutar(w, req, func(url *url.Url) {
		http.Redirect(w, req, url.Destino, http.StatusMovedPermanently)
		r.stats <- url.Id
	})
}

func Encurtador(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		responderCom(w, http.StatusMethodNotAllowed, Headers{"Allow": "POST"})
		return
	}

	url, nova, err := url.BuscarOuCriarNovaUrl(extrairUrl(r))

	if err != nil {
		responderCom(w, http.StatusBadRequest, nil)
		return
	}

	var status int
	if nova {
		status = http.StatusCreated
	} else {
		status = http.StatusOK
	}

	urlCurta := fmt.Sprintf("%s/r/%s", urlBase, url.Id)

	responderCom(w, status, Headers{
		"Location": urlCurta,
		"Link":     fmt.Sprintf("<%s/api/stats/%s>; rel=\"stats\"", urlBase, url.Id),
	})

	logar("URL %s encurtada com sucesso para %s.", url.Destino, urlCurta)
}

func Visualizador(w http.ResponseWriter, r *http.Request) {
	buscarUrlEExecutar(w, r, func(url *url.Url) {
		json, err := json.Marshal(url.Stats())

		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		responderComJSON(w, string(json))
	})
}

func buscarUrlEExecutar(w http.ResponseWriter, r *http.Request, executor func(*url.Url)) {
	caminho := strings.Split(r.URL.Path, "/")
	id := caminho[len(caminho)-1]

	if url := url.Buscar(id); url != nil {
		executor(url)
	} else {
		http.NotFound(w, r)
	}
}

func responderCom(w http.ResponseWriter, status int, headers Headers) {
	for k, v := range headers {
		w.Header().Set(k, v)
	}
	w.WriteHeader(status)
}

func responderComJSON(w http.ResponseWriter, resposta string) {
	responderCom(w, http.StatusOK, Headers{"Content-Type": "application/json"})
	fmt.Fprintf(w, resposta)
}

func extrairUrl(r *http.Request) string {
	rawBody := make([]byte, r.ContentLength, r.ContentLength)
	r.Body.Read(rawBody)
	return string(rawBody)
}

func registrarEstatisticas(stats <-chan string) {
	for id := range stats {
		url.RegistrarClick(id)
		logar("Click registrado com sucesso para %s.", id)
	}
}

func logar(formato string, valores ...interface{}) {
	if *logLigado {
		log.Printf(fmt.Sprintf("%s\n", formato), valores...)
	}
}

func main() {
	url.ConfigurarRepositorio(url.NovoRepositorioMemoria())

	stats := make(chan string)
	defer close(stats)
	go registrarEstatisticas(stats)

	http.Handle("/r/", &Redirecionador{stats})
	http.HandleFunc("/api/encurtar", Encurtador)
	http.HandleFunc("/api/stats/", Visualizador)

	logar("Iniciando servidor na porta %d...", *porta)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *porta), nil))
}
<|endoftext|>


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

// centre is used to support one or more of DHCP, TFTP, and HTTP services
// on harvey networks.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"math"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv4/bsdp"
	"github.com/insomniacslk/dhcp/dhcpv4/server4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/dhcpv6/server6"
	"harvey-os.org/ninep/protocol"
	"harvey-os.org/ninep/ufs"
	"pack.ag/tftp"
)

var (
	// TODO: get info from centre ipv4
	inf = flag.String("i", "eth0", "Interface to serve DHCPv4 on")

	// DHCPv4-specific
	ipv4         = flag.Bool("4", true, "IPv4 DHCP server")
	rootpath     = flag.String("rootpath", "", "RootPath option to serve via DHCPv4")
	bootfilename = flag.String("bootfilename", "pxelinux.0", "Boot file to serve via DHCPv4")
	raspi        = flag.Bool("raspi", false, "Configure to boot Raspberry Pi")

	// DHCPv6-specific
	ipv6           = flag.Bool("6", false, "DHCPv6 server")
	v6Bootfilename = flag.String("v6-bootfilename", "", "Boot file to serve via DHCPv6")

	// File serving
	tftpDir    = flag.String("tftp-dir", "", "Directory to serve over TFTP")
	tftpPort   = flag.Int("tftp-port", 69, "Port to serve TFTP on")
	httpDir    = flag.String("http-dir", "", "Directory to serve over HTTP")
	httpPort   = flag.Int("http-port", 80, "Port to serve HTTP on")
	ninepDir   = flag.String("ninep-dir", "", "Directory to serve over 9p")
	ninepAddr  = flag.String("ninep-addr", ":5640", "addr to serve 9p on")
	ninepDebug = flag.Int("ninep-debug", 0, "Debug level for ninep -- for now, only non-zero matters")
)

type dserver4 struct {
	mac          net.HardwareAddr
	yourIP       net.IP
	submask      net.IPMask
	self         net.IP
	bootfilename string
	rootpath     string
}

func (s *dserver4) dhcpHandler(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
	log.Printf("Handling request %v for peer %v", m, peer)

	var replyType dhcpv4.MessageType
	switch mt := m.MessageType(); mt {
	case dhcpv4.MessageTypeDiscover:
		replyType = dhcpv4.MessageTypeOffer
	case dhcpv4.MessageTypeRequest:
		replyType = dhcpv4.MessageTypeAck
	default:
		log.Printf("Can't handle type %v", mt)
		return
	}

	i, err := net.LookupIP(fmt.Sprintf("u%s", m.ClientHWAddr))
	if err != nil {
		log.Printf("Not responding to DHCP request for mac %s", m.ClientHWAddr)
		log.Printf("You can create a host entry of the form 'a.b.c.d [names] u%s' 'ip6addr [names] u%s'if you wish", m.ClientHWAddr, m.ClientHWAddr)
		return
	}

	// Since this is dserver4, we force it to be an ip4 address.
	ip := i[0].To4()
	modifiers := []dhcpv4.Modifier{
		dhcpv4.WithMessageType(replyType),
		dhcpv4.WithServerIP(s.self),
		dhcpv4.WithRouter(s.self),
		dhcpv4.WithNetmask(s.submask),
		dhcpv4.WithYourIP(ip),
		// RFC 2131, Section 4.3.1. Server Identifier: MUST
		dhcpv4.WithOption(dhcpv4.OptServerIdentifier(s.self)),
		// RFC 2131, Section 4.3.1. IP lease time: MUST
		dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(dhcpv4.MaxLeaseTime)),
		dhcpv4.WithOption(dhcpv4.OptClassIdentifier("PXEClient")),
	}
	if *raspi {
		// Add option 43, suboption 9 (PXE native boot menu) to allow Raspberry Pi to recognise the offer
		modifiers = append(modifiers, dhcpv4.WithOption(dhcpv4.Option{
			Code: dhcpv4.OptionVendorSpecificInformation,
			Value: bsdp.VendorOptions{Options: dhcpv4.OptionsFromList(
				// The dhcp package only seems to support Apple BSDP boot menu items,
				// so we have to craft the option by hand.
				// \x11 is the length of the 'Raspberry Pi Boot' string...
				dhcpv4.OptGeneric(dhcpv4.GenericOptionCode(9), []byte("\000\000\x11Raspberry Pi Boot")),
			)},
		}))
	}
	reply, err := dhcpv4.NewReplyFromRequest(m, modifiers...)

	// RFC 6842, MUST include Client Identifier if client specified one.
	if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
		reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))
	}
	if len(s.bootfilename) > 0 {
		reply.BootFileName = s.bootfilename
	}
	if len(s.rootpath) > 0 {
		reply.UpdateOption(dhcpv4.OptRootPath(s.rootpath))
	}
	if err != nil {
		log.Printf("Could not create reply for %v: %v", m, err)
		return
	}

	// Experimentally determined. You can't just blindly send a broadcast packet
	// with the broadcast address. You can, however, send a broadcast packet
	// to a subnet for an interface. That actually makes some sense.
	// This fixes the observed problem that OSX just swallows these
	// packets if the peer is 255.255.255.255.
	// I chose this way of doing it instead of files with build constraints
	// because this is not that expensive and it's just a tiny bit easier to
	// follow IMHO.
	if runtime.GOOS == "darwin" {
		p := &net.UDPAddr{IP: s.yourIP.Mask(s.submask), Port: 68}
		log.Printf("Changing %v to %v", peer, p)
		peer = p
	}

	log.Printf("Sending %v to %v", reply.Summary(), peer)
	if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
		log.Printf("Could not write %v: %v", reply, err)
	}
}

type dserver6 struct {
	mac         net.HardwareAddr
	yourIP      net.IP
	bootfileurl string
}

func (s *dserver6) dhcpHandler(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
	log.Printf("Handling DHCPv6 request %v sent by %v", m.Summary(), peer.String())

	msg, err := m.GetInnerMessage()
	if err != nil {
		log.Printf("Could not find unpacked message: %v", err)
		return
	}

	if msg.MessageType != dhcpv6.MessageTypeSolicit {
		log.Printf("Only accept SOLICIT message type, this is a %s", msg.MessageType)
		return
	}
	if msg.GetOneOption(dhcpv6.OptionRapidCommit) == nil {
		log.Printf("Only accept requests with rapid commit option.")
		return
	}
	if mac, err := dhcpv6.ExtractMAC(msg); err != nil {
		log.Printf("No MAC address in request: %v", err)
		return
	} else if s.mac != nil && !bytes.Equal(s.mac, mac) {
		log.Printf("MAC address %s doesn't match expected MAC %s", mac, s.mac)
		return
	}

	// From RFC 3315, section 17.1.4, If the client includes a Rapid Commit
	// option in the Solicit message, it will expect a Reply message that
	// includes a Rapid Commit option in response.
	reply, err := dhcpv6.NewReplyFromMessage(msg)
	if err != nil {
		log.Printf("Failed to create reply for %v: %v", m, err)
		return
	}

	iana := msg.Options.OneIANA()
	if iana != nil {
		iana.Options.Update(&dhcpv6.OptIAAddress{
			IPv6Addr:          s.yourIP,
			PreferredLifetime: math.MaxUint32 * time.Second,
			ValidLifetime:     math.MaxUint32 * time.Second,
		})
		reply.AddOption(iana)
	}
	if len(s.bootfileurl) > 0 {
		reply.Options.Add(dhcpv6.OptBootFileURL(s.bootfileurl))
	}

	if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
		log.Printf("Failed to send response %v: %v", reply, err)
		return
	}

	log.Printf("DHCPv6 request successfully handled, reply: %v", reply.Summary())
}

func main() {
	flag.Parse()

	var wg sync.WaitGroup
	if len(*tftpDir) != 0 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			server, err := tftp.NewServer(fmt.Sprintf(":%d", *tftpPort))
			if err != nil {
				log.Fatalf("Could not start TFTP server: %v", err)
			}

			log.Println("starting file server")
			server.ReadHandler(tftp.FileServer(*tftpDir))
			log.Fatal(server.ListenAndServe())
		}()
	}
	if len(*httpDir) != 0 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			http.Handle("/", http.FileServer(http.Dir(*httpDir)))
			log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), nil))
		}()
	}

	if *inf != "" {
		centre, err := net.LookupIP("centre")
		if err != nil {
			log.Printf("No centre entry found via LookupIP: not serving DHCP")
		} else if *ipv4 {
			ip := centre[0].To4()
			wg.Add(1)
			go func() {
				defer wg.Done()
				s := &dserver4{
					self:         ip,
					bootfilename: *bootfilename,
					rootpath:     *rootpath,
					submask:      ip.DefaultMask(),
				}

				laddr := &net.UDPAddr{Port: dhcpv4.ServerPort}
				server, err := server4.NewServer(*inf, laddr, s.dhcpHandler)
				if err != nil {
					log.Fatal(err)
				}
				if err := server.Serve(); err != nil {
					log.Fatal(err)
				}
			}()
		}
	}

	// not yet.
	if false && *ipv6 && *inf != "" {
		wg.Add(1)
		go func() {
			defer wg.Done()

			s := &dserver6{
				bootfileurl: *v6Bootfilename,
			}
			laddr := &net.UDPAddr{
				IP:   net.IPv6unspecified,
				Port: dhcpv6.DefaultServerPort,
			}
			server, err := server6.NewServer("eth0", laddr, s.dhcpHandler)
			if err != nil {
				log.Fatal(err)
			}

			log.Println("starting dhcpv6 server")
			if err := server.Serve(); err != nil {
				log.Fatal(err)
			}
		}()
	}

	// TODO: serve on ip6
	if len(*ninepDir) != 0 {
		ln, err := net.Listen("tcp4", *ninepAddr)
		if err != nil {
			log.Fatalf("Listen failed: %v", err)
		}

		ufslistener, err := ufs.NewUFS(*ninepDir, *ninepDebug, func(l *protocol.NetListener) error {
			l.Trace = nil
			if *ninepDebug > 1 {
				l.Trace = log.Printf
			}
			return nil
		})

		if err != nil {
			log.Fatal(err)
		}
		if err := ufslistener.Serve(ln); err != nil {
			log.Fatal(err)
		}

	}
	wg.Wait()
}
<commit_msg>centre: fix netboot on Thinkpad T440p (#34)<commit_after>// Copyright 2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// centre is used to support one or more of DHCP, TFTP, and HTTP services
// on harvey networks.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"math"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv4/bsdp"
	"github.com/insomniacslk/dhcp/dhcpv4/server4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/dhcpv6/server6"
	"harvey-os.org/ninep/protocol"
	"harvey-os.org/ninep/ufs"
	"pack.ag/tftp"
)

var (
	// TODO: get info from centre ipv4
	inf = flag.String("i", "eth0", "Interface to serve DHCPv4 on")

	// DHCPv4-specific
	ipv4         = flag.Bool("4", true, "IPv4 DHCP server")
	rootpath     = flag.String("rootpath", "", "RootPath option to serve via DHCPv4")
	bootfilename = flag.String("bootfilename", "pxelinux.0", "Boot file to serve via DHCPv4")
	raspi        = flag.Bool("raspi", false, "Configure to boot Raspberry Pi")

	// DHCPv6-specific
	ipv6           = flag.Bool("6", false, "DHCPv6 server")
	v6Bootfilename = flag.String("v6-bootfilename", "", "Boot file to serve via DHCPv6")

	// File serving
	tftpDir    = flag.String("tftp-dir", "", "Directory to serve over TFTP")
	tftpPort   = flag.Int("tftp-port", 69, "Port to serve TFTP on")
	httpDir    = flag.String("http-dir", "", "Directory to serve over HTTP")
	httpPort   = flag.Int("http-port", 80, "Port to serve HTTP on")
	ninepDir   = flag.String("ninep-dir", "", "Directory to serve over 9p")
	ninepAddr  = flag.String("ninep-addr", ":5640", "addr to serve 9p on")
	ninepDebug = flag.Int("ninep-debug", 0, "Debug level for ninep -- for now, only non-zero matters")
)

type dserver4 struct {
	mac          net.HardwareAddr
	yourIP       net.IP
	submask      net.IPMask
	self         net.IP
	bootfilename string
	rootpath     string
}

func (s *dserver4) dhcpHandler(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
	log.Printf("Handling request %v for peer %v", m, peer)

	var replyType dhcpv4.MessageType
	switch mt := m.MessageType(); mt {
	case dhcpv4.MessageTypeDiscover:
		replyType = dhcpv4.MessageTypeOffer
	case dhcpv4.MessageTypeRequest:
		replyType = dhcpv4.MessageTypeAck
	default:
		log.Printf("Can't handle type %v", mt)
		return
	}

	i, err := net.LookupIP(fmt.Sprintf("u%s", m.ClientHWAddr))
	if err != nil {
		log.Printf("Not responding to DHCP request for mac %s", m.ClientHWAddr)
		log.Printf("You can create a host entry of the form 'a.b.c.d [names] u%s' 'ip6addr [names] u%s'if you wish", m.ClientHWAddr, m.ClientHWAddr)
		return
	}

	// Since this is dserver4, we force it to be an ip4 address.
	ip := i[0].To4()
	modifiers := []dhcpv4.Modifier{
		dhcpv4.WithMessageType(replyType),
		dhcpv4.WithServerIP(s.self),
		dhcpv4.WithRouter(s.self),
		dhcpv4.WithNetmask(s.submask),
		dhcpv4.WithYourIP(ip),
		// RFC 2131, Section 4.3.1. Server Identifier: MUST
		dhcpv4.WithOption(dhcpv4.OptServerIdentifier(s.self)),
		// RFC 2131, Section 4.3.1. IP lease time: MUST
		dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(dhcpv4.MaxLeaseTime)),
	}
	if *raspi {
		modifiers = append(modifiers,
			dhcpv4.WithOption(dhcpv4.OptClassIdentifier("PXEClient")),
			// Add option 43, suboption 9 (PXE native boot menu) to allow Raspberry Pi to recognise the offer
			dhcpv4.WithOption(dhcpv4.Option{
				Code: dhcpv4.OptionVendorSpecificInformation,
				Value: bsdp.VendorOptions{Options: dhcpv4.OptionsFromList(
					// The dhcp package only seems to support Apple BSDP boot menu items,
					// so we have to craft the option by hand.
					// \x11 is the length of the 'Raspberry Pi Boot' string...
					dhcpv4.OptGeneric(dhcpv4.GenericOptionCode(9), []byte("\000\000\x11Raspberry Pi Boot")),
				)},
			}),
		)
	}
	reply, err := dhcpv4.NewReplyFromRequest(m, modifiers...)

	// RFC 6842, MUST include Client Identifier if client specified one.
	if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
		reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))
	}
	if len(s.bootfilename) > 0 {
		reply.BootFileName = s.bootfilename
	}
	if len(s.rootpath) > 0 {
		reply.UpdateOption(dhcpv4.OptRootPath(s.rootpath))
	}
	if err != nil {
		log.Printf("Could not create reply for %v: %v", m, err)
		return
	}

	// Experimentally determined. You can't just blindly send a broadcast packet
	// with the broadcast address. You can, however, send a broadcast packet
	// to a subnet for an interface. That actually makes some sense.
	// This fixes the observed problem that OSX just swallows these
	// packets if the peer is 255.255.255.255.
	// I chose this way of doing it instead of files with build constraints
	// because this is not that expensive and it's just a tiny bit easier to
	// follow IMHO.
	if runtime.GOOS == "darwin" {
		p := &net.UDPAddr{IP: s.yourIP.Mask(s.submask), Port: 68}
		log.Printf("Changing %v to %v", peer, p)
		peer = p
	}

	log.Printf("Sending %v to %v", reply.Summary(), peer)
	if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
		log.Printf("Could not write %v: %v", reply, err)
	}
}

type dserver6 struct {
	mac         net.HardwareAddr
	yourIP      net.IP
	bootfileurl string
}

func (s *dserver6) dhcpHandler(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
	log.Printf("Handling DHCPv6 request %v sent by %v", m.Summary(), peer.String())

	msg, err := m.GetInnerMessage()
	if err != nil {
		log.Printf("Could not find unpacked message: %v", err)
		return
	}

	if msg.MessageType != dhcpv6.MessageTypeSolicit {
		log.Printf("Only accept SOLICIT message type, this is a %s", msg.MessageType)
		return
	}
	if msg.GetOneOption(dhcpv6.OptionRapidCommit) == nil {
		log.Printf("Only accept requests with rapid commit option.")
		return
	}
	if mac, err := dhcpv6.ExtractMAC(msg); err != nil {
		log.Printf("No MAC address in request: %v", err)
		return
	} else if s.mac != nil && !bytes.Equal(s.mac, mac) {
		log.Printf("MAC address %s doesn't match expected MAC %s", mac, s.mac)
		return
	}

	// From RFC 3315, section 17.1.4, If the client includes a Rapid Commit
	// option in the Solicit message, it will expect a Reply message that
	// includes a Rapid Commit option in response.
	reply, err := dhcpv6.NewReplyFromMessage(msg)
	if err != nil {
		log.Printf("Failed to create reply for %v: %v", m, err)
		return
	}

	iana := msg.Options.OneIANA()
	if iana != nil {
		iana.Options.Update(&dhcpv6.OptIAAddress{
			IPv6Addr:          s.yourIP,
			PreferredLifetime: math.MaxUint32 * time.Second,
			ValidLifetime:     math.MaxUint32 * time.Second,
		})
		reply.AddOption(iana)
	}
	if len(s.bootfileurl) > 0 {
		reply.Options.Add(dhcpv6.OptBootFileURL(s.bootfileurl))
	}

	if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
		log.Printf("Failed to send response %v: %v", reply, err)
		return
	}

	log.Printf("DHCPv6 request successfully handled, reply: %v", reply.Summary())
}

func main() {
	flag.Parse()

	var wg sync.WaitGroup
	if len(*tftpDir) != 0 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			server, err := tftp.NewServer(fmt.Sprintf(":%d", *tftpPort))
			if err != nil {
				log.Fatalf("Could not start TFTP server: %v", err)
			}

			log.Println("starting file server")
			server.ReadHandler(tftp.FileServer(*tftpDir))
			log.Fatal(server.ListenAndServe())
		}()
	}
	if len(*httpDir) != 0 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			http.Handle("/", http.FileServer(http.Dir(*httpDir)))
			log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), nil))
		}()
	}

	if *inf != "" {
		centre, err := net.LookupIP("centre")
		if err != nil {
			log.Printf("No centre entry found via LookupIP: not serving DHCP")
		} else if *ipv4 {
			ip := centre[0].To4()
			wg.Add(1)
			go func() {
				defer wg.Done()
				s := &dserver4{
					self:         ip,
					bootfilename: *bootfilename,
					rootpath:     *rootpath,
					submask:      ip.DefaultMask(),
				}

				laddr := &net.UDPAddr{Port: dhcpv4.ServerPort}
				server, err := server4.NewServer(*inf, laddr, s.dhcpHandler)
				if err != nil {
					log.Fatal(err)
				}
				if err := server.Serve(); err != nil {
					log.Fatal(err)
				}
			}()
		}
	}

	// not yet.
	if false && *ipv6 && *inf != "" {
		wg.Add(1)
		go func() {
			defer wg.Done()

			s := &dserver6{
				bootfileurl: *v6Bootfilename,
			}
			laddr := &net.UDPAddr{
				IP:   net.IPv6unspecified,
				Port: dhcpv6.DefaultServerPort,
			}
			server, err := server6.NewServer("eth0", laddr, s.dhcpHandler)
			if err != nil {
				log.Fatal(err)
			}

			log.Println("starting dhcpv6 server")
			if err := server.Serve(); err != nil {
				log.Fatal(err)
			}
		}()
	}

	// TODO: serve on ip6
	if len(*ninepDir) != 0 {
		ln, err := net.Listen("tcp4", *ninepAddr)
		if err != nil {
			log.Fatalf("Listen failed: %v", err)
		}

		ufslistener, err := ufs.NewUFS(*ninepDir, *ninepDebug, func(l *protocol.NetListener) error {
			l.Trace = nil
			if *ninepDebug > 1 {
				l.Trace = log.Printf
			}
			return nil
		})

		if err != nil {
			log.Fatal(err)
		}
		if err := ufslistener.Serve(ln); err != nil {
			log.Fatal(err)
		}

	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"github.com/codegangsta/cli"
	"github.com/google/go-github/github"
)

func init() {
	cli.AppHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.Name}} {{if .Flags}}[options] {{end}}

VERSION:
   {{.Version}}{{if or .Author .Email}}

AUTHOR:{{if .Author}}
  {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
  {{.Email}}{{end}}{{end}}{{if .Flags}}

OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`
}

func main() {
	app := cli.NewApp()
	app.Name = "thisweek"
	app.Usage = "create a report of your team's activity this week (or for whenever you'd like)"
	app.Version = "0.0.1"
	app.Author = "Marcelo Silveira"
	app.Email = "marcelo@mhfs.com.br"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "repo, r",
			Value: "",
			Usage: "GitHub repository to analyze e.g. mhfs/thisweek",
		},
		cli.StringFlag{
			Name: "from, f",
			// Value: "2015-01-25", // FIXME make default beginning of current week
			Usage: "from date, inclusive",
		},
		cli.StringFlag{
			Name: "to, t",
			// Value: "2015-01-31", // FIXME make default end of current week
			Usage: "to date, inclusive",
		},
		cli.StringSliceFlag{
			Name:  "label, l",
			Value: &cli.StringSlice{},
			Usage: "label to process, defaults to all",
		},
	}

	app.Action = func(ctx *cli.Context) {
		repo := ctx.String("repo")

		if repo == "" {
			fmt.Println("\n***** Missing required flag --repo *****\n")
			cli.ShowAppHelp(ctx)
			return
		}

		fmt.Printf("Starting Processing for repo '%s'\n", repo)

		parts := strings.Split(repo, "/")
		issues, err := loadIssues(parts[0], parts[1])

		if err != nil {
			panic(err) // FIXME be smarter. handle 404s, 403s, ...
		}

		for _, issue := range issues {
			printIssue(&issue)
		}
	}

	app.Run(os.Args)
}

func loadIssues(owner string, repo string) ([]github.Issue, error) {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: os.Getenv("GH_TOKEN")},
	}

	client := github.NewClient(t.Client())

	from, err := time.Parse("2006-01-02", "2015-01-25")

	if err != nil {
		panic("invalid date provided")
	}

	options := github.IssueListByRepoOptions{State: "closed", Sort: "updated", Since: from}
	issues, _, err := client.Issues.ListByRepo(owner, repo, &options)

	return issues, err
}

func printIssue(issue *github.Issue) {
	// TODO handle nils and missing assignee
	// fmt.Printf("#%d %s %s (%s)", issue.Number, issue.ClosedAt, issue.Title, issue.Assignee.Login)
	fmt.Printf("#%d - %s - %s\n", *issue.Number, issue.ClosedAt, *issue.Title)
}
<commit_msg>since time math<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"github.com/codegangsta/cli"
	"github.com/google/go-github/github"
)

const (
	dateFormat = "2006-01-02"
)

func init() {
	cli.AppHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.Name}} {{if .Flags}}[options] {{end}}
"
VERSION:
   {{.Version}}{{if or .Author .Email}}

AUTHOR:{{if .Author}}
  {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
  {{.Email}}{{end}}{{end}}{{if .Flags}}

OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`
}

func main() {
	app := cli.NewApp()
	app.Name = "thisweek"
	app.Usage = "create a report of your team's activity this week (or for whenever you'd like)"
	app.Version = "0.0.1"
	app.Author = "Marcelo Silveira"
	app.Email = "marcelo@mhfs.com.br"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "repo, r",
			Value: "",
			Usage: "GitHub repository to analyze e.g. mhfs/thisweek",
		},
		cli.StringFlag{
			Name:  "since, s",
			Value: utcBeginningOfWeekFromLocal().In(time.Local).Format(dateFormat),
			Usage: "list issues since given date, inclusive",
		},
		cli.StringSliceFlag{
			Name:  "label, l",
			Value: &cli.StringSlice{},
			Usage: "label to process, defaults to all",
		},
	}

	app.Action = func(ctx *cli.Context) {
		repo := ctx.String("repo")

		if repo == "" {
			fmt.Println("\n***** Missing required flag --repo *****\n")
			cli.ShowAppHelp(ctx)
			return
		}

		since, err := time.ParseInLocation(dateFormat, ctx.String("since"), time.Local)
		if err != nil {
			panic("invalid date provided")
		}

		fmt.Printf("Starting work for repo '%s' since '%s'\n", repo, since.Format(dateFormat))

		parts := strings.Split(repo, "/")
		issues, err := fetchIssues(parts[0], parts[1], since)

		if err != nil {
			panic(err) // FIXME be smarter. handle 404s, 403s, ...
		}

		for _, issue := range issues {
			printIssue(&issue)
		}
	}

	app.Run(os.Args)
}

func fetchIssues(owner string, repo string, since time.Time) ([]github.Issue, error) {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: os.Getenv("GH_TOKEN")},
	}

	client := github.NewClient(t.Client())

	options := github.IssueListByRepoOptions{State: "closed", Sort: "updated", Since: since}
	issues, _, err := client.Issues.ListByRepo(owner, repo, &options)

	return issues, err
}

func printIssue(issue *github.Issue) {
	// TODO handle nils and missing assignee
	// fmt.Printf("#%d %s %s (%s)", issue.Number, issue.ClosedAt, issue.Title, issue.Assignee.Login)
	fmt.Printf("#%d - %s - %s\n", *issue.Number, issue.ClosedAt.Format(dateFormat), *issue.Title)
}

func utcBeginningOfWeekFromLocal() time.Time {
	now := time.Now()
	_, offset := now.Zone()
	beginningOfDay := now.UTC().Truncate(24 * time.Hour).Add(-1 * time.Duration(offset) * time.Second)
	weekFirstDay := beginningOfDay.Add(-time.Duration(now.Weekday()) * 24 * time.Hour)
	return weekFirstDay
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"os"
	"text/tabwriter"
	"time"

	"github.com/asaskevich/govalidator"
	"github.com/go-openapi/strfmt"
	"github.com/manifoldco/go-manifold"
	"github.com/manifoldco/promptui"
	"github.com/urfave/cli/v2"

	httptransport "github.com/go-openapi/runtime/client"
	"github.com/manifoldco/grafton/config"
	"github.com/manifoldco/grafton/generated/connector/client"
	"github.com/manifoldco/grafton/generated/connector/client/o_auth"
	"github.com/manifoldco/grafton/generated/connector/models"
)

const passwordMask = '●'
const defaultHostname = "manifold.co"
const defaultScheme = "https"

func apiURLPattern() string {
	scheme := os.Getenv("MANIFOLD_SCHEME")
	if scheme == "" {
		scheme = defaultScheme
	}
	hostname := os.Getenv("MANIFOLD_HOSTNAME")
	if hostname == "" {
		hostname = defaultHostname
	}
	return fmt.Sprintf("%s://api.%s.%s/v1", scheme, "%s", hostname)
}

var credentialFlags = []cli.Flag{
	&cli.StringFlag{
		Name:  "provider",
		Usage: "The label of the provider",
	},
	&cli.StringFlag{
		Name:  "product",
		Usage: "The label of the product",
	},
}

func init() {
	cmd := &cli.Command{
		Name:  "credentials",
		Usage: "Manage OAuth 2 credential pairs for Manifold.co",
		Subcommands: []*cli.Command{
			{
				Name:   "list",
				Usage:  "List all existing credentials for a product",
				Flags:  credentialFlags,
				Action: listCredentialsCmd,
			},
			{
				Name:   "rotate",
				Usage:  "Creates a new credential and sets the old one to expire in 24h",
				Flags:  credentialFlags,
				Action: createCredentialsCmd,
			},
			{
				Name:      "delete",
				ArgsUsage: "id",
				Usage:     "Delete a credential",
				Action:    deleteCredentialsCmd,
			},
		},
	}

	cmds = append(cmds, cmd)
}

func createCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	client, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	product, err := findProduct(ctx, cliCtx, client)
	if err != nil {
		return cli.NewExitError("Failed to find product: "+err.Error(), -1)
	}

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client: "+err.Error(), -1)
	}

	params := o_auth.NewPostCredentialsParamsWithContext(ctx)
	desc := "grafton rotation"

	body := &models.OAuthCredentialCreateRequest{
		Description: &desc,
		ProductID:   &product.ID,
	}
	params.SetBody(body)

	res, err := connector.OAuth.PostCredentials(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to rotate credentials: "+err.Error(), -1)
	}

	payload := res.Payload

	fmt.Println("Your old credentials will expire automatically in 24 hours")
	fmt.Println("Make sure to copy your new credentials now. You won’t be able to see this information again!")
	fmt.Printf("Client ID: %s\n", payload.ID)
	fmt.Printf("Secret: %s\n", payload.Secret)

	return nil
}

func listCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	client, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	product, err := findProduct(ctx, cliCtx, client)
	if err != nil {
		return cli.NewExitError("Failed to find product "+err.Error(), -1)
	}

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client "+err.Error(), -1)
	}

	params := o_auth.NewGetCredentialsParamsWithContext(ctx)
	pID := product.ID.String()
	params.SetProductID(&pID)

	res, err := connector.OAuth.GetCredentials(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to get credentials "+err.Error(), -1)
	}

	payload := res.Payload

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', 0)

	fmt.Fprintln(w, "ID\tCreated\tExpires")

	for _, cred := range payload {
		date := time.Time(cred.ExpiresAt)
		expires := "-"

		if !date.IsZero() {
			expires = date.Format("2006-01-02 15:04:05 MST")
		}

		created := time.Time(*cred.CreatedAt).Format("2006-01-02 15:04:05 MST")
		fmt.Fprintf(w, "%s\t%s\t%s\n", cred.ID, created, expires)
	}

	w.Flush()

	return nil
}

func deleteCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	args := cliCtx.Args()

	if args.Len() != 1 {
		cli.ShowCommandHelpAndExit(cliCtx, cliCtx.Command.Name, -1)
		return nil
	}

	id := args.First()

	_, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	params := o_auth.NewDeleteCredentialsIDParamsWithContext(ctx)
	params.SetID(id)

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client: "+err.Error(), -1)
	}

	_, err = connector.OAuth.DeleteCredentialsID(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to delete credential "+err.Error(), -1)
	}

	fmt.Println("Credential deleted!")

	return nil
}

// NewConnector creates a new connector client with the provided 'token'
func NewConnector(token string) (*client.Connector, error) {
	u, err := url.Parse(fmt.Sprintf(apiURLPattern(), "connector"))
	if err != nil {
		return nil, err
	}

	c := client.DefaultTransportConfig()
	c.WithHost(u.Host)
	c.WithBasePath(u.Path)
	c.WithSchemes([]string{u.Scheme})

	transport := httptransport.New(c.Host, c.BasePath, c.Schemes)

	transport.DefaultAuthentication = httptransport.BearerToken(token)

	return client.New(transport, strfmt.Default), nil
}

func login(ctx context.Context) (*manifold.Client, string, error) {
	fmt.Println("Please use your Manifold account to login.")
	fmt.Println("If you don't have an account yet, reach out to support@manifold.co.")

	p := promptui.Prompt{
		Label: "Email",
		Validate: func(input string) error {
			valid := govalidator.IsEmail(input)
			if valid {
				return nil
			}

			return errors.New("Please enter a valid email address")
		},
	}

	email, err := p.Run()
	if err != nil {
		return nil, "", err
	}

	p = promptui.Prompt{
		Label: "Password",
		Mask:  passwordMask,
		Validate: func(input string) error {
			if len(input) < 8 {
				return errors.New("Passwords must be greater than 8 characters")
			}

			return nil
		},
	}

	password, err := p.Run()
	if err != nil {
		return nil, "", err
	}
	cfgs := []manifold.ConfigFunc{}

	cfgs = append(cfgs, manifold.ForURLPattern(apiURLPattern()))
	cfgs = append(cfgs, manifold.WithUserAgent("grafton-"+config.Version))

	client := manifold.New(cfgs...)

	token, err := client.Login(ctx, email, password)
	if err != nil {
		return nil, "", err
	}

	cfgs = append(cfgs, manifold.WithAPIToken(token))
	client = manifold.New(cfgs...)

	return client, token, nil
}

func findProduct(ctx context.Context, cliCtx *cli.Context, client *manifold.Client) (*manifold.Product, error) {
	providerLabel := cliCtx.String("provider")
	if providerLabel == "" {
		return nil, errors.New("--provider flag missing")
	}

	productLabel := cliCtx.String("product")
	if productLabel == "" {
		return nil, errors.New("--product flag missing")
	}

	var provider *manifold.Provider
	var product *manifold.Product

	provList := client.Providers.List(ctx, nil)

	defer provList.Close()

	for provList.Next() {
		p, err := provList.Current()
		if err != nil {
			return nil, err
		}

		if p.Body.Label == providerLabel {
			provider = p
			break
		}
	}

	if provider == nil {
		return nil, fmt.Errorf("Provider %q not found", providerLabel)
	}

	opts := manifold.ProductsListOpts{ProviderID: &provider.ID}

	prodList := client.Products.List(ctx, &opts)
	defer prodList.Close()

	for prodList.Next() {
		p, err := prodList.Current()
		if err != nil {
			return nil, err
		}

		if p.Body.Label == productLabel {
			product = p
			break
		}
	}

	if product == nil {
		return nil, fmt.Errorf("Provider %q not found", providerLabel)
	}

	return product, nil
}
<commit_msg>Update the credentials rotate command to use the provider ID instead of a product ID (#190)<commit_after>package main

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"os"
	"text/tabwriter"
	"time"

	"github.com/asaskevich/govalidator"
	"github.com/go-openapi/strfmt"
	"github.com/manifoldco/go-manifold"
	"github.com/manifoldco/promptui"
	"github.com/urfave/cli/v2"

	httptransport "github.com/go-openapi/runtime/client"
	"github.com/manifoldco/grafton/config"
	"github.com/manifoldco/grafton/generated/connector/client"
	"github.com/manifoldco/grafton/generated/connector/client/o_auth"
	"github.com/manifoldco/grafton/generated/connector/models"
)

const passwordMask = '●'
const defaultHostname = "manifold.co"
const defaultScheme = "https"

func apiURLPattern() string {
	scheme := os.Getenv("MANIFOLD_SCHEME")
	if scheme == "" {
		scheme = defaultScheme
	}
	hostname := os.Getenv("MANIFOLD_HOSTNAME")
	if hostname == "" {
		hostname = defaultHostname
	}
	return fmt.Sprintf("%s://api.%s.%s/v1", scheme, "%s", hostname)
}

var credentialFlags = []cli.Flag{
	&cli.StringFlag{
		Name:  "provider",
		Usage: "The label of the provider to rotate the credentials for",
	},
}

func init() {
	cmd := &cli.Command{
		Name:  "credentials",
		Usage: "Manage OAuth 2 credential pairs for Manifold.co",
		Subcommands: []*cli.Command{
			{
				Name:   "list",
				Usage:  "List all existing credentials for a provider",
				Flags:  credentialFlags,
				Action: listCredentialsCmd,
			},
			{
				Name:   "rotate",
				Usage:  "Creates a new credential for a provider and sets the old one to expire in 24h",
				Flags:  credentialFlags,
				Action: createCredentialsCmd,
			},
			{
				Name:      "delete",
				ArgsUsage: "id",
				Usage:     "Delete a credential",
				Action:    deleteCredentialsCmd,
			},
		},
	}

	cmds = append(cmds, cmd)
}

func createCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	client, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	provider, err := findProvider(ctx, cliCtx, client)
	if err != nil {
		return cli.NewExitError("Failed to find product: "+err.Error(), -1)
	}

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client: "+err.Error(), -1)
	}

	params := o_auth.NewPostCredentialsParamsWithContext(ctx)
	desc := "grafton rotation"

	body := &models.OAuthCredentialCreateRequest{
		Description: &desc,
		ProviderID:  &provider.ID,
	}
	params.SetBody(body)

	res, err := connector.OAuth.PostCredentials(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to rotate credentials: "+err.Error(), -1)
	}

	payload := res.Payload

	fmt.Println("Your old credentials will expire automatically in 24 hours")
	fmt.Println("Make sure to copy your new credentials now. You won’t be able to see this information again!")
	fmt.Printf("Client ID: %s\n", payload.ID)
	fmt.Printf("Secret: %s\n", payload.Secret)

	return nil
}

func listCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	client, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	provider, err := findProvider(ctx, cliCtx, client)
	if err != nil {
		return cli.NewExitError("Failed to find provider "+err.Error(), -1)
	}

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client "+err.Error(), -1)
	}

	params := o_auth.NewGetCredentialsParamsWithContext(ctx)
	pID := provider.ID.String()
	params.SetProviderID(&pID)

	res, err := connector.OAuth.GetCredentials(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to get credentials "+err.Error(), -1)
	}

	payload := res.Payload

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', 0)

	fmt.Fprintln(w, "ID\tCreated\tExpires")

	for _, cred := range payload {
		date := time.Time(cred.ExpiresAt)
		expires := "-"

		if !date.IsZero() {
			expires = date.Format("2006-01-02 15:04:05 MST")
		}

		created := time.Time(*cred.CreatedAt).Format("2006-01-02 15:04:05 MST")
		fmt.Fprintf(w, "%s\t%s\t%s\n", cred.ID, created, expires)
	}

	w.Flush()

	return nil
}

func deleteCredentialsCmd(cliCtx *cli.Context) error {
	ctx := context.Background()

	args := cliCtx.Args()

	if args.Len() != 1 {
		cli.ShowCommandHelpAndExit(cliCtx, cliCtx.Command.Name, -1)
		return nil
	}

	id := args.First()

	_, token, err := login(ctx)
	if err != nil {
		return cli.NewExitError(err.Error(), -1)
	}

	params := o_auth.NewDeleteCredentialsIDParamsWithContext(ctx)
	params.SetID(id)

	connector, err := NewConnector(token)
	if err != nil {
		return cli.NewExitError("Failed to create connector client: "+err.Error(), -1)
	}

	_, err = connector.OAuth.DeleteCredentialsID(params, nil)
	if err != nil {
		return cli.NewExitError("Failed to delete credential "+err.Error(), -1)
	}

	fmt.Println("Credential deleted!")

	return nil
}

// NewConnector creates a new connector client with the provided 'token'
func NewConnector(token string) (*client.Connector, error) {
	u, err := url.Parse(fmt.Sprintf(apiURLPattern(), "connector"))
	if err != nil {
		return nil, err
	}

	c := client.DefaultTransportConfig()
	c.WithHost(u.Host)
	c.WithBasePath(u.Path)
	c.WithSchemes([]string{u.Scheme})

	transport := httptransport.New(c.Host, c.BasePath, c.Schemes)

	transport.DefaultAuthentication = httptransport.BearerToken(token)

	return client.New(transport, strfmt.Default), nil
}

func login(ctx context.Context) (*manifold.Client, string, error) {
	fmt.Println("Please use your Manifold account to login.")
	fmt.Println("If you don't have an account yet, reach out to support@manifold.co.")

	p := promptui.Prompt{
		Label: "Email",
		Validate: func(input string) error {
			valid := govalidator.IsEmail(input)
			if valid {
				return nil
			}

			return errors.New("Please enter a valid email address")
		},
	}

	email, err := p.Run()
	if err != nil {
		return nil, "", err
	}

	p = promptui.Prompt{
		Label: "Password",
		Mask:  passwordMask,
		Validate: func(input string) error {
			if len(input) < 8 {
				return errors.New("Passwords must be greater than 8 characters")
			}

			return nil
		},
	}

	password, err := p.Run()
	if err != nil {
		return nil, "", err
	}
	cfgs := []manifold.ConfigFunc{}

	cfgs = append(cfgs, manifold.ForURLPattern(apiURLPattern()))
	cfgs = append(cfgs, manifold.WithUserAgent("grafton-"+config.Version))

	client := manifold.New(cfgs...)

	token, err := client.Login(ctx, email, password)
	if err != nil {
		return nil, "", err
	}

	cfgs = append(cfgs, manifold.WithAPIToken(token))
	client = manifold.New(cfgs...)

	return client, token, nil
}

func findProvider(ctx context.Context, cliCtx *cli.Context, client *manifold.Client) (*manifold.Provider, error) {
	providerLabel := cliCtx.String("provider")
	if providerLabel == "" {
		return nil, errors.New("--provider flag missing")
	}

	var provider *manifold.Provider

	provList := client.Providers.List(ctx, nil)

	defer provList.Close()

	for provList.Next() {
		p, err := provList.Current()
		if err != nil {
			return nil, err
		}

		if p.Body.Label == providerLabel {
			provider = p
			break
		}
	}

	if provider == nil {
		return nil, fmt.Errorf("Provider %q not found", providerLabel)
	}

	return provider, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/docker/go-units"
	"github.com/go-debos/debos"
	"github.com/go-debos/debos/recipe"
	"github.com/go-debos/fakemachine"
	"github.com/jessevdk/go-flags"
)

func checkError(context debos.DebosContext, err error, a debos.Action, stage string) int {
	if err == nil {
		return 0
	}

	log.Printf("Action `%s` failed at stage %s, error: %s", a, stage, err)
	debos.DebugShell(context)
	return 1
}

func main() {
	var context debos.DebosContext
	var options struct {
		ArtifactDir   string            `long:"artifactdir"`
		InternalImage string            `long:"internal-image" hidden:"true"`
		TemplateVars  map[string]string `short:"t" long:"template-var" description:"Template variables"`
		DebugShell    bool              `long:"debug-shell" description:"Fall into interactive shell on error"`
		Shell         string            `short:"s" long:"shell" description:"Redefine interactive shell binary (default: bash)" optionsl:"" default:"/bin/bash"`
		ScratchSize   string            `long:"scratchsize" description:"Size of disk backed scratch space"`
	}

	var exitcode int = 0
	// Allow to run all deferred calls prior to os.Exit()
	defer func() {
		os.Exit(exitcode)
	}()

	parser := flags.NewParser(&options, flags.Default)
	args, err := parser.Parse()

	if err != nil {
		flagsErr, ok := err.(*flags.Error)
		if ok && flagsErr.Type == flags.ErrHelp {
			return
		} else {
			fmt.Printf("%v\n", flagsErr)
			exitcode = 1
			return
		}
	}

	if len(args) != 1 {
		log.Println("No recipe given!")
		exitcode = 1
		return
	}

	// Set interactive shell binary only if '--debug-shell' options passed
	if options.DebugShell {
		context.DebugShell = options.Shell
	}

	file := args[0]
	file = debos.CleanPath(file)

	r := recipe.Recipe{}
	if _, err := os.Stat(file); os.IsNotExist(err) {
		log.Println(err)
		exitcode = 1
		return
	}
	if err := r.Parse(file, options.TemplateVars); err != nil {
		log.Println(err)
		exitcode = 1
		return
	}

	/* If fakemachine is supported the outer fake machine will never use the
	 * scratchdir, so just set it to /scrach as a dummy to prevent the outer
	 * debos createing a temporary direction */
	if fakemachine.InMachine() || fakemachine.Supported() {
		context.Scratchdir = "/scratch"
	} else {
		log.Printf("fakemachine not supported, running on the host!")
		cwd, _ := os.Getwd()
		context.Scratchdir, err = ioutil.TempDir(cwd, ".debos-")
		defer os.RemoveAll(context.Scratchdir)
	}

	context.Rootdir = path.Join(context.Scratchdir, "root")
	context.Image = options.InternalImage
	context.RecipeDir = path.Dir(file)

	context.Artifactdir = options.ArtifactDir
	if context.Artifactdir == "" {
		context.Artifactdir, _ = os.Getwd()
	}
	context.Artifactdir = debos.CleanPath(context.Artifactdir)

	// Initialise origins map
	context.Origins = make(map[string]string)
	context.Origins["artifacts"] = context.Artifactdir
	context.Origins["filesystem"] = context.Rootdir
	context.Origins["recipe"] = context.RecipeDir

	context.Architecture = r.Architecture

	for _, a := range r.Actions {
		err = a.Verify(&context)
		if exitcode = checkError(context, err, a, "Verify"); exitcode != 0 {
			return
		}
	}

	if !fakemachine.InMachine() && fakemachine.Supported() {
		m := fakemachine.NewMachine()
		var args []string

		if options.ScratchSize != "" {
			size, err := units.FromHumanSize(options.ScratchSize)
			if err != nil {
				fmt.Printf("Couldn't parse scratch size: %v\n", err)
				exitcode = 1
				return
			}
			m.SetScratch(size, "")
		}

		m.AddVolume(context.Artifactdir)
		args = append(args, "--artifactdir", context.Artifactdir)

		for k, v := range options.TemplateVars {
			args = append(args, "--template-var", fmt.Sprintf("%s:\"%s\"", k, v))
		}

		m.AddVolume(context.RecipeDir)
		args = append(args, file)

		if options.DebugShell {
			args = append(args, "--debug-shell")
			args = append(args, "--shell", fmt.Sprintf("%s", options.Shell))
		}

		for _, a := range r.Actions {
			err = a.PreMachine(&context, m, &args)
			if exitcode = checkError(context, err, a, "PreMachine"); exitcode != 0 {
				return
			}
		}

		exitcode, err = m.RunInMachineWithArgs(args)
		if err != nil {
			fmt.Println(err)
			return
		}

		if exitcode != 0 {
			return
		}

		for _, a := range r.Actions {
			err = a.PostMachine(context)
			if exitcode = checkError(context, err, a, "Postmachine"); exitcode != 0 {
				return
			}
		}

		log.Printf("==== Recipe done ====")
		return
	}

	if !fakemachine.InMachine() {
		for _, a := range r.Actions {
			err = a.PreNoMachine(&context)
			if exitcode = checkError(context, err, a, "PreNoMachine"); exitcode != 0 {
				return
			}
		}
	}

	for _, a := range r.Actions {
		err = a.Run(&context)
		if exitcode = checkError(context, err, a, "Run"); exitcode != 0 {
			return
		}
	}

	for _, a := range r.Actions {
		err = a.Cleanup(context)
		if exitcode = checkError(context, err, a, "Cleanup"); exitcode != 0 {
			return
		}
	}

	if !fakemachine.InMachine() {
		for _, a := range r.Actions {
			err = a.PostMachine(context)
			if exitcode = checkError(context, err, a, "PostMachine"); exitcode != 0 {
				return
			}
		}
		log.Printf("==== Recipe done ====")
	}
}
<commit_msg>Create root directory during initialization<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/docker/go-units"
	"github.com/go-debos/debos"
	"github.com/go-debos/debos/recipe"
	"github.com/go-debos/fakemachine"
	"github.com/jessevdk/go-flags"
)

func checkError(context debos.DebosContext, err error, a debos.Action, stage string) int {
	if err == nil {
		return 0
	}

	log.Printf("Action `%s` failed at stage %s, error: %s", a, stage, err)
	debos.DebugShell(context)
	return 1
}

func main() {
	var context debos.DebosContext
	var options struct {
		ArtifactDir   string            `long:"artifactdir"`
		InternalImage string            `long:"internal-image" hidden:"true"`
		TemplateVars  map[string]string `short:"t" long:"template-var" description:"Template variables"`
		DebugShell    bool              `long:"debug-shell" description:"Fall into interactive shell on error"`
		Shell         string            `short:"s" long:"shell" description:"Redefine interactive shell binary (default: bash)" optionsl:"" default:"/bin/bash"`
		ScratchSize   string            `long:"scratchsize" description:"Size of disk backed scratch space"`
	}

	var exitcode int = 0
	// Allow to run all deferred calls prior to os.Exit()
	defer func() {
		os.Exit(exitcode)
	}()

	parser := flags.NewParser(&options, flags.Default)
	args, err := parser.Parse()

	if err != nil {
		flagsErr, ok := err.(*flags.Error)
		if ok && flagsErr.Type == flags.ErrHelp {
			return
		} else {
			fmt.Printf("%v\n", flagsErr)
			exitcode = 1
			return
		}
	}

	if len(args) != 1 {
		log.Println("No recipe given!")
		exitcode = 1
		return
	}

	// Set interactive shell binary only if '--debug-shell' options passed
	if options.DebugShell {
		context.DebugShell = options.Shell
	}

	file := args[0]
	file = debos.CleanPath(file)

	r := recipe.Recipe{}
	if _, err := os.Stat(file); os.IsNotExist(err) {
		log.Println(err)
		exitcode = 1
		return
	}
	if err := r.Parse(file, options.TemplateVars); err != nil {
		log.Println(err)
		exitcode = 1
		return
	}

	/* If fakemachine is supported the outer fake machine will never use the
	 * scratchdir, so just set it to /scrach as a dummy to prevent the outer
	 * debos createing a temporary direction */
	if fakemachine.InMachine() || fakemachine.Supported() {
		context.Scratchdir = "/scratch"
	} else {
		log.Printf("fakemachine not supported, running on the host!")
		cwd, _ := os.Getwd()
		context.Scratchdir, err = ioutil.TempDir(cwd, ".debos-")
		defer os.RemoveAll(context.Scratchdir)
	}

	context.Rootdir = path.Join(context.Scratchdir, "root")
	context.Image = options.InternalImage
	context.RecipeDir = path.Dir(file)

	context.Artifactdir = options.ArtifactDir
	if context.Artifactdir == "" {
		context.Artifactdir, _ = os.Getwd()
	}
	context.Artifactdir = debos.CleanPath(context.Artifactdir)

	// Initialise origins map
	context.Origins = make(map[string]string)
	context.Origins["artifacts"] = context.Artifactdir
	context.Origins["filesystem"] = context.Rootdir
	context.Origins["recipe"] = context.RecipeDir

	context.Architecture = r.Architecture

	for _, a := range r.Actions {
		err = a.Verify(&context)
		if exitcode = checkError(context, err, a, "Verify"); exitcode != 0 {
			return
		}
	}

	if !fakemachine.InMachine() && fakemachine.Supported() {
		m := fakemachine.NewMachine()
		var args []string

		if options.ScratchSize != "" {
			size, err := units.FromHumanSize(options.ScratchSize)
			if err != nil {
				fmt.Printf("Couldn't parse scratch size: %v\n", err)
				exitcode = 1
				return
			}
			m.SetScratch(size, "")
		}

		m.AddVolume(context.Artifactdir)
		args = append(args, "--artifactdir", context.Artifactdir)

		for k, v := range options.TemplateVars {
			args = append(args, "--template-var", fmt.Sprintf("%s:\"%s\"", k, v))
		}

		m.AddVolume(context.RecipeDir)
		args = append(args, file)

		if options.DebugShell {
			args = append(args, "--debug-shell")
			args = append(args, "--shell", fmt.Sprintf("%s", options.Shell))
		}

		for _, a := range r.Actions {
			err = a.PreMachine(&context, m, &args)
			if exitcode = checkError(context, err, a, "PreMachine"); exitcode != 0 {
				return
			}
		}

		exitcode, err = m.RunInMachineWithArgs(args)
		if err != nil {
			fmt.Println(err)
			return
		}

		if exitcode != 0 {
			return
		}

		for _, a := range r.Actions {
			err = a.PostMachine(context)
			if exitcode = checkError(context, err, a, "Postmachine"); exitcode != 0 {
				return
			}
		}

		log.Printf("==== Recipe done ====")
		return
	}

	if !fakemachine.InMachine() {
		for _, a := range r.Actions {
			err = a.PreNoMachine(&context)
			if exitcode = checkError(context, err, a, "PreNoMachine"); exitcode != 0 {
				return
			}
		}
	}

	// Create Rootdir
	if _, err = os.Stat(context.Rootdir); os.IsNotExist(err) {
		err = os.Mkdir(context.Rootdir, 755)
		if err != nil && os.IsNotExist(err) {
			exitcode = 1
			return
		}
	}

	for _, a := range r.Actions {
		err = a.Run(&context)
		if exitcode = checkError(context, err, a, "Run"); exitcode != 0 {
			return
		}
	}

	for _, a := range r.Actions {
		err = a.Cleanup(context)
		if exitcode = checkError(context, err, a, "Cleanup"); exitcode != 0 {
			return
		}
	}

	if !fakemachine.InMachine() {
		for _, a := range r.Actions {
			err = a.PostMachine(context)
			if exitcode = checkError(context, err, a, "PostMachine"); exitcode != 0 {
				return
			}
		}
		log.Printf("==== Recipe done ====")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/metadata"
	"github.com/containerd/containerd/progress"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

var imageCommand = cli.Command{
	Name:    "images",
	Aliases: []string{"images"},
	Usage:   "image management",
	Subcommands: cli.Commands{
		imagesListCommand,
		imageRemoveCommand,
	},
}

var imagesListCommand = cli.Command{
	Name:        "list",
	Aliases:     []string{"ls"},
	Usage:       "list images known to containerd",
	ArgsUsage:   "[flags] <ref>",
	Description: `List images registered with containerd.`,
	Flags:       []cli.Flag{},
	Action: func(clicontext *cli.Context) error {
		ctx, cancel := appContext(clicontext)
		defer cancel()

		imageStore, err := resolveImageStore(clicontext)
		if err != nil {
			return err
		}

		cs, err := resolveContentStore(clicontext)
		if err != nil {
			return err
		}

		images, err := imageStore.List(ctx)
		if err != nil {
			return errors.Wrap(err, "failed to list images")
		}

		tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
		fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSIZE\t")
		for _, image := range images {
			size, err := image.Size(ctx, cs)
			if err != nil {
				log.G(ctx).WithError(err).Errorf("failed calculating size for image %s", image.Name)
			}

			fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t\n", image.Name, image.Target.MediaType, image.Target.Digest, progress.Bytes(size))
		}
		tw.Flush()

		return nil
	},
}

var imageRemoveCommand = cli.Command{
	Name:        "remove",
	Aliases:     []string{"rm"},
	Usage:       "Remove one or more images by reference.",
	ArgsUsage:   "[flags] <ref> [<ref>, ...]",
	Description: `Remove one or more images by reference.`,
	Flags:       []cli.Flag{},
	Action: func(clicontext *cli.Context) error {
		var (
			exitErr error
		)
		ctx, cancel := appContext(clicontext)
		defer cancel()

		imageStore, err := resolveImageStore(clicontext)
		if err != nil {
			return err
		}

		for _, target := range clicontext.Args() {
			if err := imageStore.Delete(ctx, target); err != nil {
				if !metadata.IsNotFound(err) {
					if exitErr == nil {
						exitErr = errors.Wrapf(err, "unable to delete %v", target)
					}
					log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
					continue
				}
			}

			fmt.Println(target)
		}

		return exitErr
	},
}
<commit_msg>Remove the redundant alias of 'dist images'<commit_after>package main

import (
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/metadata"
	"github.com/containerd/containerd/progress"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

var imageCommand = cli.Command{
	Name:  "images",
	Usage: "image management",
	Subcommands: cli.Commands{
		imagesListCommand,
		imageRemoveCommand,
	},
}

var imagesListCommand = cli.Command{
	Name:        "list",
	Aliases:     []string{"ls"},
	Usage:       "list images known to containerd",
	ArgsUsage:   "[flags] <ref>",
	Description: `List images registered with containerd.`,
	Flags:       []cli.Flag{},
	Action: func(clicontext *cli.Context) error {
		ctx, cancel := appContext(clicontext)
		defer cancel()

		imageStore, err := resolveImageStore(clicontext)
		if err != nil {
			return err
		}

		cs, err := resolveContentStore(clicontext)
		if err != nil {
			return err
		}

		images, err := imageStore.List(ctx)
		if err != nil {
			return errors.Wrap(err, "failed to list images")
		}

		tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
		fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSIZE\t")
		for _, image := range images {
			size, err := image.Size(ctx, cs)
			if err != nil {
				log.G(ctx).WithError(err).Errorf("failed calculating size for image %s", image.Name)
			}

			fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t\n", image.Name, image.Target.MediaType, image.Target.Digest, progress.Bytes(size))
		}
		tw.Flush()

		return nil
	},
}

var imageRemoveCommand = cli.Command{
	Name:        "remove",
	Aliases:     []string{"rm"},
	Usage:       "Remove one or more images by reference.",
	ArgsUsage:   "[flags] <ref> [<ref>, ...]",
	Description: `Remove one or more images by reference.`,
	Flags:       []cli.Flag{},
	Action: func(clicontext *cli.Context) error {
		var (
			exitErr error
		)
		ctx, cancel := appContext(clicontext)
		defer cancel()

		imageStore, err := resolveImageStore(clicontext)
		if err != nil {
			return err
		}

		for _, target := range clicontext.Args() {
			if err := imageStore.Delete(ctx, target); err != nil {
				if !metadata.IsNotFound(err) {
					if exitErr == nil {
						exitErr = errors.Wrapf(err, "unable to delete %v", target)
					}
					log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
					continue
				}
			}

			fmt.Println(target)
		}

		return exitErr
	},
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"math"
	"path/filepath"
	"sort"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/math/sint"
	"github.com/google/gapid/gapis/client"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/service/path"
)

type infoVerb struct{ StatsFlags }

func init() {
	verb := &infoVerb{}
	verb.Frames.Count = -1
	app.AddVerb(&app.Verb{
		Name:      "stats",
		ShortHelp: "Prints information about a capture file",
		Action:    verb,
	})
}

func loadCapture(ctx context.Context, flags flag.FlagSet, gapisFlags GapisFlags) (client.Client, *path.Capture, error) {
	if flags.NArg() != 1 {
		app.Usage(ctx, "Exactly one gfx trace file expected, got %d", flags.NArg())
		return nil, nil, nil
	}

	filepath, err := filepath.Abs(flags.Arg(0))
	if err != nil {
		return nil, nil, log.Errf(ctx, err, "Finding file: %v", flags.Arg(0))
	}

	client, err := getGapis(ctx, gapisFlags, GapirFlags{})
	if err != nil {
		return nil, nil, log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}

	capture, err := client.LoadCapture(ctx, filepath)
	if err != nil {
		return nil, nil, log.Errf(ctx, err, "LoadCapture(%v)", filepath)
	}

	return client, capture, nil
}

func (verb *infoVerb) getEventsInRange(ctx context.Context, client service.Service, capture *path.Capture) ([]*service.Event, error) {
	events, err := getEvents(ctx, client, &path.Events{
		Capture:                 capture,
		AllCommands:             true,
		DrawCalls:               true,
		FirstInFrame:            true,
		LastInFrame:             true,
		FramebufferObservations: true,
	})
	if err != nil {
		return nil, err
	}

	if verb.Frames.Start == 0 && verb.Frames.Count == -1 {
		return events, err
	}

	fifIndices := []uint64{}
	for _, e := range events {
		if e.Kind == service.EventKind_FirstInFrame {
			fifIndices = append(fifIndices, e.Command.Indices[0])
		}
	}

	if verb.Frames.Start < 0 {
		return nil, log.Errf(ctx, nil, "Negative start frame %v is invalid", verb.Frames.Start)
	}
	if verb.Frames.Start >= len(fifIndices) {
		return nil, log.Errf(ctx, nil, "Captured only %v frames, not greater than start frame %v", len(fifIndices), verb.Frames.Start)
	}

	startIndex := fifIndices[verb.Frames.Start]
	endIndex := uint64(math.MaxUint64)
	if verb.Frames.Count >= 0 &&
		verb.Frames.Start+verb.Frames.Count < len(fifIndices) {

		endIndex = fifIndices[verb.Frames.Start+verb.Frames.Count]
	}

	begin := sort.Search(len(events), func(i int) bool {
		return events[i].Command.Indices[0] >= startIndex
	})
	end := sort.Search(len(events), func(i int) bool {
		return events[i].Command.Indices[0] >= endIndex
	})
	return events[begin:end], nil
}

func (verb *infoVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	client, capture, err := loadCapture(ctx, flags, verb.Gapis)
	if err != nil {
		return err
	}
	defer client.Close()

	events, err := verb.getEventsInRange(ctx, client, capture)

	if err != nil {
		return log.Err(ctx, err, "Couldn't get events")
	}

	counts := map[service.EventKind]int{}
	cmdsPerFrame, frameIdx := sint.Histogram{}, 0
	for i, e := range events {
		counts[e.Kind]++
		switch e.Kind {
		case service.EventKind_AllCommands:
			cmdsPerFrame.Add(frameIdx, 1)
		case service.EventKind_FirstInFrame:
			if i > 0 {
				frameIdx++
			}
		}
	}
	callStats := cmdsPerFrame.Stats()

	fmt.Println("Commands:                  ", counts[service.EventKind_AllCommands])
	fmt.Println("Frames:                    ", counts[service.EventKind_FirstInFrame])
	fmt.Println("Draws:                     ", counts[service.EventKind_DrawCall])
	fmt.Println("FBO:                       ", counts[service.EventKind_FramebufferObservation])
	fmt.Printf("Avg commands per frame:     %.2f\n", callStats.Average)
	fmt.Printf("Stddev commands per frame:  %.2f\n", callStats.Stddev)
	fmt.Println("Median commands per frame: ", callStats.Median)

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

package main

import (
	"context"
	"flag"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"sort"
	"text/tabwriter"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/math/sint"
	"github.com/google/gapid/gapis/client"
	"github.com/google/gapid/gapis/service"
	"github.com/google/gapid/gapis/service/path"
)

type infoVerb struct{ StatsFlags }

func init() {
	verb := &infoVerb{}
	verb.Frames.Count = -1
	app.AddVerb(&app.Verb{
		Name:      "stats",
		ShortHelp: "Prints information about a capture file",
		Action:    verb,
	})
}

func loadCapture(ctx context.Context, flags flag.FlagSet, gapisFlags GapisFlags) (client.Client, *path.Capture, error) {
	if flags.NArg() != 1 {
		app.Usage(ctx, "Exactly one gfx trace file expected, got %d", flags.NArg())
		return nil, nil, nil
	}

	filepath, err := filepath.Abs(flags.Arg(0))
	if err != nil {
		return nil, nil, log.Errf(ctx, err, "Finding file: %v", flags.Arg(0))
	}

	client, err := getGapis(ctx, gapisFlags, GapirFlags{})
	if err != nil {
		return nil, nil, log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}

	capture, err := client.LoadCapture(ctx, filepath)
	if err != nil {
		return nil, nil, log.Errf(ctx, err, "LoadCapture(%v)", filepath)
	}

	return client, capture, nil
}

func (verb *infoVerb) getEventsInRange(ctx context.Context, client service.Service, capture *path.Capture) ([]*service.Event, error) {
	events, err := getEvents(ctx, client, &path.Events{
		Capture:                 capture,
		AllCommands:             true,
		FirstInFrame:            true,
		LastInFrame:             true,
		FramebufferObservations: true,
	})
	if err != nil {
		return nil, err
	}

	if verb.Frames.Start == 0 && verb.Frames.Count == -1 {
		return events, err
	}

	fifIndices := []uint64{}
	for _, e := range events {
		if e.Kind == service.EventKind_FirstInFrame {
			fifIndices = append(fifIndices, e.Command.Indices[0])
		}
	}

	if verb.Frames.Start < 0 {
		return nil, log.Errf(ctx, nil, "Negative start frame %v is invalid", verb.Frames.Start)
	}
	if verb.Frames.Start >= len(fifIndices) {
		return nil, log.Errf(ctx, nil, "Captured only %v frames, not greater than start frame %v", len(fifIndices), verb.Frames.Start)
	}

	startIndex := fifIndices[verb.Frames.Start]
	endIndex := uint64(math.MaxUint64)
	if verb.Frames.Count >= 0 &&
		verb.Frames.Start+verb.Frames.Count < len(fifIndices) {

		endIndex = fifIndices[verb.Frames.Start+verb.Frames.Count]
	}

	begin := sort.Search(len(events), func(i int) bool {
		return events[i].Command.Indices[0] >= startIndex
	})
	end := sort.Search(len(events), func(i int) bool {
		return events[i].Command.Indices[0] >= endIndex
	})
	return events[begin:end], nil
}

func (verb *infoVerb) drawCallStats(ctx context.Context, client client.Client, c *path.Capture) (int, sint.HistogramStats, error) {
	boxedVal, err := client.Get(ctx, (&path.Stats{
		Capture:  c,
		DrawCall: true,
	}).Path())
	if err != nil {
		return 0, sint.HistogramStats{}, err
	}
	data := boxedVal.(*service.Stats).DrawCalls

	if verb.Frames.Start < len(data) {
		data = data[verb.Frames.Start:]
	} else {
		data = []uint64{}
	}
	if verb.Frames.Count >= 0 && verb.Frames.Count < len(data) {
		data = data[:verb.Frames.Count]
	}

	hist := make(sint.Histogram, len(data))
	totalDraws := 0
	for i, dat := range data {
		totalDraws += int(dat)
		hist[i] = int(dat)
	}
	return totalDraws, hist.Stats(), nil
}

func (verb *infoVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	client, capture, err := loadCapture(ctx, flags, verb.Gapis)
	if err != nil {
		return err
	}
	defer client.Close()

	events, err := verb.getEventsInRange(ctx, client, capture)

	if err != nil {
		return log.Err(ctx, err, "Couldn't get events")
	}

	counts := map[service.EventKind]int{}
	cmdsPerFrame, frameIdx := sint.Histogram{}, 0
	for i, e := range events {
		counts[e.Kind]++
		switch e.Kind {
		case service.EventKind_AllCommands:
			cmdsPerFrame.Add(frameIdx, 1)
		case service.EventKind_FirstInFrame:
			if i > 0 {
				frameIdx++
			}
		}
	}
	callStats := cmdsPerFrame.Stats()
	totalDraws, drawStats, err := verb.drawCallStats(ctx, client, capture)
	if err != nil {
		return err
	}

	w := tabwriter.NewWriter(os.Stdout, 4, 4, 0, ' ', 0)
	fmt.Fprintf(w, "Commands: \t%v\n", counts[service.EventKind_AllCommands])
	fmt.Fprintf(w, "Frames: \t%v\n", counts[service.EventKind_FirstInFrame])
	fmt.Fprintf(w, "Draws: \t%v\n", totalDraws)
	fmt.Fprintf(w, "FBO: \t%v\n", counts[service.EventKind_FramebufferObservation])

	fmt.Fprintf(w, "Avg commands per frame: \t%.2f\n", callStats.Average)
	fmt.Fprintf(w, "Stddev commands per frame: \t%.2f\n", callStats.Stddev)
	fmt.Fprintf(w, "Median commands per frame: \t%v\n", callStats.Median)

	fmt.Fprintf(w, "Avg draw calls per frame: \t%.2f\n", drawStats.Average)
	fmt.Fprintf(w, "Stddev draw calls per frame: \t%.2f\n", drawStats.Stddev)
	fmt.Fprintf(w, "Median draw calls per frame: \t%v\n", drawStats.Median)
	w.Flush()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/donatj/hookah"
)

var (
	httpPort   = flag.Uint("http-port", 8080, "HTTP port to listen on")
	serverRoot = flag.String("server-root", ".", "The root directory of the hook script hierarchy")
	secret     = flag.String("secret", "", "Optional Github HMAC secret key")
	timeout    = flag.Duration("timeout", 10*time.Minute, "Exec timeout on hook scripts")
)

func init() {
	flag.Parse()
}

func main() {
	hServe, err := hookah.NewHookServer(*serverRoot, *secret, *timeout)
	if err != nil {
		log.Fatal(err)
	}

	httpMux := http.NewServeMux()
	httpMux.Handle("/", hServe)

	err = http.ListenAndServe(":"+strconv.Itoa(int(*httpPort)), httpMux)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>No mux nessessary<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/donatj/hookah"
)

var (
	httpPort   = flag.Uint("http-port", 8080, "HTTP port to listen on")
	serverRoot = flag.String("server-root", ".", "The root directory of the hook script hierarchy")
	secret     = flag.String("secret", "", "Optional Github HMAC secret key")
	timeout    = flag.Duration("timeout", 10*time.Minute, "Exec timeout on hook scripts")
)

func init() {
	flag.Parse()
}

func main() {
	hServe, err := hookah.NewHookServer(*serverRoot, *secret, *timeout)
	if err != nil {
		log.Fatal(err)
	}

	err = http.ListenAndServe(":"+strconv.Itoa(int(*httpPort)), hServe)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>