<commit_before>package main

import (
	"flag"
	"github.com/andres-erbsen/chatterbox/client"
	"github.com/andres-erbsen/chatterbox/proto"
	"github.com/andres-erbsen/chatterbox/transport"
	//	"github.com/andres-erbsen/client/clientutil"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
)

type hex32Byte [32]byte

func (h *hex32Byte) String() string {
	return hex.EncodeToString(h[:])
}
func (h *hex32Byte) Set(value string) error {
	if len(value) != 2*32 {
		return fmt.Errorf("Server pubkey must be 64 hex digits long, got %d", len(value))
	}
	_, err := hex.Decode(h[:], []byte(value))
	if err != nil {
		return err
	}
	return err
}

func main() {
	dename := flag.String("dename", "", "Your dename username.")
	var serverTransportPubkey [32]byte
	flag.Var((*hex32Byte)(&serverTransportPubkey), "server-pubkey", "The TCP port which the server listens on. Note that people sending you mesages expct to be able to reach your home server at port 1984.")
	serverAddress := flag.String("server-host", "", "The IP address or hostname on which your (prospective) home server server can be reached")
	serverPort := flag.Int("server-port", 1984, "The TCP port which the server listens on.")
	dir := flag.String("account-directory", "", "Dedicated directory for the account.")
	flag.Parse()

	if *dename == "" || serverTransportPubkey == [32]byte{} || *serverAddress == "" {
		flag.Usage()
		os.Exit(2)
	}
	if *dir == "" {
		*dir = filepath.Join(os.Getenv("HOME"), ".chatterbox", *dename)
	}

	secretConfig := &proto.LocalAccountConfig{
		ServerAddressTCP:  *serverAddress,
		ServerPortTCP:     int32(*serverPort),
		ServerTransportPK: serverTransportPubkey,
		Dename:            []byte(*dename),
	}
	publicProfile := &proto.Profile{
		ServerAddressTCP:  *serverAddress,
		ServerPortTCP:     int32(*serverPort),
		ServerTransportPK: serverTransportPubkey,
	}
	if err := client.GenerateLongTermKeys(secretConfig, publicProfile, rand.Reader); err != nil {
		panic(err)
	}
	secretConfigBytes, err := secretConfig.Marshal()
	if err != nil {
		panic(err)
	}
	publicProfileBytes, err := publicProfile.Marshal()
	if err != nil {
		panic(err)
	}

	if err := os.Mkdir(*dir, 0700); err != nil && !os.IsExist(err) {
		fmt.Fprintf(os.Stderr, "could not create directory %s: %s", *dir, err)
		os.Exit(2)
	}
	configFilePath := filepath.Join(*dir, "config.pb")
	if _, err := os.Stat(configFilePath); !os.IsNotExist(err) {
		fmt.Fprintf(os.Stderr, "file already exists: %s\n", configFilePath)
		os.Exit(2)
	}
	if err := ioutil.WriteFile(configFilePath, secretConfigBytes, 0600); err != nil {
		// TODO: "WriteFileSync" -- issue fsync after write
		fmt.Fprintf(os.Stderr, "error writing file %s: %s\n", configFilePath, err)
		os.Exit(2)
	}
	profileFilePath := filepath.Join(*dir, "profile.pb")
	if err := ioutil.WriteFile(profileFilePath, publicProfileBytes, 0600); err != nil {
		// TODO: "WriteFileSync" -- issue fsync after write
		fmt.Fprintf(os.Stderr, "error writing file %s: %s\n", profileFilePath, err)
		os.Exit(2)
	}

	fmt.Printf("You may use the following command to link this account with your dename profile:.\n"+
		"dnmgr set '%s' 1984 - < %s\n", *dename, profileFilePath)

	// TODO: use TOR
	fmt.Printf("Registering with the server...\n")
	plainConn, err := net.Dial("tcp", net.JoinHostPort(*serverAddress, fmt.Sprint(*serverPort)))
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to server at %s%s: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	conn, _, err := transport.Handshake(plainConn,
		(*[32]byte)(&publicProfile.UserIDAtServer),
		(*[32]byte)(&secretConfig.TransportSecretKeyForServer),
		&serverTransportPubkey, proto.SERVER_MESSAGE_SIZE)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to server at %s%s: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	if err := client.CreateAccount(conn, make([]byte, proto.SERVER_MESSAGE_SIZE),
		make([]byte, proto.SERVER_MESSAGE_SIZE)); err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to serverat %s:%d: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	fmt.Printf("Account initialization done.\n")
}
<commit_msg>dename 958b885: no dash on dnmgr set from stdin<commit_after>package main

import (
	"flag"
	"github.com/andres-erbsen/chatterbox/client"
	"github.com/andres-erbsen/chatterbox/proto"
	"github.com/andres-erbsen/chatterbox/transport"
	//	"github.com/andres-erbsen/client/clientutil"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
)

type hex32Byte [32]byte

func (h *hex32Byte) String() string {
	return hex.EncodeToString(h[:])
}
func (h *hex32Byte) Set(value string) error {
	if len(value) != 2*32 {
		return fmt.Errorf("Server pubkey must be 64 hex digits long, got %d", len(value))
	}
	_, err := hex.Decode(h[:], []byte(value))
	if err != nil {
		return err
	}
	return err
}

func main() {
	dename := flag.String("dename", "", "Your dename username.")
	var serverTransportPubkey [32]byte
	flag.Var((*hex32Byte)(&serverTransportPubkey), "server-pubkey", "The TCP port which the server listens on. Note that people sending you mesages expct to be able to reach your home server at port 1984.")
	serverAddress := flag.String("server-host", "", "The IP address or hostname on which your (prospective) home server server can be reached")
	serverPort := flag.Int("server-port", 1984, "The TCP port which the server listens on.")
	dir := flag.String("account-directory", "", "Dedicated directory for the account.")
	flag.Parse()

	if *dename == "" || serverTransportPubkey == [32]byte{} || *serverAddress == "" {
		flag.Usage()
		os.Exit(2)
	}
	if *dir == "" {
		*dir = filepath.Join(os.Getenv("HOME"), ".chatterbox", *dename)
	}

	secretConfig := &proto.LocalAccountConfig{
		ServerAddressTCP:  *serverAddress,
		ServerPortTCP:     int32(*serverPort),
		ServerTransportPK: serverTransportPubkey,
		Dename:            []byte(*dename),
	}
	publicProfile := &proto.Profile{
		ServerAddressTCP:  *serverAddress,
		ServerPortTCP:     int32(*serverPort),
		ServerTransportPK: serverTransportPubkey,
	}
	if err := client.GenerateLongTermKeys(secretConfig, publicProfile, rand.Reader); err != nil {
		panic(err)
	}
	secretConfigBytes, err := secretConfig.Marshal()
	if err != nil {
		panic(err)
	}
	publicProfileBytes, err := publicProfile.Marshal()
	if err != nil {
		panic(err)
	}

	if err := os.Mkdir(*dir, 0700); err != nil && !os.IsExist(err) {
		fmt.Fprintf(os.Stderr, "could not create directory %s: %s", *dir, err)
		os.Exit(2)
	}
	configFilePath := filepath.Join(*dir, "config.pb")
	if _, err := os.Stat(configFilePath); !os.IsNotExist(err) {
		fmt.Fprintf(os.Stderr, "file already exists: %s\n", configFilePath)
		os.Exit(2)
	}
	if err := ioutil.WriteFile(configFilePath, secretConfigBytes, 0600); err != nil {
		// TODO: "WriteFileSync" -- issue fsync after write
		fmt.Fprintf(os.Stderr, "error writing file %s: %s\n", configFilePath, err)
		os.Exit(2)
	}
	profileFilePath := filepath.Join(*dir, "profile.pb")
	if err := ioutil.WriteFile(profileFilePath, publicProfileBytes, 0600); err != nil {
		// TODO: "WriteFileSync" -- issue fsync after write
		fmt.Fprintf(os.Stderr, "error writing file %s: %s\n", profileFilePath, err)
		os.Exit(2)
	}

	fmt.Printf("You may use the following command to link this account with your dename profile:.\n"+
		"dnmgr set '%s' 1984 < %s\n", *dename, profileFilePath)

	// TODO: use TOR
	fmt.Printf("Registering with the server...\n")
	plainConn, err := net.Dial("tcp", net.JoinHostPort(*serverAddress, fmt.Sprint(*serverPort)))
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to server at %s%s: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	conn, _, err := transport.Handshake(plainConn,
		(*[32]byte)(&publicProfile.UserIDAtServer),
		(*[32]byte)(&secretConfig.TransportSecretKeyForServer),
		&serverTransportPubkey, proto.SERVER_MESSAGE_SIZE)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to server at %s%s: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	if err := client.CreateAccount(conn, make([]byte, proto.SERVER_MESSAGE_SIZE),
		make([]byte, proto.SERVER_MESSAGE_SIZE)); err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to serverat %s:%d: %s\n",
			*serverAddress, *serverPort, err)
		os.Exit(1)
	}
	fmt.Printf("Account initialization done.\n")
}
<|endoftext|>


<commit_before>package hamt32

import (
	"log"

	"github.com/lleo/go-hamt-key"
)

type HamtFunctional struct {
	root     tableI
	nentries uint
	grade    bool
	compinit bool
}

func NewFunctional(opt int) *HamtFunctional {
	var h = new(HamtFunctional)

	switch opt {
	case HybridTables:
		h.grade = true
		h.compinit = true
	case CompTablesOnly:
		h.grade = false
		h.compinit = true
	case FullTablesOnly:
		fallthrough
	default:
		h.grade = false
		h.compinit = false
	}

	return h
}

func (h *HamtFunctional) IsEmpty() bool {
	return h.root == nil
	//return h.nentries == 0
}

func (h *HamtFunctional) Nentries() uint {
	return h.nentries
}

// persist() is ONLY called on a fresh copy of the current Hamt.
// Hence, modifying it is allowed.
func (nh *HamtFunctional) persist(oldTable, newTable tableI, path tableStack) {
	if path.isEmpty() {
		nh.root = newTable
		return
	}

	var depth = uint(path.len())
	var parentDepth = depth - 1

	var parentIdx = oldTable.Hash30().Index(parentDepth)

	var oldParent = path.pop()
	var newParent tableI = oldParent.copy()

	if newTable == nil {
		newParent.remove(parentIdx)
	} else {
		newParent.replace(parentIdx, newTable)
	}

	nh.persist(oldParent, newParent, path) //recurses at most MaxDepth-1 times

	return
}

func (h *HamtFunctional) find(k key.Key) (tableStack, leafI, uint) {
	if h.IsEmpty() {
		return nil, nil, 0
	}

	var h30 = k.Hash30()
	var curTable = h.root

	var path = newTableStack()
	var leaf leafI
	var idx uint

	var depth uint
DepthIter:
	for depth = 0; depth <= maxDepth; depth++ {
		path.push(curTable)
		idx = h30.Index(depth)

		var curNode = curTable.get(idx)
		switch n := curNode.(type) {
		case nil:
			leaf = nil
			break DepthIter
		case leafI:
			leaf = n
			break DepthIter
		case tableI:
			if depth == maxDepth {
				log.Panicf("SHOULD NOT BE REACHED; depth,%d == maxDepth,%d & tableI entry found; %s", depth, maxDepth, n)
			}
			curTable = n
			// exit switch then loop for
		default:
			log.Panicf("SHOULD NOT BE REACHED: depth=%d; curNode unknown type=%T;", depth, curNode)
		}
	}

	return path, leaf, idx
}

//func (h *HamtTransient) Get(k key.Key) (interface{}, bool) {
//	var _, leaf, _ = h.find(k)
//
//	if leaf == nil {
//		return nil, false
//	}
//
//	return leaf.get(k)
//}

func (h *HamtFunctional) Get(k key.Key) (interface{}, bool) {
	if h.IsEmpty() {
		return nil, false
	}

	var val interface{}
	var found bool

	var h30 = k.Hash30()

	var curTable = h.root //ISA tableI

	for depth := uint(0); depth <= maxDepth; depth++ {
		var idx = h30.Index(depth)
		var curNode = curTable.get(idx) //nodeI

		if curNode == nil {
			return nil, false
		}

		if leaf, isLeaf := curNode.(leafI); isLeaf {
			val, found = leaf.get(k)
			return val, found
		}

		if depth == maxDepth {
			panic("SHOULD NOT HAPPEN")
		}
		curTable = curNode.(tableI)
	}

	panic("SHOULD NEVER BE REACHED")
}

func (h *HamtFunctional) createRootTable(leaf leafI) tableI {
	if h.compinit {
		return createRootCompressedTable(leaf)
	}
	return createRootFullTable(leaf)
}

func (h *HamtFunctional) createTable(depth uint, leaf1 leafI, leaf2 *flatLeaf) tableI {
	if h.compinit {
		return createCompressedTable(depth, leaf1, leaf2)
	}
	return createFullTable(depth, leaf1, leaf2)
}

func (h *HamtFunctional) Put(k key.Key, v interface{}) (Hamt, bool) {
	var nh = new(HamtFunctional)
	*nh = *h

	if nh.IsEmpty() {
		nh.root = nh.createRootTable(newFlatLeaf(k, v))
		nh.nentries++
		return nh, true
	}

	var path, leaf, idx = nh.find(k)

	var curTable = path.pop()
	var depth = uint(path.len())
	var added bool

	var newTable tableI
	if leaf == nil {
		if nh.grade && (curTable.nentries()+1) == upgradeThreshold {
			newTable = upgradeToFullTable(curTable.Hash30(), curTable.entries())
		} else {
			newTable = curTable.copy()
			newTable.insert(idx, newFlatLeaf(k, v))
		}
		added = true
	} else {
		newTable = curTable.copy()
		if leaf.Hash30() == k.Hash30() {
			var newLeaf leafI
			newLeaf, added = leaf.put(k, v)
			newTable.replace(idx, newLeaf)
		} else {
			var tmpTable = nh.createTable(depth+1, leaf, newFlatLeaf(k, v))
			newTable.replace(idx, tmpTable)
			added = true
		}
	}

	if added {
		nh.nentries++
	}

	nh.persist(curTable, newTable, path)

	return nh, added
}

func (h *HamtFunctional) Del(k key.Key) (Hamt, interface{}, bool) {
	if h.IsEmpty() {
		return h, nil, false
	}

	var path, leaf, idx = h.find(k)

	var curTable = path.pop()

	var val interface{}
	var deleted bool

	if leaf == nil {
		return h, nil, false
	}

	var newLeaf leafI
	newLeaf, val, deleted = leaf.del(k)

	if !deleted {
		return h, nil, false
	}

	var newTable tableI = curTable.copy()
	if newLeaf != nil { //leaf was a CollisionLeaf
		newTable.replace(idx, newLeaf)
	} else { //leaf was a FlatLeaf
		newTable.remove(idx)

		// Side-Effects of removing a KeyVal from the table
		switch {
		case newTable.nentries() == 0:
			newTable = nil
		case h.grade && newTable.nentries() == downgradeThreshold:
			newTable = downgradeToCompressedTable(
				newTable.Hash30(), newTable.entries())
		}
	}

	var nh = new(HamtFunctional)
	*nh = *h

	nh.nentries--

	nh.persist(curTable, newTable, path)

	return nh, val, deleted
}
<commit_msg>added ToFunctional, ToTransient, String and LongString methods<commit_after>package hamt32

import (
	"fmt"
	"log"

	"github.com/lleo/go-hamt-key"
)

type HamtFunctional struct {
	root     tableI
	nentries uint
	grade    bool
	compinit bool
}

func NewFunctional(opt int) *HamtFunctional {
	var h = new(HamtFunctional)

	switch opt {
	case HybridTables:
		h.grade = true
		h.compinit = true
	case CompTablesOnly:
		h.grade = false
		h.compinit = true
	case FullTablesOnly:
		fallthrough
	default:
		h.grade = false
		h.compinit = false
	}

	return h
}

func (h *HamtFunctional) IsEmpty() bool {
	return h.root == nil
	//return h.nentries == 0
}

func (h *HamtFunctional) Nentries() uint {
	return h.nentries
}

func (h *HamtFunctional) ToFunctional() Hamt {
	return h
}

func (h *HamtFunctional) ToTransient() Hamt {
	return &HamtTransient{
		root:     h.root,
		nentries: h.nentries,
		grade:    h.grade,
		compinit: h.compinit,
	}
}

// persist() is ONLY called on a fresh copy of the current Hamt.
// Hence, modifying it is allowed.
func (nh *HamtFunctional) persist(oldTable, newTable tableI, path tableStack) {
	if path.isEmpty() {
		nh.root = newTable
		return
	}

	var depth = uint(path.len())
	var parentDepth = depth - 1

	var parentIdx = oldTable.Hash30().Index(parentDepth)

	var oldParent = path.pop()
	var newParent tableI = oldParent.copy()

	if newTable == nil {
		newParent.remove(parentIdx)
	} else {
		newParent.replace(parentIdx, newTable)
	}

	nh.persist(oldParent, newParent, path) //recurses at most MaxDepth-1 times

	return
}

func (h *HamtFunctional) find(k key.Key) (tableStack, leafI, uint) {
	if h.IsEmpty() {
		return nil, nil, 0
	}

	var h30 = k.Hash30()
	var curTable = h.root

	var path = newTableStack()
	var leaf leafI
	var idx uint

	var depth uint
DepthIter:
	for depth = 0; depth <= maxDepth; depth++ {
		path.push(curTable)
		idx = h30.Index(depth)

		var curNode = curTable.get(idx)
		switch n := curNode.(type) {
		case nil:
			leaf = nil
			break DepthIter
		case leafI:
			leaf = n
			break DepthIter
		case tableI:
			if depth == maxDepth {
				log.Panicf("SHOULD NOT BE REACHED; depth,%d == maxDepth,%d & tableI entry found; %s", depth, maxDepth, n)
			}
			curTable = n
			// exit switch then loop for
		default:
			log.Panicf("SHOULD NOT BE REACHED: depth=%d; curNode unknown type=%T;", depth, curNode)
		}
	}

	return path, leaf, idx
}

//func (h *HamtTransient) Get(k key.Key) (interface{}, bool) {
//	var _, leaf, _ = h.find(k)
//
//	if leaf == nil {
//		return nil, false
//	}
//
//	return leaf.get(k)
//}

func (h *HamtFunctional) Get(k key.Key) (interface{}, bool) {
	if h.IsEmpty() {
		return nil, false
	}

	var val interface{}
	var found bool

	var h30 = k.Hash30()

	var curTable = h.root //ISA tableI

	for depth := uint(0); depth <= maxDepth; depth++ {
		var idx = h30.Index(depth)
		var curNode = curTable.get(idx) //nodeI

		if curNode == nil {
			return nil, false
		}

		if leaf, isLeaf := curNode.(leafI); isLeaf {
			val, found = leaf.get(k)
			return val, found
		}

		if depth == maxDepth {
			panic("SHOULD NOT HAPPEN")
		}
		curTable = curNode.(tableI)
	}

	panic("SHOULD NEVER BE REACHED")
}

func (h *HamtFunctional) createRootTable(leaf leafI) tableI {
	if h.compinit {
		return createRootCompressedTable(leaf)
	}
	return createRootFullTable(leaf)
}

func (h *HamtFunctional) createTable(depth uint, leaf1 leafI, leaf2 *flatLeaf) tableI {
	if h.compinit {
		return createCompressedTable(depth, leaf1, leaf2)
	}
	return createFullTable(depth, leaf1, leaf2)
}

func (h *HamtFunctional) Put(k key.Key, v interface{}) (Hamt, bool) {
	var nh = new(HamtFunctional)
	*nh = *h

	if nh.IsEmpty() {
		nh.root = nh.createRootTable(newFlatLeaf(k, v))
		nh.nentries++
		return nh, true
	}

	var path, leaf, idx = nh.find(k)

	var curTable = path.pop()
	var depth = uint(path.len())
	var added bool

	var newTable tableI
	if leaf == nil {
		if nh.grade && (curTable.nentries()+1) == upgradeThreshold {
			newTable = upgradeToFullTable(curTable.Hash30(), curTable.entries())
		} else {
			newTable = curTable.copy()
			newTable.insert(idx, newFlatLeaf(k, v))
		}
		added = true
	} else {
		newTable = curTable.copy()
		if leaf.Hash30() == k.Hash30() {
			var newLeaf leafI
			newLeaf, added = leaf.put(k, v)
			newTable.replace(idx, newLeaf)
		} else {
			var tmpTable = nh.createTable(depth+1, leaf, newFlatLeaf(k, v))
			newTable.replace(idx, tmpTable)
			added = true
		}
	}

	if added {
		nh.nentries++
	}

	nh.persist(curTable, newTable, path)

	return nh, added
}

func (h *HamtFunctional) Del(k key.Key) (Hamt, interface{}, bool) {
	if h.IsEmpty() {
		return h, nil, false
	}

	var path, leaf, idx = h.find(k)

	var curTable = path.pop()

	var val interface{}
	var deleted bool

	if leaf == nil {
		return h, nil, false
	}

	var newLeaf leafI
	newLeaf, val, deleted = leaf.del(k)

	if !deleted {
		return h, nil, false
	}

	var newTable tableI = curTable.copy()
	if newLeaf != nil { //leaf was a CollisionLeaf
		newTable.replace(idx, newLeaf)
	} else { //leaf was a FlatLeaf
		newTable.remove(idx)

		// Side-Effects of removing a KeyVal from the table
		switch {
		case newTable.nentries() == 0:
			newTable = nil
		case h.grade && newTable.nentries() == downgradeThreshold:
			newTable = downgradeToCompressedTable(
				newTable.Hash30(), newTable.entries())
		}
	}

	var nh = new(HamtFunctional)
	*nh = *h

	nh.nentries--

	nh.persist(curTable, newTable, path)

	return nh, val, deleted
}

// String returns a string representation of the Hamt string.
func (h *HamtFunctional) String() string {
	return fmt.Sprintf(
		"HamtFunctional{ nentries: %d, root: %s }",
		h.nentries,
		h.root.LongString("", 0),
	)
}

// LongString returns a complete listing of the entire Hamt data structure.
func (h *HamtFunctional) LongString(indent string) string {
	var str string
	if h.root != nil {
		str = indent +
			fmt.Sprintf("HamtFunctional{ nentries: %d, root:\n", h.nentries)
		str += indent + h.root.LongString(indent, 0)
		str += indent + "} //HamtFunctional"
	} else {
		str = indent +
			fmt.Sprintf("HamtFunctional{ nentries: %d, root: nil }", h.nentries)
	}
	return str
}
<|endoftext|>


<commit_before>package handler

import (
	"net/http"

	"github.com/google/go-github/github"
	"github.com/mattbostock/timeoff/model"
	"golang.org/x/oauth2"
)

func GithubOauthCallback(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Query().Get("code")
	state := r.URL.Query().Get("state")

	stateCookie, _ := r.Cookie(oauthStateCookieName)

	if state != stateCookie.Value {
		log.Errorln("GitHub state mismatch during Oauth callback")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:   oauthStateCookieName,
		MaxAge: -1,
	})

	t, err := oauthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Errorf("GitHub Oauth exchange failed: %s", err)
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	httpClient := oauthConfig.Client(oauth2.NoContext, t)
	githubClient := github.NewClient(httpClient)

	// set this explicitly as we'll override it in tests
	githubClient.BaseURL = githubAPIBaseURL

	user, _, err := githubClient.Users.Get("")

	if err != nil {
		log.Infoln("Authentication failed")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	u := &model.User{
		Name:     *user.Name,
		GitHubID: uint64(*user.ID),
	}

	emails, _, err := githubClient.Users.ListEmails(nil)
	if err != nil {
		log.Errorln(err)
	}

	for _, e := range emails {
		if *e.Primary && *e.Verified {
			u.Email = *e.Email
			break
		}
	}

	u.UpdateOrCreate()

	err = sessionManager.SetCookie(w, uint64(u.ID))
	if err != nil {
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect)
}
<commit_msg>Add comment explaining empty string<commit_after>package handler

import (
	"net/http"

	"github.com/google/go-github/github"
	"github.com/mattbostock/timeoff/model"
	"golang.org/x/oauth2"
)

func GithubOauthCallback(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Query().Get("code")
	state := r.URL.Query().Get("state")

	stateCookie, _ := r.Cookie(oauthStateCookieName)

	if state != stateCookie.Value {
		log.Errorln("GitHub state mismatch during Oauth callback")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:   oauthStateCookieName,
		MaxAge: -1,
	})

	t, err := oauthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Errorf("GitHub Oauth exchange failed: %s", err)
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	httpClient := oauthConfig.Client(oauth2.NoContext, t)
	githubClient := github.NewClient(httpClient)

	// set this explicitly as we'll override it in tests
	githubClient.BaseURL = githubAPIBaseURL

	// Get authenticated user based on Oauth token provided in HTTP client config
	// An empty string means get the authenticated user
	user, _, err := githubClient.Users.Get("")

	if err != nil {
		log.Infoln("Authentication failed")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	u := &model.User{
		Name:     *user.Name,
		GitHubID: uint64(*user.ID),
	}

	emails, _, err := githubClient.Users.ListEmails(nil)
	if err != nil {
		log.Errorln(err)
	}

	for _, e := range emails {
		if *e.Primary && *e.Verified {
			u.Email = *e.Email
			break
		}
	}

	u.UpdateOrCreate()

	err = sessionManager.SetCookie(w, uint64(u.ID))
	if err != nil {
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect)
}
<|endoftext|>


<commit_before>package jsonschema

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"sort"
	"strings"

	jptr "github.com/qri-io/jsonpointer"
)

// Must turns a JSON string into a *Schema, panicing if parsing fails.
// Useful for declaring Schemas in Go code.
func Must(jsonString string) *Schema {
	s := &Schema{}
	if err := s.UnmarshalJSON([]byte(jsonString)); err != nil {
		panic(err)
	}
	return s
}

type schemaType int

const (
	schemaTypeObject schemaType = iota
	schemaTypeFalse
	schemaTypeTrue
)

// Schema is the top-level structure defining a json schema
type Schema struct {
	schemaType    schemaType
	docPath       string
	hasRegistered bool

	id string

	extraDefinitions map[string]json.RawMessage
	keywords         map[string]Keyword
	orderedkeywords  []string
}

// NewSchema allocates a new Schema Keyword/Validator
func NewSchema() Keyword {
	return &Schema{}
}

// HasKeyword is a utility function for checking if the given schema
// has an instance of the required keyword
func (s *Schema) HasKeyword(key string) bool {
	_, ok := s.keywords[key]
	return ok
}

// Register implements the Keyword interface for Schema
func (s *Schema) Register(uri string, registry *SchemaRegistry) {
	schemaDebug("[Schema] Register")
	if s.hasRegistered {
		return
	}
	s.hasRegistered = true
	registry.RegisterLocal(s)

	// load default keyset if no other is present
	if !IsRegistryLoaded() {
		LoadDraft2019_09()
	}

	address := s.id
	if uri != "" && address != "" {
		address, _ = SafeResolveURL(uri, address)
	}
	if s.docPath == "" && address != "" && address[0] != '#' {
		docURI := ""
		if u, err := url.Parse(address); err != nil {
			docURI, _ = SafeResolveURL("https://qri.io", address)
		} else {
			docURI = u.String()
		}
		s.docPath = docURI
		GetSchemaRegistry().Register(s)
		uri = docURI
	}

	for _, keyword := range s.keywords {
		keyword.Register(uri, registry)
	}
}

// Resolve implements the Keyword interface for Schema
func (s *Schema) Resolve(pointer jptr.Pointer, uri string) *Schema {
	if pointer.IsEmpty() {
		if s.docPath != "" {
			s.docPath, _ = SafeResolveURL(uri, s.docPath)
		} else {
			s.docPath = uri
		}
		return s
	}

	current := pointer.Head()

	if s.id != "" {
		if u, err := url.Parse(s.id); err == nil {
			if u.IsAbs() {
				uri = s.id
			} else {
				uri, _ = SafeResolveURL(uri, s.id)
			}
		}
	}

	keyword := s.keywords[*current]
	var keywordSchema *Schema
	if keyword != nil {
		keywordSchema = keyword.Resolve(pointer.Tail(), uri)
	}

	if keywordSchema != nil {
		return keywordSchema
	}

	found, err := pointer.Eval(s.extraDefinitions)
	if err != nil {
		return nil
	}
	if found == nil {
		return nil
	}

	if foundSchema, ok := found.(*Schema); ok {
		return foundSchema
	}

	return nil
}

// JSONProp implements the JSONPather for Schema
func (s Schema) JSONProp(name string) interface{} {
	if keyword, ok := s.keywords[name]; ok {
		return keyword
	}
	return s.extraDefinitions[name]
}

// JSONChildren implements the JSONContainer interface for Schema
func (s Schema) JSONChildren() map[string]JSONPather {
	ch := map[string]JSONPather{}

	if s.keywords != nil {
		for key, val := range s.keywords {
			if jp, ok := val.(JSONPather); ok {
				ch[key] = jp
			}
		}
	}

	return ch
}

// _schema is an internal struct for encoding & decoding purposes
type _schema struct {
	ID string `json:"$id,omitempty"`
}

// UnmarshalJSON implements the json.Unmarshaler interface for Schema
func (s *Schema) UnmarshalJSON(data []byte) error {
	var b bool
	if err := json.Unmarshal(data, &b); err == nil {
		if b {
			// boolean true Always passes validation, as if the empty schema {}
			*s = Schema{schemaType: schemaTypeTrue}
			return nil
		}
		// boolean false Always fails validation, as if the schema { "not":{} }
		*s = Schema{schemaType: schemaTypeFalse}
		return nil
	}

	if !IsRegistryLoaded() {
		LoadDraft2019_09()
	}

	_s := _schema{}
	if err := json.Unmarshal(data, &_s); err != nil {
		return err
	}

	sch := &Schema{
		id:       _s.ID,
		keywords: map[string]Keyword{},
	}

	valprops := map[string]json.RawMessage{}
	if err := json.Unmarshal(data, &valprops); err != nil {
		return err
	}

	for prop, rawmsg := range valprops {
		var keyword Keyword
		if IsRegisteredKeyword(prop) {
			keyword = GetKeyword(prop)
		} else if IsNotSupportedKeyword(prop) {
			schemaDebug(fmt.Sprintf("[Schema] WARN: '%s' is not supported and will be ignored\n", prop))
			continue
		} else {
			if sch.extraDefinitions == nil {
				sch.extraDefinitions = map[string]json.RawMessage{}
			}
			sch.extraDefinitions[prop] = rawmsg
			continue
		}
		if _, ok := keyword.(*Void); !ok {
			if err := json.Unmarshal(rawmsg, keyword); err != nil {
				return fmt.Errorf("error unmarshaling %s from json: %s", prop, err.Error())
			}
		}
		sch.keywords[prop] = keyword
	}

	// ensures proper and stable keyword validation order
	keyOrders := make([]_keyOrder, len(sch.keywords))
	i := 0
	for k := range sch.keywords {
		keyOrders[i] = _keyOrder{
			Key:   k,
			Order: GetKeywordOrder(k),
		}
		i++
	}
	sort.SliceStable(keyOrders, func(i, j int) bool {
		if keyOrders[i].Order == keyOrders[j].Order {
			return GetKeywordInsertOrder(keyOrders[i].Key) < GetKeywordInsertOrder(keyOrders[j].Key)
		}
		return keyOrders[i].Order < keyOrders[j].Order
	})
	orderedKeys := make([]string, len(sch.keywords))
	i = 0
	for _, keyOrder := range keyOrders {
		orderedKeys[i] = keyOrder.Key
		i++
	}
	sch.orderedkeywords = orderedKeys

	*s = Schema(*sch)
	return nil
}

// _keyOrder is an internal struct assigning evaluation order of keywords
type _keyOrder struct {
	Key   string
	Order int
}

// Validate initiates a fresh validation state and triggers the evaluation
func (s *Schema) Validate(ctx context.Context, data interface{}) *ValidationState {
	currentState := NewValidationState(s)
	s.ValidateKeyword(ctx, currentState, data)
	return currentState
}

// ValidateKeyword uses the schema to check an instance, collecting validation
// errors in a slice
func (s *Schema) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
	schemaDebug("[Schema] Validating")
	if s == nil {
		currentState.AddError(data, fmt.Sprintf("schema is nil"))
		return
	}
	if s.schemaType == schemaTypeTrue {
		return
	}
	if s.schemaType == schemaTypeFalse {
		currentState.AddError(data, fmt.Sprintf("schema is always false"))
		return
	}

	s.Register("", currentState.LocalRegistry)
	currentState.LocalRegistry.RegisterLocal(s)

	currentState.Local = s

	refKeyword := s.keywords["$ref"]

	if refKeyword == nil {
		if currentState.BaseURI == "" {
			currentState.BaseURI = s.docPath
		} else if s.docPath != "" {
			if u, err := url.Parse(s.docPath); err == nil {
				if u.IsAbs() {
					currentState.BaseURI = s.docPath
				} else {
					currentState.BaseURI, _ = SafeResolveURL(currentState.BaseURI, s.docPath)
				}
			}
		}
	}

	if currentState.BaseURI != "" && strings.HasSuffix(currentState.BaseURI, "#") {
		currentState.BaseURI = strings.TrimRight(currentState.BaseURI, "#")
	}

	// TODO(arqu): only on versions bellow draft2019_09
	// if refKeyword != nil {
	// 	refKeyword.ValidateKeyword(currentState, errs)
	// 	return
	// }

	s.validateSchemakeywords(ctx, currentState, data)
}

// validateSchemakeywords triggers validation of sub schemas and keywords
func (s *Schema) validateSchemakeywords(ctx context.Context, currentState *ValidationState, data interface{}) {
	if s.keywords != nil {
		for _, keyword := range s.orderedkeywords {
			s.keywords[keyword].ValidateKeyword(ctx, currentState, data)
		}
	}
}

// ValidateBytes performs schema validation against a slice of json
// byte data
func (s *Schema) ValidateBytes(ctx context.Context, data []byte) ([]KeyError, error) {
	var doc interface{}
	if err := json.Unmarshal(data, &doc); err != nil {
		return nil, fmt.Errorf("error parsing JSON bytes: %s", err.Error())
	}
	vs := s.Validate(ctx, doc)
	return *vs.Errs, nil
}

// TopLevelType returns a string representing the schema's top-level type.
func (s *Schema) TopLevelType() string {
	if t, ok := s.keywords["type"].(*Type); ok {
		return t.String()
	}
	return "unknown"
}

// MarshalJSON implements the json.Marshaler interface for Schema
func (s Schema) MarshalJSON() ([]byte, error) {
	switch s.schemaType {
	case schemaTypeFalse:
		return []byte("false"), nil
	case schemaTypeTrue:
		return []byte("true"), nil
	default:
		obj := map[string]interface{}{}

		for k, v := range s.keywords {
			obj[k] = v
		}
		for k, v := range s.extraDefinitions {
			obj[k] = v
		}
		return json.Marshal(obj)
	}
}
<commit_msg>refactor(jsonschema): change error returned from json.Unmarshal to wrapped error (#83) (#85)<commit_after>package jsonschema

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"sort"
	"strings"

	jptr "github.com/qri-io/jsonpointer"
)

// Must turns a JSON string into a *Schema, panicing if parsing fails.
// Useful for declaring Schemas in Go code.
func Must(jsonString string) *Schema {
	s := &Schema{}
	if err := s.UnmarshalJSON([]byte(jsonString)); err != nil {
		panic(err)
	}
	return s
}

type schemaType int

const (
	schemaTypeObject schemaType = iota
	schemaTypeFalse
	schemaTypeTrue
)

// Schema is the top-level structure defining a json schema
type Schema struct {
	schemaType    schemaType
	docPath       string
	hasRegistered bool

	id string

	extraDefinitions map[string]json.RawMessage
	keywords         map[string]Keyword
	orderedkeywords  []string
}

// NewSchema allocates a new Schema Keyword/Validator
func NewSchema() Keyword {
	return &Schema{}
}

// HasKeyword is a utility function for checking if the given schema
// has an instance of the required keyword
func (s *Schema) HasKeyword(key string) bool {
	_, ok := s.keywords[key]
	return ok
}

// Register implements the Keyword interface for Schema
func (s *Schema) Register(uri string, registry *SchemaRegistry) {
	schemaDebug("[Schema] Register")
	if s.hasRegistered {
		return
	}
	s.hasRegistered = true
	registry.RegisterLocal(s)

	// load default keyset if no other is present
	if !IsRegistryLoaded() {
		LoadDraft2019_09()
	}

	address := s.id
	if uri != "" && address != "" {
		address, _ = SafeResolveURL(uri, address)
	}
	if s.docPath == "" && address != "" && address[0] != '#' {
		docURI := ""
		if u, err := url.Parse(address); err != nil {
			docURI, _ = SafeResolveURL("https://qri.io", address)
		} else {
			docURI = u.String()
		}
		s.docPath = docURI
		GetSchemaRegistry().Register(s)
		uri = docURI
	}

	for _, keyword := range s.keywords {
		keyword.Register(uri, registry)
	}
}

// Resolve implements the Keyword interface for Schema
func (s *Schema) Resolve(pointer jptr.Pointer, uri string) *Schema {
	if pointer.IsEmpty() {
		if s.docPath != "" {
			s.docPath, _ = SafeResolveURL(uri, s.docPath)
		} else {
			s.docPath = uri
		}
		return s
	}

	current := pointer.Head()

	if s.id != "" {
		if u, err := url.Parse(s.id); err == nil {
			if u.IsAbs() {
				uri = s.id
			} else {
				uri, _ = SafeResolveURL(uri, s.id)
			}
		}
	}

	keyword := s.keywords[*current]
	var keywordSchema *Schema
	if keyword != nil {
		keywordSchema = keyword.Resolve(pointer.Tail(), uri)
	}

	if keywordSchema != nil {
		return keywordSchema
	}

	found, err := pointer.Eval(s.extraDefinitions)
	if err != nil {
		return nil
	}
	if found == nil {
		return nil
	}

	if foundSchema, ok := found.(*Schema); ok {
		return foundSchema
	}

	return nil
}

// JSONProp implements the JSONPather for Schema
func (s Schema) JSONProp(name string) interface{} {
	if keyword, ok := s.keywords[name]; ok {
		return keyword
	}
	return s.extraDefinitions[name]
}

// JSONChildren implements the JSONContainer interface for Schema
func (s Schema) JSONChildren() map[string]JSONPather {
	ch := map[string]JSONPather{}

	if s.keywords != nil {
		for key, val := range s.keywords {
			if jp, ok := val.(JSONPather); ok {
				ch[key] = jp
			}
		}
	}

	return ch
}

// _schema is an internal struct for encoding & decoding purposes
type _schema struct {
	ID string `json:"$id,omitempty"`
}

// UnmarshalJSON implements the json.Unmarshaler interface for Schema
func (s *Schema) UnmarshalJSON(data []byte) error {
	var b bool
	if err := json.Unmarshal(data, &b); err == nil {
		if b {
			// boolean true Always passes validation, as if the empty schema {}
			*s = Schema{schemaType: schemaTypeTrue}
			return nil
		}
		// boolean false Always fails validation, as if the schema { "not":{} }
		*s = Schema{schemaType: schemaTypeFalse}
		return nil
	}

	if !IsRegistryLoaded() {
		LoadDraft2019_09()
	}

	_s := _schema{}
	if err := json.Unmarshal(data, &_s); err != nil {
		return err
	}

	sch := &Schema{
		id:       _s.ID,
		keywords: map[string]Keyword{},
	}

	valprops := map[string]json.RawMessage{}
	if err := json.Unmarshal(data, &valprops); err != nil {
		return err
	}

	for prop, rawmsg := range valprops {
		var keyword Keyword
		if IsRegisteredKeyword(prop) {
			keyword = GetKeyword(prop)
		} else if IsNotSupportedKeyword(prop) {
			schemaDebug(fmt.Sprintf("[Schema] WARN: '%s' is not supported and will be ignored\n", prop))
			continue
		} else {
			if sch.extraDefinitions == nil {
				sch.extraDefinitions = map[string]json.RawMessage{}
			}
			sch.extraDefinitions[prop] = rawmsg
			continue
		}
		if _, ok := keyword.(*Void); !ok {
			if err := json.Unmarshal(rawmsg, keyword); err != nil {
				return fmt.Errorf("error unmarshaling %s from json: %s", prop, err.Error())
			}
		}
		sch.keywords[prop] = keyword
	}

	// ensures proper and stable keyword validation order
	keyOrders := make([]_keyOrder, len(sch.keywords))
	i := 0
	for k := range sch.keywords {
		keyOrders[i] = _keyOrder{
			Key:   k,
			Order: GetKeywordOrder(k),
		}
		i++
	}
	sort.SliceStable(keyOrders, func(i, j int) bool {
		if keyOrders[i].Order == keyOrders[j].Order {
			return GetKeywordInsertOrder(keyOrders[i].Key) < GetKeywordInsertOrder(keyOrders[j].Key)
		}
		return keyOrders[i].Order < keyOrders[j].Order
	})
	orderedKeys := make([]string, len(sch.keywords))
	i = 0
	for _, keyOrder := range keyOrders {
		orderedKeys[i] = keyOrder.Key
		i++
	}
	sch.orderedkeywords = orderedKeys

	*s = Schema(*sch)
	return nil
}

// _keyOrder is an internal struct assigning evaluation order of keywords
type _keyOrder struct {
	Key   string
	Order int
}

// Validate initiates a fresh validation state and triggers the evaluation
func (s *Schema) Validate(ctx context.Context, data interface{}) *ValidationState {
	currentState := NewValidationState(s)
	s.ValidateKeyword(ctx, currentState, data)
	return currentState
}

// ValidateKeyword uses the schema to check an instance, collecting validation
// errors in a slice
func (s *Schema) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
	schemaDebug("[Schema] Validating")
	if s == nil {
		currentState.AddError(data, fmt.Sprintf("schema is nil"))
		return
	}
	if s.schemaType == schemaTypeTrue {
		return
	}
	if s.schemaType == schemaTypeFalse {
		currentState.AddError(data, fmt.Sprintf("schema is always false"))
		return
	}

	s.Register("", currentState.LocalRegistry)
	currentState.LocalRegistry.RegisterLocal(s)

	currentState.Local = s

	refKeyword := s.keywords["$ref"]

	if refKeyword == nil {
		if currentState.BaseURI == "" {
			currentState.BaseURI = s.docPath
		} else if s.docPath != "" {
			if u, err := url.Parse(s.docPath); err == nil {
				if u.IsAbs() {
					currentState.BaseURI = s.docPath
				} else {
					currentState.BaseURI, _ = SafeResolveURL(currentState.BaseURI, s.docPath)
				}
			}
		}
	}

	if currentState.BaseURI != "" && strings.HasSuffix(currentState.BaseURI, "#") {
		currentState.BaseURI = strings.TrimRight(currentState.BaseURI, "#")
	}

	// TODO(arqu): only on versions bellow draft2019_09
	// if refKeyword != nil {
	// 	refKeyword.ValidateKeyword(currentState, errs)
	// 	return
	// }

	s.validateSchemakeywords(ctx, currentState, data)
}

// validateSchemakeywords triggers validation of sub schemas and keywords
func (s *Schema) validateSchemakeywords(ctx context.Context, currentState *ValidationState, data interface{}) {
	if s.keywords != nil {
		for _, keyword := range s.orderedkeywords {
			s.keywords[keyword].ValidateKeyword(ctx, currentState, data)
		}
	}
}

// ValidateBytes performs schema validation against a slice of json
// byte data
func (s *Schema) ValidateBytes(ctx context.Context, data []byte) ([]KeyError, error) {
	var doc interface{}
	if err := json.Unmarshal(data, &doc); err != nil {
		return nil, fmt.Errorf("error parsing JSON bytes: %w", err)
	}
	vs := s.Validate(ctx, doc)
	return *vs.Errs, nil
}

// TopLevelType returns a string representing the schema's top-level type.
func (s *Schema) TopLevelType() string {
	if t, ok := s.keywords["type"].(*Type); ok {
		return t.String()
	}
	return "unknown"
}

// MarshalJSON implements the json.Marshaler interface for Schema
func (s Schema) MarshalJSON() ([]byte, error) {
	switch s.schemaType {
	case schemaTypeFalse:
		return []byte("false"), nil
	case schemaTypeTrue:
		return []byte("true"), nil
	default:
		obj := map[string]interface{}{}

		for k, v := range s.keywords {
			obj[k] = v
		}
		for k, v := range s.extraDefinitions {
			obj[k] = v
		}
		return json.Marshal(obj)
	}
}
<|endoftext|>


<commit_before>// Package ssh implements a ssh client for go-git.
//
// The Connect() method is not allowed in ssh, use ConnectWithAuth() instead.
package ssh

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"

	"gopkg.in/src-d/go-git.v3/clients/common"
	"gopkg.in/src-d/go-git.v3/formats/pktline"

	"github.com/sourcegraph/go-vcsurl"
	"golang.org/x/crypto/ssh"
)

// New errors introduced by this package.
var (
	ErrInvalidAuthMethod      = errors.New("invalid ssh auth method")
	ErrAuthRequired           = errors.New("cannot connect: auth required")
	ErrNotConnected           = errors.New("not connected")
	ErrAlreadyConnected       = errors.New("already connected")
	ErrUploadPackAnswerFormat = errors.New("git-upload-pack bad answer format")
	ErrUnsupportedVCS         = errors.New("only git is supported")
	ErrUnsupportedRepo        = errors.New("only github.com is supported")
)

// GitUploadPackService holds the service information.
// The zero value is safe to use.
// TODO: remove NewGitUploadPackService().
type GitUploadPackService struct {
	connected bool
	vcs       *vcsurl.RepoInfo
	client    *ssh.Client
	auth      AuthMethod
}

// NewGitUploadPackService initialises a GitUploadPackService.
// TODO: remove this, as the struct is zero-value safe.
func NewGitUploadPackService() *GitUploadPackService {
	return &GitUploadPackService{}
}

// Connect cannot be used with SSH clients and always return
// ErrAuthRequired. Use ConnectWithAuth instead.
func (s *GitUploadPackService) Connect(ep common.Endpoint) (err error) {
	return ErrAuthRequired
}

// ConnectWithAuth connects to ep using SSH. Authentication is handled
// by auth.
func (s *GitUploadPackService) ConnectWithAuth(ep common.Endpoint, auth common.AuthMethod) (err error) {
	if s.connected {
		return ErrAlreadyConnected
	}

	s.vcs, err = vcsurl.Parse(string(ep))
	if err != nil {
		return err
	}

	url, err := vcsToURL(s.vcs)
	if err != nil {
		return
	}

	var ok bool
	s.auth, ok = auth.(AuthMethod)
	if !ok {
		return ErrInvalidAuthMethod
	}

	s.client, err = ssh.Dial("tcp", url.Host, s.auth.clientConfig())
	if err != nil {
		return err
	}

	s.connected = true
	return
}

func vcsToURL(vcs *vcsurl.RepoInfo) (u *url.URL, err error) {
	if vcs.VCS != vcsurl.Git {
		return nil, ErrUnsupportedVCS
	}
	if vcs.RepoHost != vcsurl.GitHub {
		return nil, ErrUnsupportedRepo
	}
	s := "ssh://git@" + string(vcs.RepoHost) + ":22/" + vcs.FullName
	u, err = url.Parse(s)
	return
}

// Info returns the GitUploadPackInfo of the repository.
// The client must be connected with the repository (using
// the ConnectWithAuth() method) before using this
// method.
func (s *GitUploadPackService) Info() (i *common.GitUploadPackInfo, err error) {
	if !s.connected {
		return nil, ErrNotConnected
	}

	session, err := s.client.NewSession()
	if err != nil {
		return nil, err
	}
	defer func() {
		// the session can be closed by the other endpoint,
		// therefore we must ignore a close error.
		_ = session.Close()
	}()

	out, err := session.Output("git-upload-pack " + s.vcs.FullName + ".git")
	if err != nil {
		return nil, err
	}

	i = common.NewGitUploadPackInfo()
	return i, i.Decode(pktline.NewDecoder(bytes.NewReader(out)))
}

// Disconnect the SSH client.
func (s *GitUploadPackService) Disconnect() (err error) {
	if !s.connected {
		return ErrNotConnected
	}
	s.connected = false
	return s.client.Close()
}

// Fetch retrieves the GitUploadPack form the repository.
// You must be connected to the repository before using this method
// (using the ConnectWithAuth() method).
// TODO: fetch should really reuse the info session instead of openning a new
// one
func (s *GitUploadPackService) Fetch(r *common.GitUploadPackRequest) (rc io.ReadCloser, err error) {
	if !s.connected {
		return nil, ErrNotConnected
	}

	session, err := s.client.NewSession()
	if err != nil {
		return nil, err
	}
	defer func() {
		// the session can be closed by the other endpoint,
		// therefore we must ignore a close error.
		_ = session.Close()
	}()

	si, err := session.StdinPipe()
	if err != nil {
		return nil, err
	}

	so, err := session.StdoutPipe()
	if err != nil {
		return nil, err
	}

	go func() {
		fmt.Fprintln(si, r.String())
		err = si.Close()
	}()

	err = session.Start("git-upload-pack " + s.vcs.FullName + ".git")
	if err != nil {
		return nil, err
	}
	// TODO: investigate this *ExitError type (command fails or
	// doesn't complete successfully), as it is happenning all
	// the time, but everything seems to work fine.
	err = session.Wait()
	if err != nil {
		if _, ok := err.(*ssh.ExitError); !ok {
			return nil, err
		}
	}

	// read until the header of the second answer
	soBuf := bufio.NewReader(so)
	token := "0000"
	for {
		var line string
		line, err = soBuf.ReadString('\n')
		if err == io.EOF {
			return nil, ErrUploadPackAnswerFormat
		}
		if line[0:len(token)] == token {
			break
		}
	}

	data, err := ioutil.ReadAll(soBuf)
	if err != nil {
		return nil, err
	}
	buf := bytes.NewBuffer(data)
	return ioutil.NopCloser(buf), nil
}
<commit_msg>Fix #62 (#66)<commit_after>// Package ssh implements a ssh client for go-git.
//
// The Connect() method is not allowed in ssh, use ConnectWithAuth() instead.
package ssh

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"

	"gopkg.in/src-d/go-git.v3/clients/common"
	"gopkg.in/src-d/go-git.v3/formats/pktline"

	"gopkg.in/sourcegraph/go-vcsurl.v1"
	"golang.org/x/crypto/ssh"
)

// New errors introduced by this package.
var (
	ErrInvalidAuthMethod      = errors.New("invalid ssh auth method")
	ErrAuthRequired           = errors.New("cannot connect: auth required")
	ErrNotConnected           = errors.New("not connected")
	ErrAlreadyConnected       = errors.New("already connected")
	ErrUploadPackAnswerFormat = errors.New("git-upload-pack bad answer format")
	ErrUnsupportedVCS         = errors.New("only git is supported")
	ErrUnsupportedRepo        = errors.New("only github.com is supported")
)

// GitUploadPackService holds the service information.
// The zero value is safe to use.
// TODO: remove NewGitUploadPackService().
type GitUploadPackService struct {
	connected bool
	vcs       *vcsurl.RepoInfo
	client    *ssh.Client
	auth      AuthMethod
}

// NewGitUploadPackService initialises a GitUploadPackService.
// TODO: remove this, as the struct is zero-value safe.
func NewGitUploadPackService() *GitUploadPackService {
	return &GitUploadPackService{}
}

// Connect cannot be used with SSH clients and always return
// ErrAuthRequired. Use ConnectWithAuth instead.
func (s *GitUploadPackService) Connect(ep common.Endpoint) (err error) {
	return ErrAuthRequired
}

// ConnectWithAuth connects to ep using SSH. Authentication is handled
// by auth.
func (s *GitUploadPackService) ConnectWithAuth(ep common.Endpoint, auth common.AuthMethod) (err error) {
	if s.connected {
		return ErrAlreadyConnected
	}

	s.vcs, err = vcsurl.Parse(string(ep))
	if err != nil {
		return err
	}

	url, err := vcsToURL(s.vcs)
	if err != nil {
		return
	}

	var ok bool
	s.auth, ok = auth.(AuthMethod)
	if !ok {
		return ErrInvalidAuthMethod
	}

	s.client, err = ssh.Dial("tcp", url.Host, s.auth.clientConfig())
	if err != nil {
		return err
	}

	s.connected = true
	return
}

func vcsToURL(vcs *vcsurl.RepoInfo) (u *url.URL, err error) {
	if vcs.VCS != vcsurl.Git {
		return nil, ErrUnsupportedVCS
	}
	if vcs.RepoHost != vcsurl.GitHub {
		return nil, ErrUnsupportedRepo
	}
	s := "ssh://git@" + string(vcs.RepoHost) + ":22/" + vcs.FullName
	u, err = url.Parse(s)
	return
}

// Info returns the GitUploadPackInfo of the repository.
// The client must be connected with the repository (using
// the ConnectWithAuth() method) before using this
// method.
func (s *GitUploadPackService) Info() (i *common.GitUploadPackInfo, err error) {
	if !s.connected {
		return nil, ErrNotConnected
	}

	session, err := s.client.NewSession()
	if err != nil {
		return nil, err
	}
	defer func() {
		// the session can be closed by the other endpoint,
		// therefore we must ignore a close error.
		_ = session.Close()
	}()

	out, err := session.Output("git-upload-pack " + s.vcs.FullName + ".git")
	if err != nil {
		return nil, err
	}

	i = common.NewGitUploadPackInfo()
	return i, i.Decode(pktline.NewDecoder(bytes.NewReader(out)))
}

// Disconnect the SSH client.
func (s *GitUploadPackService) Disconnect() (err error) {
	if !s.connected {
		return ErrNotConnected
	}
	s.connected = false
	return s.client.Close()
}

// Fetch retrieves the GitUploadPack form the repository.
// You must be connected to the repository before using this method
// (using the ConnectWithAuth() method).
// TODO: fetch should really reuse the info session instead of openning a new
// one
func (s *GitUploadPackService) Fetch(r *common.GitUploadPackRequest) (rc io.ReadCloser, err error) {
	if !s.connected {
		return nil, ErrNotConnected
	}

	session, err := s.client.NewSession()
	if err != nil {
		return nil, err
	}
	defer func() {
		// the session can be closed by the other endpoint,
		// therefore we must ignore a close error.
		_ = session.Close()
	}()

	si, err := session.StdinPipe()
	if err != nil {
		return nil, err
	}

	so, err := session.StdoutPipe()
	if err != nil {
		return nil, err
	}

	go func() {
		fmt.Fprintln(si, r.String())
		err = si.Close()
	}()

	err = session.Start("git-upload-pack " + s.vcs.FullName + ".git")
	if err != nil {
		return nil, err
	}
	// TODO: investigate this *ExitError type (command fails or
	// doesn't complete successfully), as it is happenning all
	// the time, but everything seems to work fine.
	err = session.Wait()
	if err != nil {
		if _, ok := err.(*ssh.ExitError); !ok {
			return nil, err
		}
	}

	// read until the header of the second answer
	soBuf := bufio.NewReader(so)
	token := "0000"
	for {
		var line string
		line, err = soBuf.ReadString('\n')
		if err == io.EOF {
			return nil, ErrUploadPackAnswerFormat
		}
		if line[0:len(token)] == token {
			break
		}
	}

	data, err := ioutil.ReadAll(soBuf)
	if err != nil {
		return nil, err
	}
	buf := bytes.NewBuffer(data)
	return ioutil.NopCloser(buf), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var client *http.Client
var successful_count int
var total_push int

func gatling(url string, object string, xHeaders string, ch chan<- bool, rtype string, otype string) {

	count := 0
	out := []byte(object)
	cheads := strings.Fields(xHeaders)
	request, _ := http.NewRequest(rtype, url, bytes.NewBuffer(out))
	if strings.ToLower(otype) != "xml" || strings.ToLower(otype) != "json" {
		log.Printf("[*] Invalid content type. Please specify json or xml")
		os.Exit(1)
	} else {
		request.Header.Set("Content-Type", "application/"+otype)
	}
	for _, v := range cheads {
		hs := strings.Split(v, ":")
		request.Header.Set(hs[0], hs[1])
	}
	response, yoo := client.Do(request)
	if yoo != nil {
		log.Fatalf("[*] Error making request.", yoo)
	}
	defer response.Body.Close()

	log.Printf("[*] Request complete! Finished Request No: #%v, Status: %v", count, response.StatusCode)

	total_push += 1

	count += 1

	if response.StatusCode == 200 || response.StatusCode == 201 {
		successful_count += 1
		ch <- true
	}
}

func main() {

	nCPU := runtime.NumCPU()
	runtime.GOMAXPROCS(nCPU)

	urlString := flag.String("url", "", "Url to stress test e.g. 'http://acme.com'")
	requestInt := flag.Int("rps", 0, "Number of requests to make simultaneously")
	requestObject := flag.String("object", "", "Custom object to post e.g. {'foo':'bar'}")
	objType := flag.String("objectType", "", "Type of object to post. e.g. 'xml' or 'json'")
	numRequests := flag.Int("numR", 0, "Total number of requests to make")
	reqType := flag.String("type", "", "HTTP request type you'd like to make. Currently supported type(s) 'POST'")
	heads := flag.String("headers", "", "HTTP headers to include when making request. Format should be for example 'Auth:SomeToken X-Header:Sugar'. \nHeaders should be separated by spaces")
	flag.Parse()

	// Customizing Transport to have larger connection pool
	defaultRoundTripper := http.DefaultTransport
	defaultTransportPointer, yoo := defaultRoundTripper.(*http.Transport)
	if !yoo {
		panic(fmt.Sprintf("defaultRoundTripper lied! Not an *http.Transport"))
	}

	defaultTransport := *defaultTransportPointer
	defaultTransport.MaxIdleConns = 1500
	defaultTransport.MaxIdleConnsPerHost = 1500

	client = &http.Client{Transport: &defaultTransport}

	start := time.Now()
	switch *objType {
	case "":
		flag.PrintDefaults()
	default:
		ch := make(chan bool)
		for i := 0; i < *requestInt; i++ {
			go gatling(*urlString, *requestObject, *heads, ch, *reqType, *objType)
		}

		for r := 0; r < *numRequests; r++ {
			<-ch
		}

		elapsedTime := time.Since(start)
		failed_count := total_push - successful_count
		log.Printf("[*] Total number of successful requests: %v", successful_count)
		log.Printf("[*] Total number of failed requests: %v", failed_count)
		log.Printf("[*] Total number of requests: %v", total_push)
		log.Printf("[*] Total time elapsed: %v", elapsedTime)
	}

}
<commit_msg>minor code cleanup<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var client *http.Client
var successCount, totalPush int

func gatling(url string, object string, xHeaders string, ch chan<- bool, rtype string, otype string) {

	count := 0
	out := []byte(object)
	cheads := strings.Fields(xHeaders)
	request, _ := http.NewRequest(rtype, url, bytes.NewBuffer(out))
	if strings.ToLower(otype) != "xml" || strings.ToLower(otype) != "json" {
		log.Printf("[*] Invalid content type. Please specify json or xml")
		os.Exit(1)
	} else {
		request.Header.Set("Content-Type", "application/"+otype)
	}
	for _, v := range cheads {
		hs := strings.Split(v, ":")
		request.Header.Set(hs[0], hs[1])
	}
	response, yoo := client.Do(request)
	if yoo != nil {
		log.Fatalf("[*] Error making request: %s", yoo)
	}
	defer response.Body.Close()

	log.Printf("[*] Request complete! Finished Request No: #%v, Status: %v", count, response.StatusCode)

	totalPush++

	count++

	if response.StatusCode == 200 || response.StatusCode == 201 {
		successCount++
		ch <- true
	}
}

func main() {

	nCPU := runtime.NumCPU()
	runtime.GOMAXPROCS(nCPU)

	urlString := flag.String("url", "", "Url to stress test e.g. 'http://acme.com'")
	requestInt := flag.Int("rps", 0, "Number of requests to make simultaneously")
	requestObject := flag.String("object", "", "Custom object to post e.g. {'foo':'bar'}")
	objType := flag.String("objectType", "", "Type of object to post. e.g. 'xml' or 'json'")
	numRequests := flag.Int("numR", 0, "Total number of requests to make")
	reqType := flag.String("type", "", "HTTP request type you'd like to make. Currently supported type(s) 'POST'")
	heads := flag.String("headers", "", "HTTP headers to include when making request. Format should be for example 'Auth:SomeToken X-Header:Sugar'. \nHeaders should be separated by spaces")
	flag.Parse()

	// Customizing Transport to have larger connection pool
	defaultRoundTripper := http.DefaultTransport
	defaultTransportPointer, yoo := defaultRoundTripper.(*http.Transport)
	if !yoo {
		panic(fmt.Sprintf("defaultRoundTripper lied! Not an *http.Transport"))
	}

	defaultTransport := *defaultTransportPointer
	defaultTransport.MaxIdleConns = 1500
	defaultTransport.MaxIdleConnsPerHost = 1500

	client = &http.Client{Transport: &defaultTransport}

	start := time.Now()
	switch *objType {
	case "":
		flag.PrintDefaults()
	default:
		ch := make(chan bool)
		for i := 0; i < *requestInt; i++ {
			go gatling(*urlString, *requestObject, *heads, ch, *reqType, *objType)
		}

		for r := 0; r < *numRequests; r++ {
			<-ch
		}

		elapsedTime := time.Since(start)
		failedCount := totalPush - successCount
		log.Printf("[*] Total number of successful requests: %v", successCount)
		log.Printf("[*] Total number of failed requests: %v", failedCount)
		log.Printf("[*] Total number of requests: %v", totalPush)
		log.Printf("[*] Total time elapsed: %v", elapsedTime)
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// 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 (
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/pkg/console"
	iampolicy "github.com/minio/pkg/iam/policy"
)

var adminUserSvcAcctInfoFlags = []cli.Flag{
	cli.BoolFlag{
		Name:  "policy",
		Usage: "print policy is JSON format",
	},
}

var adminUserSvcAcctInfoCmd = cli.Command{
	Name:         "info",
	Usage:        "display service account info",
	Action:       mainAdminUserSvcAcctInfo,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(adminUserSvcAcctInfoFlags, globalFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} ALIAS SERVICE-ACCOUNT

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Display information for service account 'J123C4ZXEQN8RK6ND35I'
     {{.Prompt}} {{.HelpName}} myminio/ J123C4ZXEQN8RK6ND35I
`,
}

// checkAdminUserSvcAcctInfoSyntax - validate all the passed arguments
func checkAdminUserSvcAcctInfoSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		cli.ShowCommandHelpAndExit(ctx, "info", 1)
	}
}

// mainAdminUserSvcAcctInfo is the handle for "mc admin user svcacct info" command.
func mainAdminUserSvcAcctInfo(ctx *cli.Context) error {
	checkAdminUserSvcAcctInfoSyntax(ctx)

	console.SetColor("SVCMessage", color.New(color.FgGreen))

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)
	svcAccount := args.Get(1)

	// Create a new MinIO Admin Client
	client, err := newAdminClient(aliasedURL)
	fatalIf(err, "Unable to initialize admin connection.")

	svcInfo, e := client.InfoServiceAccount(globalContext, svcAccount)
	fatalIf(probe.NewError(e).Trace(args...), "Unable to get information of the specified service account")

	if ctx.Bool("policy") {
		if svcInfo.Policy == "" {
			fatalIf(errDummy().Trace(args...), "No policy found associated to the specified service account. Check the policy of its parent user.")
		}
		p, e := iampolicy.ParseConfig(strings.NewReader(svcInfo.Policy))
		fatalIf(probe.NewError(e).Trace(args...), "Unable to parse policy.")
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", " ")
		fatalIf(probe.NewError(enc.Encode(p)).Trace(args...), "Unable to write policy to stdout.")
		return nil
	}

	printMsg(svcAcctMessage{
		op:            "info",
		AccessKey:     svcAccount,
		AccountStatus: svcInfo.AccountStatus,
		ParentUser:    svcInfo.ParentUser,
		ImpliedPolicy: svcInfo.ImpliedPolicy,
		Policy:        json.RawMessage(svcInfo.Policy),
	})

	return nil
}
<commit_msg>fix usage message wording (#4048)<commit_after>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// 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 (
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/pkg/console"
	iampolicy "github.com/minio/pkg/iam/policy"
)

var adminUserSvcAcctInfoFlags = []cli.Flag{
	cli.BoolFlag{
		Name:  "policy",
		Usage: "print policy in JSON format",
	},
}

var adminUserSvcAcctInfoCmd = cli.Command{
	Name:         "info",
	Usage:        "display service account info",
	Action:       mainAdminUserSvcAcctInfo,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(adminUserSvcAcctInfoFlags, globalFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} ALIAS SERVICE-ACCOUNT

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Display information for service account 'J123C4ZXEQN8RK6ND35I'
     {{.Prompt}} {{.HelpName}} myminio/ J123C4ZXEQN8RK6ND35I
`,
}

// checkAdminUserSvcAcctInfoSyntax - validate all the passed arguments
func checkAdminUserSvcAcctInfoSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		cli.ShowCommandHelpAndExit(ctx, "info", 1)
	}
}

// mainAdminUserSvcAcctInfo is the handle for "mc admin user svcacct info" command.
func mainAdminUserSvcAcctInfo(ctx *cli.Context) error {
	checkAdminUserSvcAcctInfoSyntax(ctx)

	console.SetColor("SVCMessage", color.New(color.FgGreen))

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)
	svcAccount := args.Get(1)

	// Create a new MinIO Admin Client
	client, err := newAdminClient(aliasedURL)
	fatalIf(err, "Unable to initialize admin connection.")

	svcInfo, e := client.InfoServiceAccount(globalContext, svcAccount)
	fatalIf(probe.NewError(e).Trace(args...), "Unable to get information of the specified service account")

	if ctx.Bool("policy") {
		if svcInfo.Policy == "" {
			fatalIf(errDummy().Trace(args...), "No policy found associated to the specified service account. Check the policy of its parent user.")
		}
		p, e := iampolicy.ParseConfig(strings.NewReader(svcInfo.Policy))
		fatalIf(probe.NewError(e).Trace(args...), "Unable to parse policy.")
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", " ")
		fatalIf(probe.NewError(enc.Encode(p)).Trace(args...), "Unable to write policy to stdout.")
		return nil
	}

	printMsg(svcAcctMessage{
		op:            "info",
		AccessKey:     svcAccount,
		AccountStatus: svcInfo.AccountStatus,
		ParentUser:    svcInfo.ParentUser,
		ImpliedPolicy: svcInfo.ImpliedPolicy,
		Policy:        json.RawMessage(svcInfo.Policy),
	})

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"time"

	"golang.org/x/crypto/ssh"
)

type BeaconConfig struct {
	Host             string   `long:"host" default:"127.0.0.1" description:"TSA host to forward the worker through."`
	Port             int      `long:"port" default:"2222" description:"TSA port to connect to."`
	PublicKey        FileFlag `long:"public-key" description:"File containing a public key to expect from the TSA."`
	WorkerPrivateKey FileFlag `long:"worker-private-key" description:"File containing the private key to use when authenticating to the TSA."`
}

func (config BeaconConfig) Dial() (*ssh.Client, error) {
	workerPrivateKeyBytes, err := ioutil.ReadFile(string(config.WorkerPrivateKey))
	if err != nil {
		return nil, fmt.Errorf("failed to read worker private key: %s", err)
	}

	workerPrivateKey, err := ssh.ParsePrivateKey(workerPrivateKeyBytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse worker private key: %s", err)
	}

	tsaAddr := fmt.Sprintf("%s:%d", config.Host, config.Port)

	conn, err := net.DialTimeout("tcp", tsaAddr, 10*time.Second)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to TSA:", err)
	}

	clientConfig := &ssh.ClientConfig{
		User: "beacon", // doesn't matter

		HostKeyCallback: config.checkHostKey,

		Auth: []ssh.AuthMethod{ssh.PublicKeys(workerPrivateKey)},
	}

	clientConn, chans, reqs, err := ssh.NewClientConn(conn, tsaAddr, clientConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to construct client connection:", err)
	}

	return ssh.NewClient(clientConn, chans, reqs), nil
}

func (config BeaconConfig) checkHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
	hostPublicKeyBytes, err := ioutil.ReadFile(string(config.PublicKey))
	if err != nil {
		return fmt.Errorf("failed to read host public key: %s", err)
	}

	hostPublicKey, _, _, _, err := ssh.ParseAuthorizedKey(hostPublicKeyBytes)
	if err != nil {
		return fmt.Errorf("failed to parse host public key: %s", err)
	}

	// note: hostname/addr are not verified; they may be behind a load balancer
	// so the definition gets a bit fuzzy

	if hostPublicKey.Type() != key.Type() || !bytes.Equal(hostPublicKey.Marshal(), key.Marshal()) {
		return errors.New("remote host public key mismatch")
	}

	return nil
}
<commit_msg>fix error message<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"time"

	"golang.org/x/crypto/ssh"
)

type BeaconConfig struct {
	Host             string   `long:"host" default:"127.0.0.1" description:"TSA host to forward the worker through."`
	Port             int      `long:"port" default:"2222" description:"TSA port to connect to."`
	PublicKey        FileFlag `long:"public-key" description:"File containing a public key to expect from the TSA."`
	WorkerPrivateKey FileFlag `long:"worker-private-key" description:"File containing the private key to use when authenticating to the TSA."`
}

func (config BeaconConfig) Dial() (*ssh.Client, error) {
	workerPrivateKeyBytes, err := ioutil.ReadFile(string(config.WorkerPrivateKey))
	if err != nil {
		return nil, fmt.Errorf("failed to read worker private key: %s", err)
	}

	workerPrivateKey, err := ssh.ParsePrivateKey(workerPrivateKeyBytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse worker private key: %s", err)
	}

	tsaAddr := fmt.Sprintf("%s:%d", config.Host, config.Port)

	conn, err := net.DialTimeout("tcp", tsaAddr, 10*time.Second)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to TSA: %s", err)
	}

	clientConfig := &ssh.ClientConfig{
		User: "beacon", // doesn't matter

		HostKeyCallback: config.checkHostKey,

		Auth: []ssh.AuthMethod{ssh.PublicKeys(workerPrivateKey)},
	}

	clientConn, chans, reqs, err := ssh.NewClientConn(conn, tsaAddr, clientConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to construct client connection:", err)
	}

	return ssh.NewClient(clientConn, chans, reqs), nil
}

func (config BeaconConfig) checkHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
	hostPublicKeyBytes, err := ioutil.ReadFile(string(config.PublicKey))
	if err != nil {
		return fmt.Errorf("failed to read host public key: %s", err)
	}

	hostPublicKey, _, _, _, err := ssh.ParseAuthorizedKey(hostPublicKeyBytes)
	if err != nil {
		return fmt.Errorf("failed to parse host public key: %s", err)
	}

	// note: hostname/addr are not verified; they may be behind a load balancer
	// so the definition gets a bit fuzzy

	if hostPublicKey.Type() != key.Type() || !bytes.Equal(hostPublicKey.Marshal(), key.Marshal()) {
		return errors.New("remote host public key mismatch")
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 convert

import (
	"fmt"

	"github.com/spf13/cobra"
	auditreg "k8s.io/api/auditregistration/v1alpha1"
	apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	apijson "k8s.io/apimachinery/pkg/runtime/serializer/json"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	kscheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/klog"
	apireg "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	whapi "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
	cmacmev1alpha3 "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha3"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmapiv1alpha3 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha3"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
)

var (
	example = templates.Examples(i18n.T(`
		# Convert 'cert.yaml' to latest version and print to stdout.
		kubectl convert -f cert.yaml

		# Convert kustomize overlay under current directory to 'cert-manager.io/v1alpha3'
		kubectl convert -k . --output-version cert-manager.io/v1alpha3`))

	longDesc = templates.LongDesc(i18n.T(`
Convert cert-manager config files between different API versions. Both YAML
and JSON formats are accepted.

The command takes filename, directory, or URL as input, and converts into the
format of the version specified by --output-version flag. If target version is
not specified or not supported, it will convert to the latest version

The default output will be printed to stdout in YAML format. One can use -o option
to change to output destination.`))
)

var (
	scheme             = runtime.NewScheme()
	codecs             = serializer.NewCodecFactory(scheme)
	parameterCodec     = runtime.NewParameterCodec(scheme)
	localSchemeBuilder = runtime.SchemeBuilder{
		cmapi.AddToScheme,
		cmapiv1alpha3.AddToScheme,
		cmacme.AddToScheme,
		cmacmev1alpha3.AddToScheme,
		cmmeta.AddToScheme,
		whapi.AddToScheme,
		kscheme.AddToScheme,
		apireg.AddToScheme,
		apiext.AddToScheme,
		auditreg.AddToScheme,
	}

	addToScheme = localSchemeBuilder.AddToScheme
)

func init() {
	// This is used to add the List object type for outputing multiple input
	// objects.
	coreGroupVersion := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
	scheme.AddKnownTypes(coreGroupVersion, &metainternalversion.List{})

	metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
	utilruntime.Must(addToScheme(scheme))
}

// Options is a struct to support convert command
type Options struct {
	PrintFlags *genericclioptions.PrintFlags
	Printer    printers.ResourcePrinter

	OutputVersion string

	resource.FilenameOptions
	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams:  ioStreams,
		PrintFlags: genericclioptions.NewPrintFlags("converted").WithDefaultOutput("yaml"),
	}
}

// NewCmdConvert returns a cobra command for converting cert-manager resources
func NewCmdConvert(ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewOptions(ioStreams)

	cmd := &cobra.Command{
		Use:                   "convert",
		Short:                 "Convert cert-manager config files between different API versions",
		Long:                  longDesc,
		Example:               example,
		DisableFlagsInUseLine: true,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete())
			cmdutil.CheckErr(o.Run())
		},
	}

	cmd.Flags().StringVar(&o.OutputVersion, "output-version", o.OutputVersion, "Output the formatted object with the given group version (for ex: 'cert-manager.io/v1alpha3').")
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "Path to a file containing cert-manager resources to be converted.")
	o.PrintFlags.AddFlags(cmd)

	return cmd
}

// Complete collects information required to run Convert command from command line.
func (o *Options) Complete() error {
	err := o.FilenameOptions.RequireFilenameOrKustomize()
	if err != nil {
		return err
	}

	// build the printer
	o.Printer, err = o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	return nil
}

// Run executes convert command
func (o *Options) Run() error {
	builder := new(resource.Builder)

	r := builder.Unstructured().LocalParam(true).ContinueOnError().
		FilenameParam(false, &o.FilenameOptions).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error here: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error here instead: %s", err)
	}

	if len(infos) == 0 {
		return fmt.Errorf("no objects passed to convert")
	}

	var specifiedOutputVersion schema.GroupVersion
	if len(o.OutputVersion) > 0 {
		specifiedOutputVersion, err = schema.ParseGroupVersion(o.OutputVersion)
		if err != nil {
			return err
		}
	}

	factory := serializer.NewCodecFactory(scheme)
	serializer := apijson.NewSerializerWithOptions(apijson.DefaultMetaFactory, scheme, scheme, apijson.SerializerOptions{})
	encoder := factory.WithoutConversion().EncoderForVersion(serializer, nil)
	objects, err := asVersionedObject(infos, !singleItemImplied, specifiedOutputVersion, encoder)
	if err != nil {
		return err
	}

	return o.Printer.PrintObj(objects, o.Out)
}

// asVersionedObject converts a list of infos into a single object - either a List containing
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
	objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)
	if err != nil {
		return nil, err
	}

	var object runtime.Object
	if len(objects) == 1 && !forceList {
		object = objects[0]
	} else {
		object = &metainternalversion.List{Items: objects}

		targetVersions := []schema.GroupVersion{}
		if !specifiedOutputVersion.Empty() {
			targetVersions = append(targetVersions, specifiedOutputVersion)
		}
		// This is needed so we are able to handle the List object when converting
		// multiple resources
		targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})

		converted, err := tryConvert(object, targetVersions...)
		if err != nil {
			return nil, err
		}

		object = converted
	}

	actualVersion := object.GetObjectKind().GroupVersionKind()

	if actualVersion.Version != specifiedOutputVersion.Version {
		defaultVersionInfo := ""
		if len(actualVersion.Version) > 0 {
			defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version)
		}
		klog.V(1).Infof("info: the output version specified is invalid. %s\n", defaultVersionInfo)
	}

	return object, nil
}

// asVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
	objects := []runtime.Object{}
	for _, info := range infos {
		if info.Object == nil {
			continue
		}

		targetVersions := []schema.GroupVersion{}
		// objects that are not part of api.Scheme must be converted to JSON
		if !specifiedOutputVersion.Empty() {
			_, _, err := scheme.ObjectKinds(info.Object)
			if err != nil {
				if runtime.IsNotRegisteredError(err) {
					data, err := runtime.Encode(encoder, info.Object)
					if err != nil {
						return nil, err
					}
					objects = append(objects, &runtime.Unknown{Raw: data})
					continue
				}

				return nil, err
			}

			targetVersions = append(targetVersions, specifiedOutputVersion)
		} else {
			gvks, _, err := scheme.ObjectKinds(info.Object)
			if err == nil {
				for _, gvk := range gvks {
					targetVersions = append(targetVersions, scheme.PrioritizedVersionsForGroup(gvk.Group)...)
				}
			}
		}

		converted, err := tryConvert(info.Object, targetVersions...)
		if err != nil {
			return nil, err
		}
		objects = append(objects, converted)
	}

	return objects, nil
}

// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) {
	var last error
	for _, version := range versions {
		if version.Empty() {
			return object, nil
		}
		obj, err := scheme.ConvertToVersion(object, version)
		if err != nil {
			last = err
			continue
		}
		return obj, nil
	}

	return nil, last
}
<commit_msg>Fix the convert example<commit_after>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 convert

import (
	"fmt"

	"github.com/spf13/cobra"
	auditreg "k8s.io/api/auditregistration/v1alpha1"
	apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	apijson "k8s.io/apimachinery/pkg/runtime/serializer/json"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	kscheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/klog"
	apireg "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	whapi "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
	cmacmev1alpha3 "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha3"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmapiv1alpha3 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha3"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
)

var (
	example = templates.Examples(i18n.T(`
		# Convert 'cert.yaml' to latest version and print to stdout.
		kubectl cert-manager convert -f cert.yaml

		# Convert kustomize overlay under current directory to 'cert-manager.io/v1alpha3'
		kubectl cert-manager convert -k . --output-version cert-manager.io/v1alpha3`))

	longDesc = templates.LongDesc(i18n.T(`
Convert cert-manager config files between different API versions. Both YAML
and JSON formats are accepted.

The command takes filename, directory, or URL as input, and converts into the
format of the version specified by --output-version flag. If target version is
not specified or not supported, it will convert to the latest version

The default output will be printed to stdout in YAML format. One can use -o option
to change to output destination.`))
)

var (
	scheme             = runtime.NewScheme()
	codecs             = serializer.NewCodecFactory(scheme)
	parameterCodec     = runtime.NewParameterCodec(scheme)
	localSchemeBuilder = runtime.SchemeBuilder{
		cmapi.AddToScheme,
		cmapiv1alpha3.AddToScheme,
		cmacme.AddToScheme,
		cmacmev1alpha3.AddToScheme,
		cmmeta.AddToScheme,
		whapi.AddToScheme,
		kscheme.AddToScheme,
		apireg.AddToScheme,
		apiext.AddToScheme,
		auditreg.AddToScheme,
	}

	addToScheme = localSchemeBuilder.AddToScheme
)

func init() {
	// This is used to add the List object type for outputing multiple input
	// objects.
	coreGroupVersion := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
	scheme.AddKnownTypes(coreGroupVersion, &metainternalversion.List{})

	metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
	utilruntime.Must(addToScheme(scheme))
}

// Options is a struct to support convert command
type Options struct {
	PrintFlags *genericclioptions.PrintFlags
	Printer    printers.ResourcePrinter

	OutputVersion string

	resource.FilenameOptions
	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams:  ioStreams,
		PrintFlags: genericclioptions.NewPrintFlags("converted").WithDefaultOutput("yaml"),
	}
}

// NewCmdConvert returns a cobra command for converting cert-manager resources
func NewCmdConvert(ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewOptions(ioStreams)

	cmd := &cobra.Command{
		Use:                   "convert",
		Short:                 "Convert cert-manager config files between different API versions",
		Long:                  longDesc,
		Example:               example,
		DisableFlagsInUseLine: true,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete())
			cmdutil.CheckErr(o.Run())
		},
	}

	cmd.Flags().StringVar(&o.OutputVersion, "output-version", o.OutputVersion, "Output the formatted object with the given group version (for ex: 'cert-manager.io/v1alpha3').")
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "Path to a file containing cert-manager resources to be converted.")
	o.PrintFlags.AddFlags(cmd)

	return cmd
}

// Complete collects information required to run Convert command from command line.
func (o *Options) Complete() error {
	err := o.FilenameOptions.RequireFilenameOrKustomize()
	if err != nil {
		return err
	}

	// build the printer
	o.Printer, err = o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	return nil
}

// Run executes convert command
func (o *Options) Run() error {
	builder := new(resource.Builder)

	r := builder.Unstructured().LocalParam(true).ContinueOnError().
		FilenameParam(false, &o.FilenameOptions).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error here: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error here instead: %s", err)
	}

	if len(infos) == 0 {
		return fmt.Errorf("no objects passed to convert")
	}

	var specifiedOutputVersion schema.GroupVersion
	if len(o.OutputVersion) > 0 {
		specifiedOutputVersion, err = schema.ParseGroupVersion(o.OutputVersion)
		if err != nil {
			return err
		}
	}

	factory := serializer.NewCodecFactory(scheme)
	serializer := apijson.NewSerializerWithOptions(apijson.DefaultMetaFactory, scheme, scheme, apijson.SerializerOptions{})
	encoder := factory.WithoutConversion().EncoderForVersion(serializer, nil)
	objects, err := asVersionedObject(infos, !singleItemImplied, specifiedOutputVersion, encoder)
	if err != nil {
		return err
	}

	return o.Printer.PrintObj(objects, o.Out)
}

// asVersionedObject converts a list of infos into a single object - either a List containing
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
	objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)
	if err != nil {
		return nil, err
	}

	var object runtime.Object
	if len(objects) == 1 && !forceList {
		object = objects[0]
	} else {
		object = &metainternalversion.List{Items: objects}

		targetVersions := []schema.GroupVersion{}
		if !specifiedOutputVersion.Empty() {
			targetVersions = append(targetVersions, specifiedOutputVersion)
		}
		// This is needed so we are able to handle the List object when converting
		// multiple resources
		targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})

		converted, err := tryConvert(object, targetVersions...)
		if err != nil {
			return nil, err
		}

		object = converted
	}

	actualVersion := object.GetObjectKind().GroupVersionKind()

	if actualVersion.Version != specifiedOutputVersion.Version {
		defaultVersionInfo := ""
		if len(actualVersion.Version) > 0 {
			defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version)
		}
		klog.V(1).Infof("info: the output version specified is invalid. %s\n", defaultVersionInfo)
	}

	return object, nil
}

// asVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
	objects := []runtime.Object{}
	for _, info := range infos {
		if info.Object == nil {
			continue
		}

		targetVersions := []schema.GroupVersion{}
		// objects that are not part of api.Scheme must be converted to JSON
		if !specifiedOutputVersion.Empty() {
			_, _, err := scheme.ObjectKinds(info.Object)
			if err != nil {
				if runtime.IsNotRegisteredError(err) {
					data, err := runtime.Encode(encoder, info.Object)
					if err != nil {
						return nil, err
					}
					objects = append(objects, &runtime.Unknown{Raw: data})
					continue
				}

				return nil, err
			}

			targetVersions = append(targetVersions, specifiedOutputVersion)
		} else {
			gvks, _, err := scheme.ObjectKinds(info.Object)
			if err == nil {
				for _, gvk := range gvks {
					targetVersions = append(targetVersions, scheme.PrioritizedVersionsForGroup(gvk.Group)...)
				}
			}
		}

		converted, err := tryConvert(info.Object, targetVersions...)
		if err != nil {
			return nil, err
		}
		objects = append(objects, converted)
	}

	return objects, nil
}

// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) {
	var last error
	for _, version := range versions {
		if version.Empty() {
			return object, nil
		}
		obj, err := scheme.ConvertToVersion(object, version)
		if err != nil {
			last = err
			continue
		}
		return obj, nil
	}

	return nil, last
}
<|endoftext|>