<commit_before>package main

import (
    "bufio"
    "os"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
    "encoding/json"
    "strconv"
)

const delim = '\n'

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {

  var token string = os.Getenv("GH_TOKEN_ALLI") // env var name
  var home string = os.Getenv("HOME") // env var name

  if token == "" {
    fmt.Printf("No token found! Using highly rate-limited public access.\n")
  } else {
    fmt.Printf("Yay! Using authentication token!\n")
  }

  r := bufio.NewReader(os.Stdin)

  var username string
  saved_username, err := ioutil.ReadFile(home + "/.alli")

  if string(saved_username) == "" {
    print("Enter Github username: ")
    username, err = r.ReadString(delim)
    username = strings.TrimSpace(username)
    check(err)

    fmt.Printf("Would you like to save %s as the default? (y/n): ", username)
    response, err := r.ReadString(delim)
    check(err)
    if response == "y\n" {
      // write username to save
      d1 := []byte(username)
      ioutil.WriteFile(home + "/.alli", d1, 0644)
    }
  } else {
    username = strings.TrimSpace(string(saved_username))
    fmt.Printf("Using saved username: %s\n", username)
  }

  client := &http.Client{}
  var anotherPage bool = true
  pageNum := 1
  for anotherPage {
    req, _ := http.NewRequest("GET", "https://api.github.com/users/" + username + "/repos?per_page=100&page=" + strconv.Itoa(pageNum), nil)
    req.Header.Set("Accept", "application/vnd.github.v3+json")

    if token != "" {
      req.SetBasicAuth(token, "x-oauth-basic") // user, password
    }

    repos, err := client.Do(req)
    check(err)

    defer repos.Body.Close()
    contents, err := ioutil.ReadAll(repos.Body);
    check(err)

    byt := []byte(contents)

    var f interface{}
    err = json.Unmarshal(byt, &f)
    check(err)

    array := f.([]interface {})
    pageNum++
    if(len(array) == 100) {
      anotherPage = true
    } else {
      anotherPage = false
    }

    println()

    for i := range array {
      repo := array[i].(map[string]interface {})
      var countFloat float64 = repo["open_issues_count"].(float64)
      var countInt int = int(countFloat)
      if(countInt != 0) {
        var name string = repo["full_name"].(string)
        fmt.Printf("%s\n", name)

        req, _ = http.NewRequest("GET", "https://api.github.com/repos/" + name + "/issues?state=open", nil)
        req.Header.Set("Accept", "application/vnd.github.v3+json")
        if token != "" {
          req.SetBasicAuth(token, "x-oauth-basic") // user, password
        }
        issues, err := client.Do(req)
        check(err)

        defer issues.Body.Close()
        iss, err := ioutil.ReadAll(issues.Body);
        check(err)

        byt = [] byte(iss)
        var g interface{}
        err = json.Unmarshal(byt, &g)
        check(err)

        issue_array := g.([]interface {})

        for j := range issue_array {
          issue := issue_array[j].(map[string]interface {})
          var number int = int(issue["number"].(float64))
          var title string = issue["title"].(string)
          fmt.Printf("#%d %s\n", number, title)
        }
        fmt.Printf("\n");
      }
    }
  }
}
<commit_msg>Go fmt<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
)

const delim = '\n'

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {

	var token string = os.Getenv("GH_TOKEN_ALLI") // env var name
	var home string = os.Getenv("HOME")           // env var name

	if token == "" {
		fmt.Printf("No token found! Using highly rate-limited public access.\n")
	} else {
		fmt.Printf("Yay! Using authentication token!\n")
	}

	r := bufio.NewReader(os.Stdin)

	var username string
	saved_username, err := ioutil.ReadFile(home + "/.alli")

	if string(saved_username) == "" {
		print("Enter Github username: ")
		username, err = r.ReadString(delim)
		username = strings.TrimSpace(username)
		check(err)

		fmt.Printf("Would you like to save %s as the default? (y/n): ", username)
		response, err := r.ReadString(delim)
		check(err)
		if response == "y\n" {
			// write username to save
			d1 := []byte(username)
			ioutil.WriteFile(home+"/.alli", d1, 0644)
		}
	} else {
		username = strings.TrimSpace(string(saved_username))
		fmt.Printf("Using saved username: %s\n", username)
	}

	client := &http.Client{}
	var anotherPage bool = true
	pageNum := 1
	for anotherPage {
		req, _ := http.NewRequest("GET", "https://api.github.com/users/"+username+"/repos?per_page=100&page="+strconv.Itoa(pageNum), nil)
		req.Header.Set("Accept", "application/vnd.github.v3+json")

		if token != "" {
			req.SetBasicAuth(token, "x-oauth-basic") // user, password
		}

		repos, err := client.Do(req)
		check(err)

		defer repos.Body.Close()
		contents, err := ioutil.ReadAll(repos.Body)
		check(err)

		byt := []byte(contents)

		var f interface{}
		err = json.Unmarshal(byt, &f)
		check(err)

		array := f.([]interface{})
		pageNum++
		if len(array) == 100 {
			anotherPage = true
		} else {
			anotherPage = false
		}

		println()

		for i := range array {
			repo := array[i].(map[string]interface{})
			var countFloat float64 = repo["open_issues_count"].(float64)
			var countInt int = int(countFloat)
			if countInt != 0 {
				var name string = repo["full_name"].(string)
				fmt.Printf("%s\n", name)

				req, _ = http.NewRequest("GET", "https://api.github.com/repos/"+name+"/issues?state=open", nil)
				req.Header.Set("Accept", "application/vnd.github.v3+json")
				if token != "" {
					req.SetBasicAuth(token, "x-oauth-basic") // user, password
				}
				issues, err := client.Do(req)
				check(err)

				defer issues.Body.Close()
				iss, err := ioutil.ReadAll(issues.Body)
				check(err)

				byt = []byte(iss)
				var g interface{}
				err = json.Unmarshal(byt, &g)
				check(err)

				issue_array := g.([]interface{})

				for j := range issue_array {
					issue := issue_array[j].(map[string]interface{})
					var number int = int(issue["number"].(float64))
					var title string = issue["title"].(string)
					fmt.Printf("#%d %s\n", number, title)
				}
				fmt.Printf("\n")
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker, 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 volume

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"

	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/aci"
	"github.com/docker/compose-cli/api/client"
	"github.com/docker/compose-cli/progress"
)

// ACICommand manage volumes
func ACICommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "volume",
		Short: "Manages volumes",
	}

	cmd.AddCommand(
		createVolume(),
		listVolume(),
		rmVolume(),
	)
	return cmd
}

func createVolume() *cobra.Command {
	aciOpts := aci.VolumeCreateOptions{}
	cmd := &cobra.Command{
		Use:   "create",
		Short: "Creates an Azure file share to use as ACI volume.",
		Args:  cobra.ExactArgs(0),
		RunE: func(cmd *cobra.Command, args []string) error {
			ctx := cmd.Context()
			c, err := client.New(ctx)
			if err != nil {
				return err
			}
			err = progress.Run(ctx, func(ctx context.Context) error {
				if _, err := c.VolumeService().Create(ctx, aciOpts); err != nil {
					return err
				}
				return nil
			})
			if err != nil {
				return err
			}
			fmt.Println(aci.VolumeID(aciOpts.Account, aciOpts.Fileshare))
			return nil
		},
	}

	cmd.Flags().StringVar(&aciOpts.Account, "storage-account", "", "Storage account name")
	cmd.Flags().StringVar(&aciOpts.Fileshare, "fileshare", "", "Fileshare name")
	return cmd
}

func rmVolume() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "rm [OPTIONS] VOLUME [VOLUME...]",
		Short: "Remove one or more volumes.",
		Args:  cobra.MinimumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			c, err := client.New(cmd.Context())
			if err != nil {
				return err
			}
			var errs *multierror.Error
			for _, id := range args {
				err = c.VolumeService().Delete(cmd.Context(), id, nil)
				if err != nil {
					errs = multierror.Append(errs, err)
					continue
				}
				fmt.Println(id)
			}
			if errs != nil {
				errs.ErrorFormat = formatErrors
			}
			return errs.ErrorOrNil()
		},
	}
	return cmd
}

func formatErrors(errs []error) string {
	messages := make([]string, len(errs))
	for i, err := range errs {
		messages[i] = "Error: " + err.Error()
	}
	return strings.Join(messages, "\n")
}
<commit_msg>ACI Volume create flags are required<commit_after>/*
   Copyright 2020 Docker, 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 volume

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"

	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/aci"
	"github.com/docker/compose-cli/api/client"
	"github.com/docker/compose-cli/progress"
)

// ACICommand manage volumes
func ACICommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "volume",
		Short: "Manages volumes",
	}

	cmd.AddCommand(
		createVolume(),
		listVolume(),
		rmVolume(),
	)
	return cmd
}

func createVolume() *cobra.Command {
	aciOpts := aci.VolumeCreateOptions{}
	cmd := &cobra.Command{
		Use:   "create --storage-account ACCOUNT --fileshare FILESHARE",
		Short: "Creates an Azure file share to use as ACI volume.",
		Args:  cobra.ExactArgs(0),
		RunE: func(cmd *cobra.Command, args []string) error {
			ctx := cmd.Context()
			c, err := client.New(ctx)
			if err != nil {
				return err
			}
			err = progress.Run(ctx, func(ctx context.Context) error {
				if _, err := c.VolumeService().Create(ctx, aciOpts); err != nil {
					return err
				}
				return nil
			})
			if err != nil {
				return err
			}
			fmt.Println(aci.VolumeID(aciOpts.Account, aciOpts.Fileshare))
			return nil
		},
	}

	cmd.Flags().StringVar(&aciOpts.Account, "storage-account", "", "Storage account name")
	cmd.Flags().StringVar(&aciOpts.Fileshare, "fileshare", "", "Fileshare name")
	_ = cmd.MarkFlagRequired("fileshare")
	_ = cmd.MarkFlagRequired("storage-account")
	return cmd
}

func rmVolume() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "rm [OPTIONS] VOLUME [VOLUME...]",
		Short: "Remove one or more volumes.",
		Args:  cobra.MinimumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			c, err := client.New(cmd.Context())
			if err != nil {
				return err
			}
			var errs *multierror.Error
			for _, id := range args {
				err = c.VolumeService().Delete(cmd.Context(), id, nil)
				if err != nil {
					errs = multierror.Append(errs, err)
					continue
				}
				fmt.Println(id)
			}
			if errs != nil {
				errs.ErrorFormat = formatErrors
			}
			return errs.ErrorOrNil()
		},
	}
	return cmd
}

func formatErrors(errs []error) string {
	messages := make([]string, len(errs))
	for i, err := range errs {
		messages[i] = "Error: " + err.Error()
	}
	return strings.Join(messages, "\n")
}
<|endoftext|>


<commit_before>package client

import (
	"github.com/gtfierro/cs262-project/common"
	"github.com/pkg/errors"
	"github.com/tinylib/msgp/msgp"
	"io"
	"net"
	"sync"
	"time"
)

type BrokerConnection struct {
	// Handling the connection to the local broker
	// the IP:Port of the local broker we talk to
	BrokerAddress    *net.TCPAddr
	brokerConn       *net.TCPConn
	brokerEncoder    *msgp.Writer
	brokerEncodeLock sync.Mutex

	// the IP:Port of the coordinator that we fall back to
	CoordinatorAddress *net.TCPAddr
	coordConn          *net.TCPConn
	coordEncoder       *msgp.Writer
	coordEncodeLock    sync.Mutex

	// signals on this channel when it is done
	Stop       chan bool
	brokerDead bool

	connectCallback func()
	msgHandler      func(common.Sendable)
}

func (bc *BrokerConnection) initialize(connectCallback func(), msgHandler func(common.Sendable), cfg *Config) (err error) {
	bc.brokerDead = true
	bc.connectCallback = connectCallback
	bc.msgHandler = msgHandler
	bc.Stop = make(chan bool)

	if bc.BrokerAddress, err = net.ResolveTCPAddr("tcp", cfg.BrokerAddress); err != nil {
		return
	}

	if err = bc.connectBroker(bc.BrokerAddress); err != nil {
		return
	}

	bc.CoordinatorAddress, err = net.ResolveTCPAddr("tcp", cfg.CoordinatorAddress)
	return
}

func (bc *BrokerConnection) Start() {
	go bc.listen()
	bc.connectCallback()
}

// after the duration expires, stop the client by signalling on c.Stop
func (bc *BrokerConnection) StopIn(d time.Duration) {
	go func(c *BrokerConnection) {
		time.Sleep(d)
		c.Stop <- true
	}(bc)
}

func (bc *BrokerConnection) listen() {
	reader := msgp.NewReader(net.Conn(bc.brokerConn))
	for {
		if bc.brokerDead {
			return
		}
		msg, err := common.MessageFromDecoderMsgp(reader)
		if err == io.EOF {
			log.Warn("connection closed. Do failover")
			bc.doFailover()
			break
		}
		if err != nil {
			log.Warn(errors.Wrap(err, "Could not decode message"))
		}

		bc.msgHandler(msg)
	}
}

// This function should contact the coordinator to get the new broker
func (bc *BrokerConnection) configureNewBroker(m *common.BrokerAssignmentMessage) {
	var err error
	bc.BrokerAddress, err = net.ResolveTCPAddr("tcp", m.ClientBrokerAddr)
	if err != nil {
		log.Critical(errors.Wrap(err, "Could not resolve local broker address"))
	}
	if err = bc.connectBroker(bc.BrokerAddress); err != nil {
		log.Critical(errors.Wrap(err, "Could not connect to local broker"))
	}
	// send our subscription once we're back
	bc.connectCallback()
	go bc.listen()
}

func (bc *BrokerConnection) connectBroker(address *net.TCPAddr) error {
	var err error
	if bc.brokerConn, err = net.DialTCP("tcp", nil, address); err != nil {
		return errors.Wrap(err, "Could not dial broker")
	}
	bc.brokerEncodeLock.Lock()
	bc.brokerEncoder = msgp.NewWriter(bc.brokerConn)
	bc.brokerEncodeLock.Unlock()
	bc.brokerDead = false
	return nil
}

// Sends message to the currently configured broker
func (bc *BrokerConnection) sendBroker(m common.Sendable) error {
	bc.brokerEncodeLock.Lock()
	defer bc.brokerEncodeLock.Unlock()
	if err := m.Encode(bc.brokerEncoder); err != nil {
		return errors.Wrap(err, "Could not encode message")
	}
	if err := bc.brokerEncoder.Flush(); err != nil {
		return errors.Wrap(err, "Could not send message to broker")
	}
	return nil
}

// This should be triggered when we can no longer contact our local broker. In this
// case, we sent a BrokerRequestMessage to the coordinator
func (bc *BrokerConnection) doFailover() {
	bc.brokerDead = true
	// establish the coordinator connection
	bc.connectCoordinator()
	// prepare the BrokerRequestMessage
	brm := &common.BrokerRequestMessage{
		LocalBrokerAddr: bc.BrokerAddress.String(),
		IsPublisher:     false,                                  // TODO
		UUID:            "392c1b18-0c37-11e6-b352-1002b58053c7", // TODO
	}
	// loop until we can contact the coordinator
	err := bc.sendCoordinator(brm)
	for err != nil {
		time.Sleep(1)
		err = bc.sendCoordinator(brm)
	}
}

// Loop until we can finally connect to the coordinator.
// This blocks indefinitely until it is successful
func (bc *BrokerConnection) connectCoordinator() {
	var (
		err      error
		waitTime = 1 * time.Second
		maxWait  = 30 * time.Second
	)
	bc.coordConn, err = net.DialTCP("tcp", nil, bc.CoordinatorAddress)
	for err != nil {
		log.Warningf("Retrying coordinator connection to %v with delay %v", bc.CoordinatorAddress, waitTime)
		time.Sleep(waitTime)
		waitTime *= 2
		if waitTime > maxWait {
			waitTime = maxWait
		}
		bc.coordConn, err = net.DialTCP("tcp", nil, bc.CoordinatorAddress)
	}
	log.Debug("Connected to coordinator")
	go bc.listenCoordinator()
	bc.coordEncoder = msgp.NewWriter(bc.coordConn)
}

func (bc *BrokerConnection) listenCoordinator() {
	if bc.coordConn == nil {
		return
	}
	reader := msgp.NewReader(net.Conn(bc.coordConn))
	for {
		msg, err := common.MessageFromDecoderMsgp(reader)
		if err == io.EOF {
			log.Warn("connection closed. Do failover")
			bc.doFailover()
			break
		}
		if err != nil {
			log.Warn(errors.Wrap(err, "Could not decode message"))
		}

		log.Infof("Got %T message %v from coordinator", msg, msg)
		switch m := msg.(type) {
		case *common.BrokerAssignmentMessage:
			bc.configureNewBroker(m)
			bc.coordConn.Close()
			return
		default:
			log.Infof("Got %T message %v from coordinator", m, m)
		}
	}
}

func (bc *BrokerConnection) sendCoordinator(m common.Sendable) error {
	bc.coordEncodeLock.Lock()
	if err := m.Encode(bc.coordEncoder); err != nil {
		bc.coordEncodeLock.Unlock()
		return errors.Wrap(err, "Could not encode message")
	}
	if err := bc.coordEncoder.Flush(); err != nil {
		bc.coordEncodeLock.Unlock()
		bc.connectCoordinator()
		return errors.Wrap(err, "Could not send message to coordinator")
	}
	bc.coordEncodeLock.Unlock()
	return nil
}
<commit_msg>Fix issue with host names<commit_after>package client

import (
	"github.com/gtfierro/cs262-project/common"
	"github.com/pkg/errors"
	"github.com/tinylib/msgp/msgp"
	"io"
	"net"
	"sync"
	"time"
)

type BrokerConnection struct {
	// Handling the connection to the local broker
	// the IP:Port of the local broker we talk to
	BrokerAddrStr    string
	BrokerAddress    *net.TCPAddr
	brokerConn       *net.TCPConn
	brokerEncoder    *msgp.Writer
	brokerEncodeLock sync.Mutex

	// the IP:Port of the coordinator that we fall back to
	CoordinatorAddress *net.TCPAddr
	coordConn          *net.TCPConn
	coordEncoder       *msgp.Writer
	coordEncodeLock    sync.Mutex

	// signals on this channel when it is done
	Stop       chan bool
	brokerDead bool

	connectCallback func()
	msgHandler      func(common.Sendable)
}

func (bc *BrokerConnection) initialize(connectCallback func(), msgHandler func(common.Sendable), cfg *Config) (err error) {
	bc.brokerDead = true
	bc.connectCallback = connectCallback
	bc.msgHandler = msgHandler
	bc.Stop = make(chan bool)
	bc.BrokerAddrStr = cfg.BrokerAddress

	if bc.BrokerAddress, err = net.ResolveTCPAddr("tcp", cfg.BrokerAddress); err != nil {
		return
	}

	if err = bc.connectBroker(bc.BrokerAddress); err != nil {
		return
	}

	bc.CoordinatorAddress, err = net.ResolveTCPAddr("tcp", cfg.CoordinatorAddress)
	return
}

func (bc *BrokerConnection) Start() {
	go bc.listen()
	bc.connectCallback()
}

// after the duration expires, stop the client by signalling on c.Stop
func (bc *BrokerConnection) StopIn(d time.Duration) {
	go func(c *BrokerConnection) {
		time.Sleep(d)
		c.Stop <- true
	}(bc)
}

func (bc *BrokerConnection) listen() {
	reader := msgp.NewReader(net.Conn(bc.brokerConn))
	for {
		if bc.brokerDead {
			return
		}
		msg, err := common.MessageFromDecoderMsgp(reader)
		if err == io.EOF {
			log.Warn("connection closed. Do failover")
			bc.doFailover()
			break
		}
		if err != nil {
			log.Warn(errors.Wrap(err, "Could not decode message"))
		}

		bc.msgHandler(msg)
	}
}

// This function should contact the coordinator to get the new broker
func (bc *BrokerConnection) configureNewBroker(m *common.BrokerAssignmentMessage) {
	var err error
	bc.BrokerAddress, err = net.ResolveTCPAddr("tcp", m.ClientBrokerAddr)
	if err != nil {
		log.Critical(errors.Wrap(err, "Could not resolve local broker address"))
	}
	if err = bc.connectBroker(bc.BrokerAddress); err != nil {
		log.Critical(errors.Wrap(err, "Could not connect to local broker"))
	}
	// send our subscription once we're back
	bc.connectCallback()
	go bc.listen()
}

func (bc *BrokerConnection) connectBroker(address *net.TCPAddr) error {
	var err error
	if bc.brokerConn, err = net.DialTCP("tcp", nil, address); err != nil {
		return errors.Wrap(err, "Could not dial broker")
	}
	bc.brokerEncodeLock.Lock()
	bc.brokerEncoder = msgp.NewWriter(bc.brokerConn)
	bc.brokerEncodeLock.Unlock()
	bc.brokerDead = false
	return nil
}

// Sends message to the currently configured broker
func (bc *BrokerConnection) sendBroker(m common.Sendable) error {
	bc.brokerEncodeLock.Lock()
	defer bc.brokerEncodeLock.Unlock()
	if err := m.Encode(bc.brokerEncoder); err != nil {
		return errors.Wrap(err, "Could not encode message")
	}
	if err := bc.brokerEncoder.Flush(); err != nil {
		return errors.Wrap(err, "Could not send message to broker")
	}
	return nil
}

// This should be triggered when we can no longer contact our local broker. In this
// case, we sent a BrokerRequestMessage to the coordinator
func (bc *BrokerConnection) doFailover() {
	bc.brokerDead = true
	// establish the coordinator connection
	bc.connectCoordinator()
	// prepare the BrokerRequestMessage
	brm := &common.BrokerRequestMessage{
		LocalBrokerAddr: bc.BrokerAddrStr,
		IsPublisher:     false,                                  // TODO
		UUID:            "392c1b18-0c37-11e6-b352-1002b58053c7", // TODO
	}
	// loop until we can contact the coordinator
	err := bc.sendCoordinator(brm)
	for err != nil {
		time.Sleep(1)
		err = bc.sendCoordinator(brm)
	}
}

// Loop until we can finally connect to the coordinator.
// This blocks indefinitely until it is successful
func (bc *BrokerConnection) connectCoordinator() {
	var (
		err      error
		waitTime = 1 * time.Second
		maxWait  = 30 * time.Second
	)
	bc.coordConn, err = net.DialTCP("tcp", nil, bc.CoordinatorAddress)
	for err != nil {
		log.Warningf("Retrying coordinator connection to %v with delay %v", bc.CoordinatorAddress, waitTime)
		time.Sleep(waitTime)
		waitTime *= 2
		if waitTime > maxWait {
			waitTime = maxWait
		}
		bc.coordConn, err = net.DialTCP("tcp", nil, bc.CoordinatorAddress)
	}
	log.Debug("Connected to coordinator")
	go bc.listenCoordinator()
	bc.coordEncoder = msgp.NewWriter(bc.coordConn)
}

func (bc *BrokerConnection) listenCoordinator() {
	if bc.coordConn == nil {
		return
	}
	reader := msgp.NewReader(net.Conn(bc.coordConn))
	for {
		msg, err := common.MessageFromDecoderMsgp(reader)
		if err == io.EOF {
			log.Warn("connection closed. Do failover")
			bc.doFailover()
			break
		}
		if err != nil {
			log.Warn(errors.Wrap(err, "Could not decode message"))
		}

		log.Infof("Got %T message %v from coordinator", msg, msg)
		switch m := msg.(type) {
		case *common.BrokerAssignmentMessage:
			bc.configureNewBroker(m)
			bc.coordConn.Close()
			return
		default:
			log.Infof("Got %T message %v from coordinator", m, m)
		}
	}
}

func (bc *BrokerConnection) sendCoordinator(m common.Sendable) error {
	bc.coordEncodeLock.Lock()
	if err := m.Encode(bc.coordEncoder); err != nil {
		bc.coordEncodeLock.Unlock()
		return errors.Wrap(err, "Could not encode message")
	}
	if err := bc.coordEncoder.Flush(); err != nil {
		bc.coordEncodeLock.Unlock()
		bc.connectCoordinator()
		return errors.Wrap(err, "Could not send message to coordinator")
	}
	bc.coordEncodeLock.Unlock()
	return nil
}
<|endoftext|>


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

package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/maruel/subcommands"
)

func cmdCheck() *subcommands.Command {
	return &subcommands.Command{
		UsageLine: "check <options>",
		ShortDesc: "checks that all the inputs are present and generates .isolated",
		LongDesc:  "",
		CommandRun: func() subcommands.CommandRun {
			c := checkRun{}
			c.commonFlags.Init()
			c.isolateFlags.Init(&c.Flags)
			return &c
		},
	}
}

type checkRun struct {
	commonFlags
	isolateFlags
}

func (c *checkRun) Parse(a subcommands.Application, args []string) error {
	if err := c.commonFlags.Parse(); err != nil {
		return err
	}
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	if err := c.isolateFlags.Parse(cwd, RequireIsolatedFile|RequireIsolateFile); err != nil {
		return err
	}
	if len(args) != 0 {
		return errors.New("position arguments not expected")
	}
	return nil
}

func (c *checkRun) main(a subcommands.Application, args []string) error {
	if !c.defaultFlags.Quiet {
		fmt.Printf("Isolate:   %s\n", c.Isolate)
		fmt.Printf("Isolated:  %s\n", c.Isolated)
		fmt.Printf("Blacklist: %s\n", c.Blacklist)
		fmt.Printf("Config:    %s\n", c.ConfigVariables)
		fmt.Printf("Path:      %s\n", c.PathVariables)
		fmt.Printf("Extra:     %s\n", c.ExtraVariables)
	}
	return errors.New("TODO")
}

func (c *checkRun) Run(a subcommands.Application, args []string, _ subcommands.Env) int {
	if err := c.Parse(a, args); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	cl, err := c.defaultFlags.StartTracing()
	if err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	defer cl.Close()
	if err := c.main(a, args); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<commit_msg>isolate: implement check<commit_after>// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"

	"github.com/maruel/subcommands"
	"go.chromium.org/luci/client/isolate"
	"go.chromium.org/luci/common/errors"
)

func cmdCheck() *subcommands.Command {
	return &subcommands.Command{
		UsageLine: "check <options>",
		ShortDesc: "checks that all the inputs are present",
		LongDesc:  "",
		CommandRun: func() subcommands.CommandRun {
			c := checkRun{}
			c.commonFlags.Init()
			c.isolateFlags.Init(&c.Flags)
			return &c
		},
	}
}

type checkRun struct {
	commonFlags
	isolateFlags
}

func (c *checkRun) Parse(a subcommands.Application, args []string) error {
	if err := c.commonFlags.Parse(); err != nil {
		return err
	}
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	if err := c.isolateFlags.Parse(cwd, RequireIsolatedFile|RequireIsolateFile); err != nil {
		return err
	}
	if len(args) != 0 {
		return errors.Reason("position arguments not expected").Err()
	}
	return nil
}

func (c *checkRun) main(a subcommands.Application, args []string) error {
	if !c.defaultFlags.Quiet {
		fmt.Printf("Isolate:   %s\n", c.Isolate)
		fmt.Printf("Isolated:  %s\n", c.Isolated)
		fmt.Printf("Blacklist: %s\n", c.Blacklist)
		fmt.Printf("Config:    %s\n", c.ConfigVariables)
		fmt.Printf("Path:      %s\n", c.PathVariables)
		fmt.Printf("Extra:     %s\n", c.ExtraVariables)
	}

	deps, _, _, err := isolate.ProcessIsolate(&c.ArchiveOptions)
	if err != nil {
		return errors.Annotate(err, "failed to process isolate").Err()
	}

	for _, dep := range deps {
		_, err := os.Stat(dep)
		if err != nil {
			return errors.Annotate(err, "failed to stat input file: %s", dep).Err()
		}
	}
	return nil
}

func (c *checkRun) Run(a subcommands.Application, args []string, _ subcommands.Env) int {
	if err := c.Parse(a, args); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	cl, err := c.defaultFlags.StartTracing()
	if err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	defer cl.Close()
	if err := c.main(a, args); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<|endoftext|>


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

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"google.golang.org/api/googleapi"

	"github.com/maruel/subcommands"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/api/swarming/swarming/v1"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/flag"
	"go.chromium.org/luci/common/system/signals"
)

func cmdBots(defaultAuthOpts auth.Options) *subcommands.Command {
	return &subcommands.Command{
		UsageLine: "bots <options>",
		ShortDesc: "lists bots",
		LongDesc:  "List bots matching the given options.",
		CommandRun: func() subcommands.CommandRun {
			r := &botsRun{}
			r.Init(defaultAuthOpts)
			return r
		},
	}
}

type botsRun struct {
	commonFlags
	outfile string
	mp      bool
	nomp    bool
	fields  []googleapi.Field
}

func (b *botsRun) Init(defaultAuthOpts auth.Options) {
	b.commonFlags.Init(defaultAuthOpts)

	b.Flags.StringVar(&b.outfile, "json", "", "Path to output JSON results. Implies quiet.")
	b.Flags.BoolVar(&b.mp, "mp", false, "Only fetch Machine Provider bots.")
	b.Flags.BoolVar(&b.nomp, "nomp", false, "Exclude Machine Provider bots.")
	b.Flags.Var(flag.FieldSlice(&b.fields), "field", "Fields to include in a partial response. May be repeated.")
}

func (b *botsRun) Parse() error {
	if err := b.commonFlags.Parse(); err != nil {
		return err
	}
	if b.mp && b.nomp {
		return errors.Reason("at most one of -mp and -nomp must be specified").Err()
	}
	if b.defaultFlags.Quiet && b.outfile == "" {
		return errors.Reason("specify -json when using -quiet").Err()
	}
	if b.outfile != "" {
		b.defaultFlags.Quiet = true
	}
	return nil
}

func (b *botsRun) main(a subcommands.Application) error {
	ctx, cancel := context.WithCancel(b.defaultFlags.MakeLoggingContext(os.Stderr))
	signals.HandleInterrupt(cancel)
	client, err := b.createAuthClient(ctx)
	if err != nil {
		return err
	}
	s, err := swarming.New(client)
	if err != nil {
		return err
	}
	s.BasePath = b.commonFlags.serverURL + "/_ah/api/swarming/v1/"
	call := s.Bots.List()
	if b.mp {
		call.IsMp("TRUE")
	} else if b.nomp {
		call.IsMp("FALSE")
	}
	// If no fields are specified, all fields will be returned. If any fields are
	// specified, ensure the cursor is specified so we can get subsequent pages.
	if len(b.fields) > 0 {
		b.fields = append(b.fields, "cursor")
	}
	call.Fields(b.fields...)
	// Keep calling as long as there's a cursor indicating more bots to list.
	// Create an empty array, so that if saved to b.outfile, it's an empty list,
	// not null.
	bots := []*swarming.SwarmingRpcsBotInfo{}
	for {
		result, err := call.Do()
		if err != nil {
			return err
		}
		bots = append(bots, result.Items...)
		if result.Cursor == "" {
			break
		}
		call.Cursor(result.Cursor)
	}
	if !b.defaultFlags.Quiet {
		j, err := json.MarshalIndent(bots, "", " ")
		if err != nil {
			return err
		}
		fmt.Printf("%s\n", j)
	}
	if b.outfile != "" {
		j, err := json.Marshal(bots)
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(b.outfile, j, 0644); err != nil {
			return err
		}
	}
	return nil
}

func (b *botsRun) Run(a subcommands.Application, args []string, _ subcommands.Env) int {
	if err := b.Parse(); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	cl, err := b.defaultFlags.StartTracing()
	if err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	defer cl.Close()
	if err := b.main(a); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<commit_msg>swarming: add dimension flag to bots subcommand<commit_after>// Copyright 2018 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"google.golang.org/api/googleapi"

	"github.com/maruel/subcommands"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/api/swarming/swarming/v1"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/flag"
	"go.chromium.org/luci/common/flag/stringmapflag"
	"go.chromium.org/luci/common/system/signals"
)

func cmdBots(defaultAuthOpts auth.Options) *subcommands.Command {
	return &subcommands.Command{
		UsageLine: "bots <options>",
		ShortDesc: "lists bots",
		LongDesc:  "List bots matching the given options.",
		CommandRun: func() subcommands.CommandRun {
			r := &botsRun{}
			r.Init(defaultAuthOpts)
			return r
		},
	}
}

type botsRun struct {
	commonFlags
	outfile    string
	mp         bool
	nomp       bool
	dimensions stringmapflag.Value
	fields     []googleapi.Field
}

func (b *botsRun) Init(defaultAuthOpts auth.Options) {
	b.commonFlags.Init(defaultAuthOpts)

	b.Flags.StringVar(&b.outfile, "json", "", "Path to output JSON results. Implies quiet.")
	b.Flags.BoolVar(&b.mp, "mp", false, "Only fetch Machine Provider bots.")
	b.Flags.BoolVar(&b.nomp, "nomp", false, "Exclude Machine Provider bots.")
	b.Flags.Var(&b.dimensions, "dimension", "Dimension to select the right kind of bot. In the form of `key=value`")
	b.Flags.Var(flag.FieldSlice(&b.fields), "field", "Fields to include in a partial response. May be repeated.")

}

func (b *botsRun) Parse() error {
	if err := b.commonFlags.Parse(); err != nil {
		return err
	}
	if b.mp && b.nomp {
		return errors.Reason("at most one of -mp and -nomp must be specified").Err()
	}
	if b.defaultFlags.Quiet && b.outfile == "" {
		return errors.Reason("specify -json when using -quiet").Err()
	}
	if b.outfile != "" {
		b.defaultFlags.Quiet = true
	}
	return nil
}

func (b *botsRun) main(a subcommands.Application) error {
	ctx, cancel := context.WithCancel(b.defaultFlags.MakeLoggingContext(os.Stderr))
	signals.HandleInterrupt(cancel)
	client, err := b.createAuthClient(ctx)
	if err != nil {
		return err
	}
	s, err := swarming.New(client)
	if err != nil {
		return err
	}
	s.BasePath = b.commonFlags.serverURL + "/_ah/api/swarming/v1/"
	call := s.Bots.List()
	if b.mp {
		call.IsMp("TRUE")
	} else if b.nomp {
		call.IsMp("FALSE")
	}

	var dims []string
	for k, v := range b.dimensions {
		dims = append(dims, k+":"+v)
	}
	call.Dimensions(dims...)

	// If no fields are specified, all fields will be returned. If any fields are
	// specified, ensure the cursor is specified so we can get subsequent pages.
	if len(b.fields) > 0 {
		b.fields = append(b.fields, "cursor")
	}
	call.Fields(b.fields...)
	// Keep calling as long as there's a cursor indicating more bots to list.
	// Create an empty array, so that if saved to b.outfile, it's an empty list,
	// not null.
	bots := []*swarming.SwarmingRpcsBotInfo{}
	for {
		result, err := call.Do()
		if err != nil {
			return err
		}
		bots = append(bots, result.Items...)
		if result.Cursor == "" {
			break
		}
		call.Cursor(result.Cursor)
	}
	if !b.defaultFlags.Quiet {
		j, err := json.MarshalIndent(bots, "", " ")
		if err != nil {
			return err
		}
		fmt.Printf("%s\n", j)
	}
	if b.outfile != "" {
		j, err := json.Marshal(bots)
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(b.outfile, j, 0644); err != nil {
			return err
		}
	}
	return nil
}

func (b *botsRun) Run(a subcommands.Application, args []string, _ subcommands.Env) int {
	if err := b.Parse(); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	cl, err := b.defaultFlags.StartTracing()
	if err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	defer cl.Close()
	if err := b.main(a); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<|endoftext|>


<commit_before>// +build integrationTest

package esvector

import (
	"testing"

	"github.com/elastic/go-elasticsearch/v5"
	"github.com/semi-technologies/weaviate/entities/models"
	"github.com/semi-technologies/weaviate/entities/schema"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
)

// this is not a test suite in itself, but it makes sure that other test suites
// which depend no a specific caching state are run sequentially in a specific
// order, so that they don't interefere with each other

func Test_OrchestrateCaching(t *testing.T) {

	// requires caching to be stopped initially, starts caching at some point in
	// the test suite, stops caching in the end to clean up
	testEsVectorCache(t)

	client, err := elasticsearch.NewClient(elasticsearch.Config{
		Addresses: []string{"http://localhost:9201"},
	})
	require.Nil(t, err)
	schemaGetter := &fakeSchemaGetter{schema: parkingGaragesSchema()}
	logger := logrus.New()
	repo := NewRepo(client, logger, schemaGetter, 2)
	waitForEsToBeReady(t, repo)
	requestCounter := &testCounter{}
	repo.requestCounter = requestCounter
	migrator := NewMigrator(repo)

	// cache indexing not started yet, as the below test suite asserts on both state
	t.Run("test multiple cross ref types", testMultipleCrossRefTypes(repo, migrator))

	// cache indexing has since been started in the previous test suite
	t.Run("filtering on refprops", testFilteringOnRefProps(repo))

	t.Run("updating cached ref props", testUpdatingCachedRefProps(repo, parkingGaragesSchema()))

	// explicitly stop cache indexing to clean up
	repo.StopCacheIndexing()
}

func parkingGaragesSchema() schema.Schema {
	return schema.Schema{
		Things: &models.Schema{
			Classes: []*models.Class{
				&models.Class{
					Class: "MultiRefParkingGarage",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "location",
							DataType: []string{string(schema.DataTypeGeoCoordinates)},
						},
					},
				},
				&models.Class{
					Class: "MultiRefParkingLot",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
					},
				},
				&models.Class{
					Class: "MultiRefCar",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "parkedAt",
							DataType: []string{"MultiRefParkingGarage", "MultiRefParkingLot"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefDriver",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "drives",
							DataType: []string{"MultiRefCar"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefPerson",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "friendsWith",
							DataType: []string{"MultiRefDriver"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefSociety",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "hasMembers",
							DataType: []string{"MultiRefPerson"},
						},
					},
				},
			},
		},
	}
}
<commit_msg>🤖 bleep bloop => auto updated Weaviate<commit_after>//                           _       _
// __      _____  __ ___   ___  __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
//  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
//   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
//  Copyright © 2016 - 2019 SeMI Holding B.V. (registered @ Dutch Chamber of Commerce no 75221632). All rights reserved.
//  LICENSE WEAVIATE OPEN SOURCE: https://www.semi.technology/playbook/playbook/contract-weaviate-OSS.html
//  LICENSE WEAVIATE ENTERPRISE: https://www.semi.technology/playbook/contract-weaviate-enterprise.html
//  CONCEPT: Bob van Luijt (@bobvanluijt)
//  CONTACT: hello@semi.technology
//

// +build integrationTest

package esvector

import (
	"testing"

	"github.com/elastic/go-elasticsearch/v5"
	"github.com/semi-technologies/weaviate/entities/models"
	"github.com/semi-technologies/weaviate/entities/schema"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
)

// this is not a test suite in itself, but it makes sure that other test suites
// which depend no a specific caching state are run sequentially in a specific
// order, so that they don't interefere with each other

func Test_OrchestrateCaching(t *testing.T) {

	// requires caching to be stopped initially, starts caching at some point in
	// the test suite, stops caching in the end to clean up
	testEsVectorCache(t)

	client, err := elasticsearch.NewClient(elasticsearch.Config{
		Addresses: []string{"http://localhost:9201"},
	})
	require.Nil(t, err)
	schemaGetter := &fakeSchemaGetter{schema: parkingGaragesSchema()}
	logger := logrus.New()
	repo := NewRepo(client, logger, schemaGetter, 2)
	waitForEsToBeReady(t, repo)
	requestCounter := &testCounter{}
	repo.requestCounter = requestCounter
	migrator := NewMigrator(repo)

	// cache indexing not started yet, as the below test suite asserts on both state
	t.Run("test multiple cross ref types", testMultipleCrossRefTypes(repo, migrator))

	// cache indexing has since been started in the previous test suite
	t.Run("filtering on refprops", testFilteringOnRefProps(repo))

	t.Run("updating cached ref props", testUpdatingCachedRefProps(repo, parkingGaragesSchema()))

	// explicitly stop cache indexing to clean up
	repo.StopCacheIndexing()
}

func parkingGaragesSchema() schema.Schema {
	return schema.Schema{
		Things: &models.Schema{
			Classes: []*models.Class{
				&models.Class{
					Class: "MultiRefParkingGarage",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "location",
							DataType: []string{string(schema.DataTypeGeoCoordinates)},
						},
					},
				},
				&models.Class{
					Class: "MultiRefParkingLot",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
					},
				},
				&models.Class{
					Class: "MultiRefCar",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "parkedAt",
							DataType: []string{"MultiRefParkingGarage", "MultiRefParkingLot"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefDriver",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "drives",
							DataType: []string{"MultiRefCar"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefPerson",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "friendsWith",
							DataType: []string{"MultiRefDriver"},
						},
					},
				},
				&models.Class{
					Class: "MultiRefSociety",
					Properties: []*models.Property{
						&models.Property{
							Name:     "name",
							DataType: []string{string(schema.DataTypeString)},
						},
						&models.Property{
							Name:     "hasMembers",
							DataType: []string{"MultiRefPerson"},
						},
					},
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package onedriveclient

import (
	"encoding/json"
	"github.com/koofr/go-httpclient"
	"net/http"
	"net/url"
	"time"
)

const (
	InvalidGrantError = "invalid_grant"
)

type RefreshResp struct {
	ExpiresIn    int64  `json:"expires_in"`
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
}

type RefreshRespError struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

type OneDriveAuth struct {
	ClientId     string
	ClientSecret string
	RedirectUri  string
	AccessToken  string
	RefreshToken string
	ExpiresAt    time.Time
}

func (a *OneDriveAuth) ValidToken() (token string, err error) {
	if time.Now().Unix() > a.ExpiresAt.Unix() {
		data := url.Values{}
		data.Set("grant_type", "refresh_token")
		data.Set("client_id", a.ClientId)
		data.Set("client_secret", a.ClientSecret)
		data.Set("redirect_uri", a.RedirectUri)
		data.Set("refresh_token", a.RefreshToken)

		var respVal RefreshResp

		_, err = httpclient.DefaultClient.Request(&httpclient.RequestData{
			Method:         "POST",
			FullURL:        "https://login.live.com/oauth20_token.srf",
			ExpectedStatus: []int{http.StatusOK},
			ReqEncoding:    httpclient.EncodingForm,
			ReqValue:       data,
			RespEncoding:   httpclient.EncodingJSON,
			RespValue:      &respVal,
		})

		if err != nil {
			err = HandleError(err)

			if ode, ok := IsOneDriveError(err); ok {
				refreshErr := &RefreshRespError{}
				if jsonErr := json.Unmarshal([]byte(ode.Err.Message), &refreshErr); jsonErr == nil {
					ode.Err.Code = refreshErr.Error
					ode.Err.Message = refreshErr.ErrorDescription
				}
			}

			return "", err
		}

		a.AccessToken = respVal.AccessToken
		a.RefreshToken = respVal.RefreshToken
		a.ExpiresAt = time.Now().Add(time.Duration(respVal.ExpiresIn) * time.Second)
	}

	token = a.AccessToken

	return token, nil
}
<commit_msg>Add refresh token callback<commit_after>package onedriveclient

import (
	"encoding/json"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/koofr/go-httpclient"
)

const (
	InvalidGrantError = "invalid_grant"
)

type RefreshResp struct {
	ExpiresIn    int64  `json:"expires_in"`
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
}

type RefreshRespError struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

type OneDriveAuth struct {
	ClientId       string
	ClientSecret   string
	RedirectUri    string
	AccessToken    string
	RefreshToken   string
	ExpiresAt      time.Time
	OnTokenRefresh func()

	mutex sync.Mutex
}

func (a *OneDriveAuth) ValidToken() (token string, err error) {
	if time.Now().Unix() > a.ExpiresAt.Unix() {
		err = a.UpdateRefreshToken()
		if err != nil {
			return "", err
		}
	}

	token = a.AccessToken

	return token, nil
}

func (a *OneDriveAuth) UpdateRefreshToken() (err error) {
	a.mutex.Lock()
	defer a.mutex.Unlock()

	data := url.Values{}
	data.Set("grant_type", "refresh_token")
	data.Set("client_id", a.ClientId)
	data.Set("client_secret", a.ClientSecret)
	data.Set("redirect_uri", a.RedirectUri)
	data.Set("refresh_token", a.RefreshToken)

	var respVal RefreshResp

	_, err = httpclient.DefaultClient.Request(&httpclient.RequestData{
		Method:         "POST",
		FullURL:        "https://login.live.com/oauth20_token.srf",
		ExpectedStatus: []int{http.StatusOK},
		ReqEncoding:    httpclient.EncodingForm,
		ReqValue:       data,
		RespEncoding:   httpclient.EncodingJSON,
		RespValue:      &respVal,
	})

	if err != nil {
		err = HandleError(err)

		if ode, ok := IsOneDriveError(err); ok {
			refreshErr := &RefreshRespError{}
			if jsonErr := json.Unmarshal([]byte(ode.Err.Message), &refreshErr); jsonErr == nil {
				ode.Err.Code = refreshErr.Error
				ode.Err.Message = refreshErr.ErrorDescription
			}
		}

		return err
	}

	a.AccessToken = respVal.AccessToken
	a.RefreshToken = respVal.RefreshToken
	a.ExpiresAt = time.Now().Add(time.Duration(respVal.ExpiresIn) * time.Second)

	if a.OnTokenRefresh != nil {
		a.OnTokenRefresh()
	}

	return nil
}
<|endoftext|>


<commit_before>package ginAuth

// this package is a secure cookie authentication middleware for the the Gin Web Framework
// https://github.com/gin-gonic/gin

import (
	"github.com/gin-gonic/gin"
	"github.com/astaxie/beego/config"
	"github.com/gorilla/securecookie"
	"time"
	"strconv"
	"strings"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"io"
	"net/http"
)

const VERSION = "0.0.1"

// set our global package variables
var (
	CookieName string                       // the name of the cookie that will be used, default: "token"
	ConfigPath string                       // path to config file, default: ""
	ConfigType string                       // type of config file, default: "ini"
	Prefix string                           // the key in ctx.Keys[] to use, default: ""
	HashKey []byte                          // hash key for securecookie
	BlockKey []byte                         // block key for securecookie
	Expiration int64                        // time until the cookie expires in seconds, default: 604800
	Unauthorized func(ctx *gin.Context)     // function called if user is not authorized
	Authorized func(ctx *gin.Context)       // function called if user is authorized
	SecureCookie *securecookie.SecureCookie // global secure cookie object
)

func init() {

	ConfigPath = ""
	ConfigType = "ini"
	CookieName = "token"
	Prefix = ""
	Expiration = 604800 // 7 days

}

// gin middleware handler
// call this on your groups that require authentication
func Use(ctx *gin.Context) {

	err := Check(ctx)

	if err == nil {

		loggedIn, _ := ctx.Get(Prefix + "loggedIn")

		if loggedIn == true {

			if Authorized != nil {
				Authorized(ctx)
			}

		} else {

			if Unauthorized != nil {
				Unauthorized(ctx)
			}

		}

	}

}

// this function loads your specified configuration file and it's values
func LoadConfig() error {

	if ConfigPath != "" {
		conf, err := config.NewConfig(ConfigType, ConfigPath)
		if err != nil {

			return err
		}

		if cookiename := conf.String("cookiename"); cookiename != "" {
			CookieName = cookiename
		}

		if prefix := conf.String("prefix"); prefix != "" {
			Prefix = prefix
		}

		if hashkey := conf.String("hashkey"); hashkey != "" {

			val, err := hex.DecodeString(hashkey)
			if err != nil {
				return err
			}

			HashKey = val
		}

		if blockkey := conf.String("blockkey"); blockkey != "" {

			val, err := hex.DecodeString(blockkey)
			if err != nil {
				return err
			}

			BlockKey = val

		}

		if expiration := conf.String("expiration"); expiration != "" {

			val, err := strconv.ParseInt(expiration, 10, 64)
			if err != nil {
				return err
			}

			Expiration = val

		}
	}

	return nil
}

// a private function that simply saves the log in status of the user to the current context
func saveLogin(ctx *gin.Context, status bool) {
	ctx.Set(Prefix+"loggedIn", status)
}

// a private function that returns the ip from the current request
func ip(ctx *gin.Context) string {
	return strings.Split(ctx.Req.RemoteAddr, ":")[0]
}

// checks for our token cookie, decodes it, and determines if it is valid
// the encrypted cookie data set in Login() will be set to the current context as well
func Check(ctx *gin.Context) error {

	// get the encrypted cookie value
	cookie, err := ctx.Req.Cookie(CookieName)

	if err == nil {

		data := make(map[string]string)

		SecureCookie = securecookie.New(HashKey, BlockKey)
		if err := SecureCookie.Decode(CookieName, cookie.Value, &data); err == nil {

			// save the login cookie data to the context
			ctx.Set(Prefix+"cookieData", data)

			hash := hashHeader(ctx)

			expiration, err := strconv.ParseInt(data["expiration"], 10, 64)
			if err != nil {
				return err
			}

			if hash == data["hash"] && ip(ctx) == data["ip"] && time.Now().Before(time.Unix(expiration, 0)) {

				saveLogin(ctx, true)

			} else {
				//call the full logout because it'll remove the cookie as well
				Logout(ctx)
			}

		} else {
			return err
		}

	}

	return nil
}

// handles the login process
// the first param is a map of strings that will be added to the cookie data before encryption and will be
// able to be recovered when Check() is called
func Login(ctx *gin.Context, extra map[string]string) error {

	data := make(map[string]string)

	for key, value := range extra {

		if key == "ip" || key == "hash" || key == "experation" {
			return errors.New("The key '" + key + "' is reserved.")
		}

		data[key] = value
	}

	// our current time + our expiration time, converted to a unix time stamp
	data["expiration"] = strconv.FormatInt(time.Now().Add(time.Duration(Expiration)*time.Second).Unix(), 10)
	data["ip"] = ip(ctx)
	data["hash"] = hashHeader(ctx)

	// encode our cookie data securely
	SecureCookie = securecookie.New(HashKey, BlockKey)
	if encoded, err := SecureCookie.Encode(CookieName, data); err == nil {

		//set our cookie
		cookie := http.Cookie{Name: CookieName, Value: encoded, Path: "/", MaxAge: int(Expiration)}
		http.SetCookie(ctx.Writer, &cookie)

	} else {
		return err
	}

	return nil
}

// removes our token cookie, sets the context to: not logged in
func Logout(ctx *gin.Context) {

	cookie := http.Cookie{Name: CookieName, Path: "/", MaxAge: -1}
	http.SetCookie(ctx.Writer, &cookie)
	saveLogin(ctx, false)

}

// this function returns and md5 hash (string) for a few common request headers
func hashHeader(ctx *gin.Context) string {

	h := md5.New()

	io.WriteString(h, ctx.Req.Header.Get("User-Agent"))
	io.WriteString(h, ctx.Req.Header.Get("Accept-Encoding"))
	io.WriteString(h, ctx.Req.Header.Get("Accept-Language"))
	io.WriteString(h, ctx.Req.Header.Get("Host"))

	return hex.EncodeToString(h.Sum(nil))

}
<commit_msg>removed certain headers that where either useless or causing issues in chrome<commit_after>package ginAuth

// this package is a secure cookie authentication middleware for the the Gin Web Framework
// https://github.com/gin-gonic/gin

import (
	"github.com/gin-gonic/gin"
	"github.com/astaxie/beego/config"
	"github.com/gorilla/securecookie"
	"time"
	"strconv"
	"strings"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"io"
	"net/http"
)

const VERSION = "0.0.1"

// set our global package variables
var (
	CookieName string                       // the name of the cookie that will be used, default: "token"
	ConfigPath string                       // path to config file, default: ""
	ConfigType string                       // type of config file, default: "ini"
	Prefix string                           // the key in ctx.Keys[] to use, default: ""
	HashKey []byte                          // hash key for securecookie
	BlockKey []byte                         // block key for securecookie
	Expiration int64                        // time until the cookie expires in seconds, default: 604800
	Unauthorized func(ctx *gin.Context)     // function called if user is not authorized
	Authorized func(ctx *gin.Context)       // function called if user is authorized
	SecureCookie *securecookie.SecureCookie // global secure cookie object
)

func init() {

	ConfigPath = ""
	ConfigType = "ini"
	CookieName = "token"
	Prefix = ""
	Expiration = 604800 // 7 days

}

// gin middleware handler
// call this on your groups that require authentication
func Use(ctx *gin.Context) {

	err := Check(ctx)

	if err == nil {

		loggedIn, _ := ctx.Get(Prefix + "loggedIn")

		if loggedIn == true {

			if Authorized != nil {
				Authorized(ctx)
			}

		} else {

			if Unauthorized != nil {
				Unauthorized(ctx)
			}

		}

	}

}

// this function loads your specified configuration file and it's values
func LoadConfig() error {

	if ConfigPath != "" {
		conf, err := config.NewConfig(ConfigType, ConfigPath)
		if err != nil {

			return err
		}

		if cookiename := conf.String("cookiename"); cookiename != "" {
			CookieName = cookiename
		}

		if prefix := conf.String("prefix"); prefix != "" {
			Prefix = prefix
		}

		if hashkey := conf.String("hashkey"); hashkey != "" {

			val, err := hex.DecodeString(hashkey)
			if err != nil {
				return err
			}

			HashKey = val
		}

		if blockkey := conf.String("blockkey"); blockkey != "" {

			val, err := hex.DecodeString(blockkey)
			if err != nil {
				return err
			}

			BlockKey = val

		}

		if expiration := conf.String("expiration"); expiration != "" {

			val, err := strconv.ParseInt(expiration, 10, 64)
			if err != nil {
				return err
			}

			Expiration = val

		}
	}

	return nil
}

// a private function that simply saves the log in status of the user to the current context
func saveLogin(ctx *gin.Context, status bool) {
	ctx.Set(Prefix+"loggedIn", status)
}

// a private function that returns the ip from the current request
func ip(ctx *gin.Context) string {
	return strings.Split(ctx.Req.RemoteAddr, ":")[0]
}

// checks for our token cookie, decodes it, and determines if it is valid
// the encrypted cookie data set in Login() will be set to the current context as well
func Check(ctx *gin.Context) error {

	// get the encrypted cookie value
	cookie, err := ctx.Req.Cookie(CookieName)

	if err == nil {

		data := make(map[string]string)

		SecureCookie = securecookie.New(HashKey, BlockKey)
		if err := SecureCookie.Decode(CookieName, cookie.Value, &data); err == nil {

			// save the login cookie data to the context
			ctx.Set(Prefix+"cookieData", data)

			hash := hashHeader(ctx)

			expiration, err := strconv.ParseInt(data["expiration"], 10, 64)
			if err != nil {
				return err
			}

			if hash == data["hash"] && ip(ctx) == data["ip"] && time.Now().Before(time.Unix(expiration, 0)) {

				saveLogin(ctx, true)

			} else {
				//call the full logout because it'll remove the cookie as well
				Logout(ctx)
			}

		} else {
			return err
		}

	}

	return nil
}

// handles the login process
// the first param is a map of strings that will be added to the cookie data before encryption and will be
// able to be recovered when Check() is called
func Login(ctx *gin.Context, extra map[string]string) error {

	data := make(map[string]string)

	for key, value := range extra {

		if key == "ip" || key == "hash" || key == "experation" {
			return errors.New("The key '" + key + "' is reserved.")
		}

		data[key] = value
	}

	// our current time + our expiration time, converted to a unix time stamp
	data["expiration"] = strconv.FormatInt(time.Now().Add(time.Duration(Expiration)*time.Second).Unix(), 10)
	data["ip"] = ip(ctx)
	data["hash"] = hashHeader(ctx)

	// encode our cookie data securely
	SecureCookie = securecookie.New(HashKey, BlockKey)
	if encoded, err := SecureCookie.Encode(CookieName, data); err == nil {

		//set our cookie
		cookie := http.Cookie{Name: CookieName, Value: encoded, Path: "/", MaxAge: int(Expiration)}
		http.SetCookie(ctx.Writer, &cookie)

	} else {
		return err
	}

	return nil
}

// removes our token cookie, sets the context to: not logged in
func Logout(ctx *gin.Context) {

	cookie := http.Cookie{Name: CookieName, Path: "/", MaxAge: -1}
	http.SetCookie(ctx.Writer, &cookie)
	saveLogin(ctx, false)

}

// this function returns and md5 hash (string) for a few common request headers
func hashHeader(ctx *gin.Context) string {

	h := md5.New()

	io.WriteString(h, ctx.Req.Header.Get("User-Agent"))
	io.WriteString(h, ctx.Req.Header.Get("Accept-Language"))

	return hex.EncodeToString(h.Sum(nil))

}
<|endoftext|>


<commit_before>package qtypes

import (
	"time"
)

const (
	version = "0.3.1"
)

type Base struct {
	BaseVersion string
	Time		time.Time
	SourceID	int
	SourcePath	[]string
}

func NewBase(src string) Base {
	return NewTimedBase(src, time.Now())
}

func NewTimedBase(src string, t time.Time) Base {
	return Base {
		BaseVersion: version,
		Time: t,
		SourceID: 0,
		SourcePath: []string{src},
	}
}

func (b *Base) GetTimeRFC() string {
	return b.Time.Format("2006-01-02T15:04:05.999999-07:00")
}

func (b *Base) GetTimeUnix() int64 {
	return b.Time.Unix()
}

func (b *Base) GetTimeUnixNano() int64 {
	return b.Time.UnixNano()
}

func (b *Base) AppendSource(src string) {
	b.SourcePath = append(b.SourcePath, src)
}

func (b *Base) IsLastSource(src string) bool {
	return b.SourcePath[len(b.SourcePath)-1] == src
}

func (b *Base) InputsMatch(inputs []string) bool {
	for _, inp := range inputs {
		if b.IsLastSource(inp) {
			return true
		}

	}
	return false
}<commit_msg>add SourceSuccess to Base<commit_after>package qtypes

import (
	"time"
)

const (
	version = "0.3.1"
)

type Base struct {
	BaseVersion string
	Time			time.Time
	SourceID		int
	SourcePath		[]string
	SourceSuccess 	bool
}

func NewBase(src string) Base {
	return NewTimedBase(src, time.Now())
}

func NewTimedBase(src string, t time.Time) Base {
	return Base {
		BaseVersion: version,
		Time: t,
		SourceID: 0,
		SourcePath: []string{src},
		SourceSuccess: true,
	}
}

func (b *Base) GetTimeRFC() string {
	return b.Time.Format("2006-01-02T15:04:05.999999-07:00")
}

func (b *Base) GetTimeUnix() int64 {
	return b.Time.Unix()
}

func (b *Base) GetTimeUnixNano() int64 {
	return b.Time.UnixNano()
}

func (b *Base) AppendSource(src string) {
	b.SourcePath = append(b.SourcePath, src)
}

func (b *Base) IsLastSource(src string) bool {
	return b.SourcePath[len(b.SourcePath)-1] == src
}

func (b *Base) InputsMatch(inputs []string) bool {
	for _, inp := range inputs {
		if b.IsLastSource(inp) {
			return true
		}

	}
	return false
}<|endoftext|>


<commit_before>// Beam is a protocol and library for service-oriented communication,
// with an emphasis on real-world patterns, simplicity and not reinventing the wheel.
//
// See http://github.com/dotcloud/beam.
//
// github.com/dotcloud/beam/common holds functions and data structures common to the
// client and server implementations. It is typically not used directly by external
// programs.

package beam

import (
	_ "github.com/dotcloud/go-redis-server"
	"io"
)

type DB interface {

}

type Streamer interface {
	// OpenRead returns a read-only interface to receive data on the stream <name>.
	// If the stream hasn't been open for read access before, it is advertised as such to the peer.
	OpenRead(name string) io.Reader

	// ReadFrom opens a read-only interface on the stream <name>, and copies data
	// to that interface from <src> until EOF or error.
	// The return value n is the number of bytes read.
	// Any error encountered during the write is also returned.
	ReadFrom(src io.Reader, name string) (int64, error)

	// OpenWrite returns a write-only interface to send data on the stream <name>.
	// If the stream hasn't been open for write access before, it is advertised as such to the peer.
	OpenWrite(name string) io.Writer

	// WriteTo opens a write-only interface on the stream <name>, and copies data
	// from that interface to <dst> until there's no more data to write or when an error occurs.
	// The return value n is the number of bytes written.
	// Any error encountered during the write is also returned.
	WriteTo(dst io.Writer, name string) (int64, error)

	// OpenReadWrite returns a read-write interface to send and receive on the stream <name>.
	// If the stream hasn't been open for read or write access before, it is advertised as such to the peer.
	OpenReadWrite(name string) io.ReadWriter

	// Close closes the stream <name>. All future reads will return io.EOF, and writes will return
	// io.ErrClosedPipe
	Close(name string)
}
<commit_msg>Streamer.Shutdown: graceful shutdown of a job's streams after cleanly draining them.<commit_after>// Beam is a protocol and library for service-oriented communication,
// with an emphasis on real-world patterns, simplicity and not reinventing the wheel.
//
// See http://github.com/dotcloud/beam.
//
// github.com/dotcloud/beam/common holds functions and data structures common to the
// client and server implementations. It is typically not used directly by external
// programs.

package beam

import (
	_ "github.com/dotcloud/go-redis-server"
	"io"
)

type DB interface {

}

type Streamer interface {
	// OpenRead returns a read-only interface to receive data on the stream <name>.
	// If the stream hasn't been open for read access before, it is advertised as such to the peer.
	OpenRead(name string) io.Reader

	// ReadFrom opens a read-only interface on the stream <name>, and copies data
	// to that interface from <src> until EOF or error.
	// The return value n is the number of bytes read.
	// Any error encountered during the write is also returned.
	ReadFrom(src io.Reader, name string) (int64, error)

	// OpenWrite returns a write-only interface to send data on the stream <name>.
	// If the stream hasn't been open for write access before, it is advertised as such to the peer.
	OpenWrite(name string) io.Writer

	// WriteTo opens a write-only interface on the stream <name>, and copies data
	// from that interface to <dst> until there's no more data to write or when an error occurs.
	// The return value n is the number of bytes written.
	// Any error encountered during the write is also returned.
	WriteTo(dst io.Writer, name string) (int64, error)

	// OpenReadWrite returns a read-write interface to send and receive on the stream <name>.
	// If the stream hasn't been open for read or write access before, it is advertised as such to the peer.
	OpenReadWrite(name string) io.ReadWriter

	// Close closes the stream <name>. All future reads will return io.EOF, and writes will return
	// io.ErrClosedPipe
	Close(name string)

	// Shutdown waits until all streams with read access are closed and
	// all WriteTo and ReadFrom operations are completed,
	// then it stops accepting remote messages for its streams,
	// then it returns.
	Shutdown() error
}
<|endoftext|>


<commit_before>package libp2ptls

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"errors"
	"fmt"
	"math/big"
	"time"

	"golang.org/x/sys/cpu"

	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
)

const certValidityPeriod = 100 * 365 * 24 * time.Hour // ~100 years
const certificatePrefix = "libp2p-tls-handshake:"
const alpn string = "libp2p"

var extensionID = getPrefixedExtensionID([]int{1, 1})
var extensionCritical bool // so we can mark the extension critical in tests

type signedKey struct {
	PubKey    []byte
	Signature []byte
}

// Identity is used to secure connections
type Identity struct {
	config tls.Config
}

// NewIdentity creates a new identity
func NewIdentity(privKey ic.PrivKey) (*Identity, error) {
	cert, err := keyToCertificate(privKey)
	if err != nil {
		return nil, err
	}
	return &Identity{
		config: tls.Config{
			MinVersion:               tls.VersionTLS13,
			PreferServerCipherSuites: preferServerCipherSuites(),
			InsecureSkipVerify:       true, // This is not insecure here. We will verify the cert chain ourselves.
			ClientAuth:               tls.RequireAnyClientCert,
			Certificates:             []tls.Certificate{*cert},
			VerifyPeerCertificate: func(_ [][]byte, _ [][]*x509.Certificate) error {
				panic("tls config not specialized for peer")
			},
			NextProtos:             []string{alpn},
			SessionTicketsDisabled: true,
		},
	}, nil
}

// ConfigForPeer creates a new single-use tls.Config that verifies the peer's
// certificate chain and returns the peer's public key via the channel. If the
// peer ID is empty, the returned config will accept any peer.
//
// It should be used to create a new tls.Config before securing either an
// incoming or outgoing connection.
func (i *Identity) ConfigForPeer(remote peer.ID) (*tls.Config, <-chan ic.PubKey) {
	keyCh := make(chan ic.PubKey, 1)
	// We need to check the peer ID in the VerifyPeerCertificate callback.
	// The tls.Config it is also used for listening, and we might also have concurrent dials.
	// Clone it so we can check for the specific peer ID we're dialing here.
	conf := i.config.Clone()
	// We're using InsecureSkipVerify, so the verifiedChains parameter will always be empty.
	// We need to parse the certificates ourselves from the raw certs.
	conf.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		defer close(keyCh)

		chain := make([]*x509.Certificate, len(rawCerts))
		for i := 0; i < len(rawCerts); i++ {
			cert, err := x509.ParseCertificate(rawCerts[i])
			if err != nil {
				return err
			}
			chain[i] = cert
		}

		pubKey, err := PubKeyFromCertChain(chain)
		if err != nil {
			return err
		}
		if remote != "" && !remote.MatchesPublicKey(pubKey) {
			peerID, err := peer.IDFromPublicKey(pubKey)
			if err != nil {
				peerID = peer.ID(fmt.Sprintf("(not determined: %s)", err.Error()))
			}
			return fmt.Errorf("peer IDs don't match: expected %s, got %s", remote, peerID)
		}
		keyCh <- pubKey
		return nil
	}
	return conf, keyCh
}

// PubKeyFromCertChain verifies the certificate chain and extract the remote's public key.
func PubKeyFromCertChain(chain []*x509.Certificate) (ic.PubKey, error) {
	if len(chain) != 1 {
		return nil, errors.New("expected one certificates in the chain")
	}
	cert := chain[0]
	pool := x509.NewCertPool()
	pool.AddCert(cert)
	var found bool
	var keyExt pkix.Extension
	// find the libp2p key extension, skipping all unknown extensions
	for _, ext := range cert.Extensions {
		if extensionIDEqual(ext.Id, extensionID) {
			keyExt = ext
			found = true
			for i, oident := range cert.UnhandledCriticalExtensions {
				if oident.Equal(ext.Id) {
					// delete the extension from UnhandledCriticalExtensions
					cert.UnhandledCriticalExtensions = append(cert.UnhandledCriticalExtensions[:i], cert.UnhandledCriticalExtensions[i+1:]...)
					break
				}
			}
			break
		}
	}
	if !found {
		return nil, errors.New("expected certificate to contain the key extension")
	}
	if _, err := cert.Verify(x509.VerifyOptions{Roots: pool}); err != nil {
		// If we return an x509 error here, it will be sent on the wire.
		// Wrap the error to avoid that.
		return nil, fmt.Errorf("certificate verification failed: %s", err)
	}

	var sk signedKey
	if _, err := asn1.Unmarshal(keyExt.Value, &sk); err != nil {
		return nil, fmt.Errorf("unmarshalling signed certificate failed: %s", err)
	}
	pubKey, err := ic.UnmarshalPublicKey(sk.PubKey)
	if err != nil {
		return nil, fmt.Errorf("unmarshalling public key failed: %s", err)
	}
	certKeyPub, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
	if err != nil {
		return nil, err
	}
	valid, err := pubKey.Verify(append([]byte(certificatePrefix), certKeyPub...), sk.Signature)
	if err != nil {
		return nil, fmt.Errorf("signature verification failed: %s", err)
	}
	if !valid {
		return nil, errors.New("signature invalid")
	}
	return pubKey, nil
}

func keyToCertificate(sk ic.PrivKey) (*tls.Certificate, error) {
	certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	keyBytes, err := ic.MarshalPublicKey(sk.GetPublic())
	if err != nil {
		return nil, err
	}
	certKeyPub, err := x509.MarshalPKIXPublicKey(certKey.Public())
	if err != nil {
		return nil, err
	}
	signature, err := sk.Sign(append([]byte(certificatePrefix), certKeyPub...))
	if err != nil {
		return nil, err
	}
	value, err := asn1.Marshal(signedKey{
		PubKey:    keyBytes,
		Signature: signature,
	})
	if err != nil {
		return nil, err
	}

	bigNum := big.NewInt(1 << 62)
	sn, err := rand.Int(rand.Reader, bigNum)
	if err != nil {
		return nil, err
	}
	subjectSN, err := rand.Int(rand.Reader, bigNum)
	if err != nil {
		return nil, err
	}
	tmpl := &x509.Certificate{
		SerialNumber: sn,
		NotBefore:    time.Time{},
		NotAfter:     time.Now().Add(certValidityPeriod),
		// According to RFC 3280, the issuer field must be set,
		// see https://datatracker.ietf.org/doc/html/rfc3280#section-4.1.2.4.
		Subject: pkix.Name{SerialNumber: subjectSN.String()},
		// after calling CreateCertificate, these will end up in Certificate.Extensions
		ExtraExtensions: []pkix.Extension{
			{Id: extensionID, Critical: extensionCritical, Value: value},
		},
	}
	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, certKey.Public(), certKey)
	if err != nil {
		return nil, err
	}
	return &tls.Certificate{
		Certificate: [][]byte{certDER},
		PrivateKey:  certKey,
	}, nil
}

// We want nodes without AES hardware (e.g. ARM) support to always use ChaCha.
// Only if both nodes have AES hardware support (e.g. x86), AES should be used.
// x86->x86: AES, ARM->x86: ChaCha, x86->ARM: ChaCha and ARM->ARM: Chacha
// This function returns true if we don't have AES hardware support, and false otherwise.
// Thus, ARM servers will always use their own cipher suite preferences (ChaCha first),
// and x86 servers will aways use the client's cipher suite preferences.
func preferServerCipherSuites() bool {
	// Copied from the Go TLS implementation.

	// Check the cpu flags for each platform that has optimized GCM implementations.
	// Worst case, these variables will just all be false.
	var (
		hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
		hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
		// Keep in sync with crypto/aes/cipher_s390x.go.
		hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)

		hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
	)
	return !hasGCMAsm
}
<commit_msg>set an actual NotBefore time on the certificate<commit_after>package libp2ptls

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"errors"
	"fmt"
	"math/big"
	"time"

	"golang.org/x/sys/cpu"

	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
)

const certValidityPeriod = 100 * 365 * 24 * time.Hour // ~100 years
const certificatePrefix = "libp2p-tls-handshake:"
const alpn string = "libp2p"

var extensionID = getPrefixedExtensionID([]int{1, 1})
var extensionCritical bool // so we can mark the extension critical in tests

type signedKey struct {
	PubKey    []byte
	Signature []byte
}

// Identity is used to secure connections
type Identity struct {
	config tls.Config
}

// NewIdentity creates a new identity
func NewIdentity(privKey ic.PrivKey) (*Identity, error) {
	cert, err := keyToCertificate(privKey)
	if err != nil {
		return nil, err
	}
	return &Identity{
		config: tls.Config{
			MinVersion:               tls.VersionTLS13,
			PreferServerCipherSuites: preferServerCipherSuites(),
			InsecureSkipVerify:       true, // This is not insecure here. We will verify the cert chain ourselves.
			ClientAuth:               tls.RequireAnyClientCert,
			Certificates:             []tls.Certificate{*cert},
			VerifyPeerCertificate: func(_ [][]byte, _ [][]*x509.Certificate) error {
				panic("tls config not specialized for peer")
			},
			NextProtos:             []string{alpn},
			SessionTicketsDisabled: true,
		},
	}, nil
}

// ConfigForPeer creates a new single-use tls.Config that verifies the peer's
// certificate chain and returns the peer's public key via the channel. If the
// peer ID is empty, the returned config will accept any peer.
//
// It should be used to create a new tls.Config before securing either an
// incoming or outgoing connection.
func (i *Identity) ConfigForPeer(remote peer.ID) (*tls.Config, <-chan ic.PubKey) {
	keyCh := make(chan ic.PubKey, 1)
	// We need to check the peer ID in the VerifyPeerCertificate callback.
	// The tls.Config it is also used for listening, and we might also have concurrent dials.
	// Clone it so we can check for the specific peer ID we're dialing here.
	conf := i.config.Clone()
	// We're using InsecureSkipVerify, so the verifiedChains parameter will always be empty.
	// We need to parse the certificates ourselves from the raw certs.
	conf.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		defer close(keyCh)

		chain := make([]*x509.Certificate, len(rawCerts))
		for i := 0; i < len(rawCerts); i++ {
			cert, err := x509.ParseCertificate(rawCerts[i])
			if err != nil {
				return err
			}
			chain[i] = cert
		}

		pubKey, err := PubKeyFromCertChain(chain)
		if err != nil {
			return err
		}
		if remote != "" && !remote.MatchesPublicKey(pubKey) {
			peerID, err := peer.IDFromPublicKey(pubKey)
			if err != nil {
				peerID = peer.ID(fmt.Sprintf("(not determined: %s)", err.Error()))
			}
			return fmt.Errorf("peer IDs don't match: expected %s, got %s", remote, peerID)
		}
		keyCh <- pubKey
		return nil
	}
	return conf, keyCh
}

// PubKeyFromCertChain verifies the certificate chain and extract the remote's public key.
func PubKeyFromCertChain(chain []*x509.Certificate) (ic.PubKey, error) {
	if len(chain) != 1 {
		return nil, errors.New("expected one certificates in the chain")
	}
	cert := chain[0]
	pool := x509.NewCertPool()
	pool.AddCert(cert)
	var found bool
	var keyExt pkix.Extension
	// find the libp2p key extension, skipping all unknown extensions
	for _, ext := range cert.Extensions {
		if extensionIDEqual(ext.Id, extensionID) {
			keyExt = ext
			found = true
			for i, oident := range cert.UnhandledCriticalExtensions {
				if oident.Equal(ext.Id) {
					// delete the extension from UnhandledCriticalExtensions
					cert.UnhandledCriticalExtensions = append(cert.UnhandledCriticalExtensions[:i], cert.UnhandledCriticalExtensions[i+1:]...)
					break
				}
			}
			break
		}
	}
	if !found {
		return nil, errors.New("expected certificate to contain the key extension")
	}
	if _, err := cert.Verify(x509.VerifyOptions{Roots: pool}); err != nil {
		// If we return an x509 error here, it will be sent on the wire.
		// Wrap the error to avoid that.
		return nil, fmt.Errorf("certificate verification failed: %s", err)
	}

	var sk signedKey
	if _, err := asn1.Unmarshal(keyExt.Value, &sk); err != nil {
		return nil, fmt.Errorf("unmarshalling signed certificate failed: %s", err)
	}
	pubKey, err := ic.UnmarshalPublicKey(sk.PubKey)
	if err != nil {
		return nil, fmt.Errorf("unmarshalling public key failed: %s", err)
	}
	certKeyPub, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
	if err != nil {
		return nil, err
	}
	valid, err := pubKey.Verify(append([]byte(certificatePrefix), certKeyPub...), sk.Signature)
	if err != nil {
		return nil, fmt.Errorf("signature verification failed: %s", err)
	}
	if !valid {
		return nil, errors.New("signature invalid")
	}
	return pubKey, nil
}

func keyToCertificate(sk ic.PrivKey) (*tls.Certificate, error) {
	certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, err
	}

	keyBytes, err := ic.MarshalPublicKey(sk.GetPublic())
	if err != nil {
		return nil, err
	}
	certKeyPub, err := x509.MarshalPKIXPublicKey(certKey.Public())
	if err != nil {
		return nil, err
	}
	signature, err := sk.Sign(append([]byte(certificatePrefix), certKeyPub...))
	if err != nil {
		return nil, err
	}
	value, err := asn1.Marshal(signedKey{
		PubKey:    keyBytes,
		Signature: signature,
	})
	if err != nil {
		return nil, err
	}

	bigNum := big.NewInt(1 << 62)
	sn, err := rand.Int(rand.Reader, bigNum)
	if err != nil {
		return nil, err
	}
	subjectSN, err := rand.Int(rand.Reader, bigNum)
	if err != nil {
		return nil, err
	}
	tmpl := &x509.Certificate{
		SerialNumber: sn,
		NotBefore:    time.Now().Add(-time.Hour),
		NotAfter:     time.Now().Add(certValidityPeriod),
		// According to RFC 3280, the issuer field must be set,
		// see https://datatracker.ietf.org/doc/html/rfc3280#section-4.1.2.4.
		Subject: pkix.Name{SerialNumber: subjectSN.String()},
		// after calling CreateCertificate, these will end up in Certificate.Extensions
		ExtraExtensions: []pkix.Extension{
			{Id: extensionID, Critical: extensionCritical, Value: value},
		},
	}
	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, certKey.Public(), certKey)
	if err != nil {
		return nil, err
	}
	return &tls.Certificate{
		Certificate: [][]byte{certDER},
		PrivateKey:  certKey,
	}, nil
}

// We want nodes without AES hardware (e.g. ARM) support to always use ChaCha.
// Only if both nodes have AES hardware support (e.g. x86), AES should be used.
// x86->x86: AES, ARM->x86: ChaCha, x86->ARM: ChaCha and ARM->ARM: Chacha
// This function returns true if we don't have AES hardware support, and false otherwise.
// Thus, ARM servers will always use their own cipher suite preferences (ChaCha first),
// and x86 servers will aways use the client's cipher suite preferences.
func preferServerCipherSuites() bool {
	// Copied from the Go TLS implementation.

	// Check the cpu flags for each platform that has optimized GCM implementations.
	// Worst case, these variables will just all be false.
	var (
		hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
		hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
		// Keep in sync with crypto/aes/cipher_s390x.go.
		hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)

		hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
	)
	return !hasGCMAsm
}
<|endoftext|>


<commit_before>// Package ioconn allows any combination of an io.Reader, io.Writer and io.Closer to become a net.Conn
package ioconn

import (
	"errors"
	"io"
	"net"
	"time"
)

// CloserFunc is a func that implements the io.Closer interface allowing a
// closure or other function to be io.Closer
type CloserFunc func() error

// Close simply calls the CloserFunc func
func (c CloserFunc) Close() error {
	return c()
}

// FileAddr is a net.Addr that represents a file. Should be a full path
type FileAddr string

// Network always returns "file"
func (f FileAddr) Network() string {
	return "file"
}

// String returns file://path
func (f FileAddr) String() string {
	return "file://" + string(f)
}

// Addr is a simple implementation of the net.Addr interface
type Addr struct {
	Net, Str string
}

// Network returns the Net string
func (a Addr) Network() string {
	return a.Net
}

// String returns the Str string
func (a Addr) String() string {
	return a.Str
}

// Conn implements a net.Conn
type Conn struct {
	io.Reader
	io.Writer
	io.Closer
	Local, Remote net.Addr
}

// LocalAddr returns the Local Address
func (c Conn) LocalAddr() net.Addr {
	return c.Local
}

// RemoteAddr returns the Remote Address
func (c Conn) RemoteAddr() net.Addr {
	return c.Remote
}

// SetDeadline is unimplemented and always returns an error
func (Conn) SetDeadline(time.Time) error {
	return ErrUnimplemented
}

// SetReadDeadline is unimplemented and always returns an error
func (Conn) SetReadDeadline(time.Time) error {
	return ErrUnimplemented
}

// SetWriteDeadline is unimplemented and always returns an error
func (Conn) SetWriteDeadline(time.Time) error {
	return ErrUnimplemented
}

// Errors
var ErrUnimplemented = errors.New("not implmented")
<commit_msg>Added simple timeout implementation<commit_after>// Package ioconn allows any combination of an io.Reader, io.Writer and io.Closer to become a net.Conn
package ioconn

import (
	"errors"
	"io"
	"net"
	"time"
)

// CloserFunc is a func that implements the io.Closer interface allowing a
// closure or other function to be io.Closer
type CloserFunc func() error

// Close simply calls the CloserFunc func
func (c CloserFunc) Close() error {
	return c()
}

// FileAddr is a net.Addr that represents a file. Should be a full path
type FileAddr string

// Network always returns "file"
func (f FileAddr) Network() string {
	return "file"
}

// String returns file://path
func (f FileAddr) String() string {
	return "file://" + string(f)
}

// Addr is a simple implementation of the net.Addr interface
type Addr struct {
	Net, Str string
}

// Network returns the Net string
func (a Addr) Network() string {
	return a.Net
}

// String returns the Str string
func (a Addr) String() string {
	return a.Str
}

// Conn implements a net.Conn
type Conn struct {
	io.Reader
	io.Writer
	io.Closer
	Local, Remote               net.Addr
	ReadDeadline, WriteDeadline time.Time
}

// Read implements the io.Reader interface
func (c *Conn) Read(p []byte) (int, error) {
	if time.Now().After(c.ReadDeadline) {
		return 0, ErrTimeout
	}
	return c.Reader.Read(p)
}

// Write implements the io.Writer interface
func (c *Conn) Write(p []byte) (int, error) {
	if time.Now().After(c.WriteDeadline) {
		return 0, ErrTimeout
	}
	return c.Writer.Write(p)
}

// LocalAddr returns the Local Address
func (c *Conn) LocalAddr() net.Addr {
	return c.Local
}

// RemoteAddr returns the Remote Address
func (c *Conn) RemoteAddr() net.Addr {
	return c.Remote
}

// SetDeadline is unimplemented and always returns an error
func (c *Conn) SetDeadline(t time.Time) error {
	c.ReadDeadline = t
	c.WriteDeadline = t
	return nil
}

// SetReadDeadline is unimplemented and always returns an error
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.ReadDeadline = t
	return nil
}

// SetWriteDeadline is unimplemented and always returns an error
func (c *Conn) SetWriteDeadline(t time.Time) error {
	c.WriteDeadline = t
	return nil
}

// Timeout Error
var ErrTimeout = errors.New("timeout occurred")
<|endoftext|>