<commit_before>// package diagnostics implements a network diagnostics service that
// allows a request to traverse the network and gather information
// on every node connected to it.
package diagnostics

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"sync"
	"time"

	"crypto/rand"

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"

	host "github.com/jbenet/go-ipfs/p2p/host"
	inet "github.com/jbenet/go-ipfs/p2p/net"
	peer "github.com/jbenet/go-ipfs/p2p/peer"
	protocol "github.com/jbenet/go-ipfs/p2p/protocol"

	pb "github.com/jbenet/go-ipfs/diagnostics/internal/pb"
	util "github.com/jbenet/go-ipfs/util"
)

var log = util.Logger("diagnostics")

// ProtocolDiag is the diagnostics protocol.ID
var ProtocolDiag protocol.ID = "/ipfs/diagnostics"

const ResponseTimeout = time.Second * 10

// Diagnostics is a net service that manages requesting and responding to diagnostic
// requests
type Diagnostics struct {
	host host.Host
	self peer.ID

	diagLock sync.Mutex
	diagMap  map[string]time.Time
	birth    time.Time
}

// NewDiagnostics instantiates a new diagnostics service running on the given network
func NewDiagnostics(self peer.ID, h host.Host) *Diagnostics {
	d := &Diagnostics{
		host:    h,
		self:    self,
		birth:   time.Now(),
		diagMap: make(map[string]time.Time),
	}

	h.SetStreamHandler(ProtocolDiag, d.handleNewStream)
	return d
}

type connDiagInfo struct {
	Latency time.Duration
	ID      string
	Count   int
}

type DiagInfo struct {
	// This nodes ID
	ID string

	// A list of peers this node currently has open connections to
	Connections []connDiagInfo

	// A list of keys provided by this node
	//    (currently not filled)
	Keys []string

	// How long this node has been running for
	// TODO rename Uptime
	LifeSpan time.Duration

	// Incoming Bandwidth Usage
	BwIn uint64

	// Outgoing Bandwidth Usage
	BwOut uint64

	// Information about the version of code this node is running
	CodeVersion string
}

// Marshal to json
func (di *DiagInfo) Marshal() []byte {
	b, err := json.Marshal(di)
	if err != nil {
		panic(err)
	}
	//TODO: also consider compressing this. There will be a lot of these
	return b
}

func (d *Diagnostics) getPeers() map[peer.ID]int {
	counts := make(map[peer.ID]int)
	for _, p := range d.host.Network().Peers() {
		counts[p]++
	}

	return counts
}

func (d *Diagnostics) getDiagInfo() *DiagInfo {
	di := new(DiagInfo)
	di.CodeVersion = "github.com/jbenet/go-ipfs"
	di.ID = d.self.Pretty()
	di.LifeSpan = time.Since(d.birth)
	di.Keys = nil // Currently no way to query datastore

	// di.BwIn, di.BwOut = d.host.BandwidthTotals() //TODO fix this.

	for p, n := range d.getPeers() {
		d := connDiagInfo{
			Latency: d.host.Peerstore().LatencyEWMA(p),
			ID:      p.Pretty(),
			Count:   n,
		}
		di.Connections = append(di.Connections, d)
	}
	return di
}

func newID() string {
	id := make([]byte, 16)
	rand.Read(id)
	return string(id)
}

// GetDiagnostic runs a diagnostics request across the entire network
func (d *Diagnostics) GetDiagnostic(timeout time.Duration) ([]*DiagInfo, error) {
	log.Debug("Getting diagnostic.")
	ctx, _ := context.WithTimeout(context.TODO(), timeout)

	diagID := newID()
	d.diagLock.Lock()
	d.diagMap[diagID] = time.Now()
	d.diagLock.Unlock()

	log.Debug("Begin Diagnostic")

	peers := d.getPeers()
	log.Debugf("Sending diagnostic request to %d peers.", len(peers))

	var out []*DiagInfo
	di := d.getDiagInfo()
	out = append(out, di)

	pmes := newMessage(diagID)

	respdata := make(chan []byte)
	sends := 0
	for p, _ := range peers {
		log.Debugf("Sending getDiagnostic to: %s", p)
		sends++
		go func(p peer.ID) {
			data, err := d.getDiagnosticFromPeer(ctx, p, pmes)
			if err != nil {
				log.Errorf("GetDiagnostic error: %v", err)
				respdata <- nil
				return
			}
			respdata <- data
		}(p)
	}

	for i := 0; i < sends; i++ {
		data := <-respdata
		if data == nil {
			continue
		}
		out = appendDiagnostics(data, out)
	}
	return out, nil
}

func appendDiagnostics(data []byte, cur []*DiagInfo) []*DiagInfo {
	buf := bytes.NewBuffer(data)
	dec := json.NewDecoder(buf)
	for {
		di := new(DiagInfo)
		err := dec.Decode(di)
		if err != nil {
			if err != io.EOF {
				log.Errorf("error decoding DiagInfo: %v", err)
			}
			break
		}
		cur = append(cur, di)
	}
	return cur
}

// TODO: this method no longer needed.
func (d *Diagnostics) getDiagnosticFromPeer(ctx context.Context, p peer.ID, mes *pb.Message) ([]byte, error) {
	rpmes, err := d.sendRequest(ctx, p, mes)
	if err != nil {
		return nil, err
	}
	return rpmes.GetData(), nil
}

func newMessage(diagID string) *pb.Message {
	pmes := new(pb.Message)
	pmes.DiagID = proto.String(diagID)
	return pmes
}

func (d *Diagnostics) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) {

	s, err := d.host.NewStream(ProtocolDiag, p)
	if err != nil {
		return nil, err
	}
	defer s.Close()

	r := ggio.NewDelimitedReader(s, inet.MessageSizeMax)
	w := ggio.NewDelimitedWriter(s)

	start := time.Now()

	if err := w.WriteMsg(pmes); err != nil {
		return nil, err
	}

	rpmes := new(pb.Message)
	if err := r.ReadMsg(rpmes); err != nil {
		return nil, err
	}
	if rpmes == nil {
		return nil, errors.New("no response to request")
	}

	rtt := time.Since(start)
	log.Infof("diagnostic request took: %s", rtt.String())
	return rpmes, nil
}

func (d *Diagnostics) handleDiagnostic(p peer.ID, pmes *pb.Message) (*pb.Message, error) {
	log.Debugf("HandleDiagnostic from %s for id = %s", p, util.Key(pmes.GetDiagID()).B58String())
	resp := newMessage(pmes.GetDiagID())

	// Make sure we havent already handled this request to prevent loops
	d.diagLock.Lock()
	_, found := d.diagMap[pmes.GetDiagID()]
	if found {
		d.diagLock.Unlock()
		return resp, nil
	}
	d.diagMap[pmes.GetDiagID()] = time.Now()
	d.diagLock.Unlock()

	buf := new(bytes.Buffer)
	di := d.getDiagInfo()
	buf.Write(di.Marshal())

	ctx, _ := context.WithTimeout(context.TODO(), ResponseTimeout)

	respdata := make(chan []byte)
	sendcount := 0
	for p, _ := range d.getPeers() {
		log.Debugf("Sending diagnostic request to peer: %s", p)
		sendcount++
		go func(p peer.ID) {
			out, err := d.getDiagnosticFromPeer(ctx, p, pmes)
			if err != nil {
				log.Errorf("getDiagnostic error: %v", err)
				respdata <- nil
				return
			}
			respdata <- out
		}(p)
	}

	for i := 0; i < sendcount; i++ {
		out := <-respdata
		_, err := buf.Write(out)
		if err != nil {
			log.Errorf("getDiagnostic write output error: %v", err)
			continue
		}
	}

	resp.Data = buf.Bytes()
	return resp, nil
}

func (d *Diagnostics) HandleMessage(ctx context.Context, s inet.Stream) error {

	r := ggio.NewDelimitedReader(s, 32768) // maxsize
	w := ggio.NewDelimitedWriter(s)

	// deserialize msg
	pmes := new(pb.Message)
	if err := r.ReadMsg(pmes); err != nil {
		log.Errorf("Failed to decode protobuf message: %v", err)
		return nil
	}

	// Print out diagnostic
	log.Infof("[peer: %s] Got message from [%s]\n",
		d.self.Pretty(), s.Conn().RemotePeer())

	// dispatch handler.
	p := s.Conn().RemotePeer()
	rpmes, err := d.handleDiagnostic(p, pmes)
	if err != nil {
		log.Errorf("handleDiagnostic error: %s", err)
		return nil
	}

	// if nil response, return it before serializing
	if rpmes == nil {
		return nil
	}

	// serialize + send response msg
	if err := w.WriteMsg(rpmes); err != nil {
		log.Errorf("Failed to encode protobuf message: %v", err)
		return nil
	}

	return nil
}

func (d *Diagnostics) handleNewStream(s inet.Stream) {
	d.HandleMessage(context.Background(), s)
	s.Close()
}
<commit_msg>diag/net: io must respect timeout ctx<commit_after>// package diagnostics implements a network diagnostics service that
// allows a request to traverse the network and gather information
// on every node connected to it.
package diagnostics

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"sync"
	"time"

	"crypto/rand"

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
	ctxutil "github.com/jbenet/go-ipfs/util/ctx"

	host "github.com/jbenet/go-ipfs/p2p/host"
	inet "github.com/jbenet/go-ipfs/p2p/net"
	peer "github.com/jbenet/go-ipfs/p2p/peer"
	protocol "github.com/jbenet/go-ipfs/p2p/protocol"

	pb "github.com/jbenet/go-ipfs/diagnostics/internal/pb"
	util "github.com/jbenet/go-ipfs/util"
)

var log = util.Logger("diagnostics")

// ProtocolDiag is the diagnostics protocol.ID
var ProtocolDiag protocol.ID = "/ipfs/diagnostics"

const ResponseTimeout = time.Second * 10

// Diagnostics is a net service that manages requesting and responding to diagnostic
// requests
type Diagnostics struct {
	host host.Host
	self peer.ID

	diagLock sync.Mutex
	diagMap  map[string]time.Time
	birth    time.Time
}

// NewDiagnostics instantiates a new diagnostics service running on the given network
func NewDiagnostics(self peer.ID, h host.Host) *Diagnostics {
	d := &Diagnostics{
		host:    h,
		self:    self,
		birth:   time.Now(),
		diagMap: make(map[string]time.Time),
	}

	h.SetStreamHandler(ProtocolDiag, d.handleNewStream)
	return d
}

type connDiagInfo struct {
	Latency time.Duration
	ID      string
	Count   int
}

type DiagInfo struct {
	// This nodes ID
	ID string

	// A list of peers this node currently has open connections to
	Connections []connDiagInfo

	// A list of keys provided by this node
	//    (currently not filled)
	Keys []string

	// How long this node has been running for
	// TODO rename Uptime
	LifeSpan time.Duration

	// Incoming Bandwidth Usage
	BwIn uint64

	// Outgoing Bandwidth Usage
	BwOut uint64

	// Information about the version of code this node is running
	CodeVersion string
}

// Marshal to json
func (di *DiagInfo) Marshal() []byte {
	b, err := json.Marshal(di)
	if err != nil {
		panic(err)
	}
	//TODO: also consider compressing this. There will be a lot of these
	return b
}

func (d *Diagnostics) getPeers() map[peer.ID]int {
	counts := make(map[peer.ID]int)
	for _, p := range d.host.Network().Peers() {
		counts[p]++
	}

	return counts
}

func (d *Diagnostics) getDiagInfo() *DiagInfo {
	di := new(DiagInfo)
	di.CodeVersion = "github.com/jbenet/go-ipfs"
	di.ID = d.self.Pretty()
	di.LifeSpan = time.Since(d.birth)
	di.Keys = nil // Currently no way to query datastore

	// di.BwIn, di.BwOut = d.host.BandwidthTotals() //TODO fix this.

	for p, n := range d.getPeers() {
		d := connDiagInfo{
			Latency: d.host.Peerstore().LatencyEWMA(p),
			ID:      p.Pretty(),
			Count:   n,
		}
		di.Connections = append(di.Connections, d)
	}
	return di
}

func newID() string {
	id := make([]byte, 16)
	rand.Read(id)
	return string(id)
}

// GetDiagnostic runs a diagnostics request across the entire network
func (d *Diagnostics) GetDiagnostic(timeout time.Duration) ([]*DiagInfo, error) {
	log.Debug("Getting diagnostic.")
	ctx, _ := context.WithTimeout(context.TODO(), timeout)

	diagID := newID()
	d.diagLock.Lock()
	d.diagMap[diagID] = time.Now()
	d.diagLock.Unlock()

	log.Debug("Begin Diagnostic")

	peers := d.getPeers()
	log.Debugf("Sending diagnostic request to %d peers.", len(peers))

	var out []*DiagInfo
	di := d.getDiagInfo()
	out = append(out, di)

	pmes := newMessage(diagID)

	respdata := make(chan []byte)
	sends := 0
	for p, _ := range peers {
		log.Debugf("Sending getDiagnostic to: %s", p)
		sends++
		go func(p peer.ID) {
			data, err := d.getDiagnosticFromPeer(ctx, p, pmes)
			if err != nil {
				log.Errorf("GetDiagnostic error: %v", err)
				respdata <- nil
				return
			}
			respdata <- data
		}(p)
	}

	for i := 0; i < sends; i++ {
		data := <-respdata
		if data == nil {
			continue
		}
		out = appendDiagnostics(data, out)
	}
	return out, nil
}

func appendDiagnostics(data []byte, cur []*DiagInfo) []*DiagInfo {
	buf := bytes.NewBuffer(data)
	dec := json.NewDecoder(buf)
	for {
		di := new(DiagInfo)
		err := dec.Decode(di)
		if err != nil {
			if err != io.EOF {
				log.Errorf("error decoding DiagInfo: %v", err)
			}
			break
		}
		cur = append(cur, di)
	}
	return cur
}

// TODO: this method no longer needed.
func (d *Diagnostics) getDiagnosticFromPeer(ctx context.Context, p peer.ID, mes *pb.Message) ([]byte, error) {
	rpmes, err := d.sendRequest(ctx, p, mes)
	if err != nil {
		return nil, err
	}
	return rpmes.GetData(), nil
}

func newMessage(diagID string) *pb.Message {
	pmes := new(pb.Message)
	pmes.DiagID = proto.String(diagID)
	return pmes
}

func (d *Diagnostics) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) {

	s, err := d.host.NewStream(ProtocolDiag, p)
	if err != nil {
		return nil, err
	}
	defer s.Close()

	cr := ctxutil.NewReader(ctx, s) // ok to use. we defer close stream in this func
	cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func
	r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax)
	w := ggio.NewDelimitedWriter(cw)

	start := time.Now()

	if err := w.WriteMsg(pmes); err != nil {
		return nil, err
	}

	rpmes := new(pb.Message)
	if err := r.ReadMsg(rpmes); err != nil {
		return nil, err
	}
	if rpmes == nil {
		return nil, errors.New("no response to request")
	}

	rtt := time.Since(start)
	log.Infof("diagnostic request took: %s", rtt.String())
	return rpmes, nil
}

func (d *Diagnostics) handleDiagnostic(p peer.ID, pmes *pb.Message) (*pb.Message, error) {
	log.Debugf("HandleDiagnostic from %s for id = %s", p, util.Key(pmes.GetDiagID()).B58String())
	resp := newMessage(pmes.GetDiagID())

	// Make sure we havent already handled this request to prevent loops
	d.diagLock.Lock()
	_, found := d.diagMap[pmes.GetDiagID()]
	if found {
		d.diagLock.Unlock()
		return resp, nil
	}
	d.diagMap[pmes.GetDiagID()] = time.Now()
	d.diagLock.Unlock()

	buf := new(bytes.Buffer)
	di := d.getDiagInfo()
	buf.Write(di.Marshal())

	ctx, _ := context.WithTimeout(context.TODO(), ResponseTimeout)

	respdata := make(chan []byte)
	sendcount := 0
	for p, _ := range d.getPeers() {
		log.Debugf("Sending diagnostic request to peer: %s", p)
		sendcount++
		go func(p peer.ID) {
			out, err := d.getDiagnosticFromPeer(ctx, p, pmes)
			if err != nil {
				log.Errorf("getDiagnostic error: %v", err)
				respdata <- nil
				return
			}
			respdata <- out
		}(p)
	}

	for i := 0; i < sendcount; i++ {
		out := <-respdata
		_, err := buf.Write(out)
		if err != nil {
			log.Errorf("getDiagnostic write output error: %v", err)
			continue
		}
	}

	resp.Data = buf.Bytes()
	return resp, nil
}

func (d *Diagnostics) HandleMessage(ctx context.Context, s inet.Stream) error {

	r := ggio.NewDelimitedReader(s, 32768) // maxsize
	w := ggio.NewDelimitedWriter(s)

	// deserialize msg
	pmes := new(pb.Message)
	if err := r.ReadMsg(pmes); err != nil {
		log.Errorf("Failed to decode protobuf message: %v", err)
		return nil
	}

	// Print out diagnostic
	log.Infof("[peer: %s] Got message from [%s]\n",
		d.self.Pretty(), s.Conn().RemotePeer())

	// dispatch handler.
	p := s.Conn().RemotePeer()
	rpmes, err := d.handleDiagnostic(p, pmes)
	if err != nil {
		log.Errorf("handleDiagnostic error: %s", err)
		return nil
	}

	// if nil response, return it before serializing
	if rpmes == nil {
		return nil
	}

	// serialize + send response msg
	if err := w.WriteMsg(rpmes); err != nil {
		log.Errorf("Failed to encode protobuf message: %v", err)
		return nil
	}

	return nil
}

func (d *Diagnostics) handleNewStream(s inet.Stream) {
	d.HandleMessage(context.Background(), s)
	s.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/jessevdk/go-flags"
	"golang.org/x/crypto/sha3"
	"gopkg.in/kothar/brotli-go.v0/dec"
	"gopkg.in/kothar/brotli-go.v0/enc"
	"hash"
	"io"
	"os"
	"bytes"
)

type Options struct {
	Verbose          bool `short:"v" long:"verbose" description:"Show verbose debug information"`
	DecompressFlag   bool `short:"d" long:"decompress" description:"Decompress the input file"`
	CompressionLevel int  `long:"level" description:"Compression level, 0 - 11" default:"5"`
	Version          bool `long:"version" description:"Show version information"`
	StandardOutput   bool `short:"c" long:"stdout" description:"Output to standard out"`
}

func versionInformation() {
	fmt.Printf("IZip v0.6\n")
	fmt.Printf("Copyright (C) 2015 Ian S. Nelson <nelsonis@pobox.com>\n")
	os.Exit(0)
}

func init() {
}

func main() {
	var options Options
	var parser = flags.NewParser(&options, flags.Default)
	inputFileName, err := parser.Parse()
	checkError(err)

	if options.Version {
		versionInformation()
	}

	if options.CompressionLevel < 0 {
		options.CompressionLevel = 0
	}
	if options.CompressionLevel > 11 {
		options.CompressionLevel = 11
	}

	for _, fileName := range inputFileName {
		if options.DecompressFlag {
			decompressFile(fileName, decompressFileName(fileName), options.Verbose, options.StandardOutput)
		} else {
			compressFile(fileName, compressFileName(fileName), options.CompressionLevel, options.Verbose, options.StandardOutput)
		}
	}
}

func decompressFileName(inFileName string) string {
	if inFileName == "-"  {
		return inFileName
	}

	return inFileName[0 : len(inFileName)-3]
}

func compressFileName(inFileName string) string {
	if inFileName == "-"  {
		return inFileName
	}
	return inFileName + ".iz"
}

func checkError(err error) {
	if err != nil {
		os.Exit(1)
	}
}

func compressFile(inFileName string, outFileName string, level int, verbose bool, standardOutput bool) {
	var inFile *os.File
	var err error
	if inFileName == "-" {
		fmt.Printf("Using stdin!\n")
		inFile = os.Stdin
	} else {
		inFile, err = os.Open(inFileName)
		checkError(err)
	}
	defer inFile.Close()
	var outFile *os.File

	if !standardOutput {
		outFile, err = os.Create(outFileName)
		checkError(err)
	} else {
		outFile = os.Stdout
	}

	defer outFile.Close()

	hasher := NewHashWriter()
	archiveWriter := NewArchiveWriter(hasher,outFile)
	teeReader := io.TeeReader(inFile, hasher)

	params := enc.NewBrotliParams()
	params.SetQuality(level)
	params.SetLgwin(24)
	brotliWriter := enc.NewBrotliWriter(params, archiveWriter)
	defer brotliWriter.Close()

	// Perform the actual compression
	io.Copy(brotliWriter, teeReader)
}

// Flag IZ0x01   3 bytes
// Compressed data
// 32 bytes of hash
func writeHeader(outFile io.Writer) {
	var header [3]byte
	header[0] = 'I'
	header[1] = 'Z'
	header[2] = 0x1
	outFile.Write(header[:])
}

func readHeader(inFile io.Reader) bool {
	var header [3]byte
	inFile.Read(header[:])
	if header[0] == 'I' &&
	   header[1] == 'Z' &&
	   header[2] == 0x1 {
		return true
	}
	return false
}

func decompressFile(inFileName string, outFileName string, verbose bool, standardOutput bool) {
	var inFile *os.File
	var err error
	if inFileName != "-" {
		inFile, err = os.Open(inFileName)
		checkError(err)
	} else {
		inFile = os.Stdin
	}

	hashtail := NewHashCatcher()
	hashWriter := NewHashWriter()

	if(!readHeader(inFile)) {
	    fmt.Printf("Invalid header!\n");
	    os.Exit(1)
	}

	readerTee := io.TeeReader(inFile, hashtail)

	brotliReader := dec.NewBrotliReader(readerTee)
	defer brotliReader.Close()

	var outFile *os.File
	if !standardOutput {
		outFile, err = os.Create(outFileName)
		checkError(err)
	} else {
		outFile = os.Stdout
	}

	outFileMulti := io.MultiWriter(outFile, hashWriter)

	io.Copy(outFileMulti, brotliReader)
	outFile.Close()

	hashOutput := hashWriter.Sum()

	if bytes.Compare(hashOutput, hashtail.hashbuffer[:]) == 0 {
		os.Exit(0)
    } else {
        os.Exit(1)
    }
}

/** Writer that performs hashing */
type HashWriter struct {
	hash hash.Hash
}

func NewHashWriter() *HashWriter {
	return &HashWriter {
		hash: sha3.New256(),
	}
}

func (h* HashWriter)Write(buffer []byte)(int, error) {
	return h.hash.Write(buffer)
}

func (h* HashWriter)Close() error {
	return nil;
}

func (h* HashWriter)Sum() []byte {
	return h.hash.Sum(nil);
}

type HashCatcher struct {
	hashbuffer [32]byte
}

func NewHashCatcher() *HashCatcher {
	var tmpBuffer [32]byte
	return &HashCatcher {
		hashbuffer:tmpBuffer,
	}
}

func (h* HashCatcher)Write(buffer []byte)(int, error) {
	if(len(buffer) > 32) {
		copy(h.hashbuffer[:],buffer[len(buffer)-32:len(buffer)])
	} else {
		myLen := len(buffer)
		var copyBuffer [32]byte
		copy(copyBuffer[:],h.hashbuffer[:])
		copy(h.hashbuffer[:], copyBuffer[32-myLen:])
		copy(h.hashbuffer[32-myLen:], buffer)
	}
	return len(buffer),nil
}

func (h* HashCatcher)Close() error {
	return nil;
}

/**
  Encapsulate the archive format.   Header, compressed data, sha3-256 of the input data
*/
type ArchiveWriter struct {
	writer io.WriteCloser
	hashWriter *HashWriter
}

func NewArchiveWriter(hashWriter *HashWriter, output io.WriteCloser) *ArchiveWriter {
	writeHeader(output)
	return &ArchiveWriter {
		writer: output,
		hashWriter: hashWriter,
	}
}

func (w* ArchiveWriter)Write(buffer []byte)(int,error) {
	return w.writer.Write(buffer)
}

func (w* ArchiveWriter)Close() error {
	hashOutput := w.hashWriter.Sum()
	_,err:=w.writer.Write(hashOutput)
	checkError(err)
	w.writer.Close()
	return w.writer.Close()
}

<commit_msg>Updating year<commit_after>package main

import (
	"fmt"
	"github.com/jessevdk/go-flags"
	"golang.org/x/crypto/sha3"
	"gopkg.in/kothar/brotli-go.v0/dec"
	"gopkg.in/kothar/brotli-go.v0/enc"
	"hash"
	"io"
	"os"
	"bytes"
)

type Options struct {
	Verbose          bool `short:"v" long:"verbose" description:"Show verbose debug information"`
	DecompressFlag   bool `short:"d" long:"decompress" description:"Decompress the input file"`
	CompressionLevel int  `long:"level" description:"Compression level, 0 - 11" default:"5"`
	Version          bool `long:"version" description:"Show version information"`
	StandardOutput   bool `short:"c" long:"stdout" description:"Output to standard out"`
}

func versionInformation() {
	fmt.Printf("IZip v0.6\n")
	fmt.Printf("Copyright (C) 2015-2016 Ian S. Nelson <nelsonis@pobox.com>\n")
	os.Exit(0)
}

func init() {
}

func main() {
	var options Options
	var parser = flags.NewParser(&options, flags.Default)
	inputFileName, err := parser.Parse()
	checkError(err)

	if options.Version {
		versionInformation()
	}

	if options.CompressionLevel < 0 {
		options.CompressionLevel = 0
	}
	if options.CompressionLevel > 11 {
		options.CompressionLevel = 11
	}

	for _, fileName := range inputFileName {
		if options.DecompressFlag {
			decompressFile(fileName, decompressFileName(fileName), options.Verbose, options.StandardOutput)
		} else {
			compressFile(fileName, compressFileName(fileName), options.CompressionLevel, options.Verbose, options.StandardOutput)
		}
	}
}

func decompressFileName(inFileName string) string {
	if inFileName == "-"  {
		return inFileName
	}

	return inFileName[0 : len(inFileName)-3]
}

func compressFileName(inFileName string) string {
	if inFileName == "-"  {
		return inFileName
	}
	return inFileName + ".iz"
}

func checkError(err error) {
	if err != nil {
		os.Exit(1)
	}
}

func compressFile(inFileName string, outFileName string, level int, verbose bool, standardOutput bool) {
	var inFile *os.File
	var err error
	if inFileName == "-" {
		fmt.Printf("Using stdin!\n")
		inFile = os.Stdin
	} else {
		inFile, err = os.Open(inFileName)
		checkError(err)
	}
	defer inFile.Close()
	var outFile *os.File

	if !standardOutput {
		outFile, err = os.Create(outFileName)
		checkError(err)
	} else {
		outFile = os.Stdout
	}

	defer outFile.Close()

	hasher := NewHashWriter()
	archiveWriter := NewArchiveWriter(hasher,outFile)
	teeReader := io.TeeReader(inFile, hasher)

	params := enc.NewBrotliParams()
	params.SetQuality(level)
	params.SetLgwin(24)
	brotliWriter := enc.NewBrotliWriter(params, archiveWriter)
	defer brotliWriter.Close()

	// Perform the actual compression
	io.Copy(brotliWriter, teeReader)
}

// Flag IZ0x01   3 bytes
// Compressed data
// 32 bytes of hash
func writeHeader(outFile io.Writer) {
	var header [3]byte
	header[0] = 'I'
	header[1] = 'Z'
	header[2] = 0x1
	outFile.Write(header[:])
}

func readHeader(inFile io.Reader) bool {
	var header [3]byte
	inFile.Read(header[:])
	if header[0] == 'I' &&
	   header[1] == 'Z' &&
	   header[2] == 0x1 {
		return true
	}
	return false
}

func decompressFile(inFileName string, outFileName string, verbose bool, standardOutput bool) {
	var inFile *os.File
	var err error
	if inFileName != "-" {
		inFile, err = os.Open(inFileName)
		checkError(err)
	} else {
		inFile = os.Stdin
	}

	hashtail := NewHashCatcher()
	hashWriter := NewHashWriter()

	if(!readHeader(inFile)) {
	    fmt.Printf("Invalid header!\n");
	    os.Exit(1)
	}

	readerTee := io.TeeReader(inFile, hashtail)

	brotliReader := dec.NewBrotliReader(readerTee)
	defer brotliReader.Close()

	var outFile *os.File
	if !standardOutput {
		outFile, err = os.Create(outFileName)
		checkError(err)
	} else {
		outFile = os.Stdout
	}

	outFileMulti := io.MultiWriter(outFile, hashWriter)

	io.Copy(outFileMulti, brotliReader)
	outFile.Close()

	hashOutput := hashWriter.Sum()

	if bytes.Compare(hashOutput, hashtail.hashbuffer[:]) == 0 {
		os.Exit(0)
    } else {
        os.Exit(1)
    }
}

/** Writer that performs hashing */
type HashWriter struct {
	hash hash.Hash
}

func NewHashWriter() *HashWriter {
	return &HashWriter {
		hash: sha3.New256(),
	}
}

func (h* HashWriter)Write(buffer []byte)(int, error) {
	return h.hash.Write(buffer)
}

func (h* HashWriter)Close() error {
	return nil;
}

func (h* HashWriter)Sum() []byte {
	return h.hash.Sum(nil);
}

type HashCatcher struct {
	hashbuffer [32]byte
}

func NewHashCatcher() *HashCatcher {
	var tmpBuffer [32]byte
	return &HashCatcher {
		hashbuffer:tmpBuffer,
	}
}

func (h* HashCatcher)Write(buffer []byte)(int, error) {
	if(len(buffer) > 32) {
		copy(h.hashbuffer[:],buffer[len(buffer)-32:len(buffer)])
	} else {
		myLen := len(buffer)
		var copyBuffer [32]byte
		copy(copyBuffer[:],h.hashbuffer[:])
		copy(h.hashbuffer[:], copyBuffer[32-myLen:])
		copy(h.hashbuffer[32-myLen:], buffer)
	}
	return len(buffer),nil
}

func (h* HashCatcher)Close() error {
	return nil;
}

/**
  Encapsulate the archive format.   Header, compressed data, sha3-256 of the input data
*/
type ArchiveWriter struct {
	writer io.WriteCloser
	hashWriter *HashWriter
}

func NewArchiveWriter(hashWriter *HashWriter, output io.WriteCloser) *ArchiveWriter {
	writeHeader(output)
	return &ArchiveWriter {
		writer: output,
		hashWriter: hashWriter,
	}
}

func (w* ArchiveWriter)Write(buffer []byte)(int,error) {
	return w.writer.Write(buffer)
}

func (w* ArchiveWriter)Close() error {
	hashOutput := w.hashWriter.Sum()
	_,err:=w.writer.Write(hashOutput)
	checkError(err)
	w.writer.Close()
	return w.writer.Close()
}

<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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 updates expands an update item from IDispatch to a struct.
package updates

import (
	"fmt"
	"reflect"
	"time"

	"github.com/google/cabbie/cablib"
	"github.com/google/cabbie/errors"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// Identity represents the unique identifier of an update.
type Identity struct {
	RevisionNumber int
	UpdateID       string
}

// Category is information about a single Category.
type Category struct {
	Name       string
	Type       string
	CategoryID string
}

// Update contains the  update interface and properties that are available to an update.
type Update struct {
	Item                     *ole.IDispatch
	Title                    string
	CanRequireSource         bool
	Categories               []Category
	Deadline                 time.Time
	Description              string
	EulaAccepted             bool
	Identity                 Identity
	IsBeta                   bool
	IsDownloaded             bool
	IsHidden                 bool
	IsInstalled              bool
	IsMandatory              bool
	IsUninstallable          bool
	LastDeploymentChangeTime time.Time
	MaxDownloadSize          int
	MinDownloadSize          int
	MsrcSeverity             string
	RecommendedCPUSpeed      int
	RecommendedHardDiskSpace int
	RecommendedMemory        int
	SecurityBulletinIDs      []string
	SupersededUpdateIDs      []string
	SupportURL               string
	Type                     string
	KBArticleIDs             []string
	RebootRequired           bool
	IsPresent                bool
	CveIDs                   []string
	BrowseOnly               bool
	PerUser                  bool
	AutoSelection            int
	AutoDownload             int
}

// New expands an IUpdate object into a usable go struct.
func New(item *ole.IDispatch) (*Update, []error) {
	var errors []error
	u := &Update{Item: item}

	fields := reflect.TypeOf(*u)
	data := make(map[string]interface{})
	var err error
	for i := 0; i < fields.NumField(); i++ {
		field := fields.Field(i)
		p := field.Name
		switch field.Type.String() {
		case "string":
			data[p], err = u.toString(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "bool":
			data[p], err = u.toBool(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "int":
			data[p], err = u.toInt(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "[]string":
			data[p], err = u.toStringSlice(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "time.Time":
			data[p], err = u.toDateTime(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "[]updates.Category":
			data[p], err = u.toCategories(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "updates.Identity":
			data[p], err = u.toIdentity(p)
			if err != nil {
				errors = append(errors, err)
			}
		}
	}

	if err := u.fillStruct(data); err != nil {
		errors = append(errors, err)
	}

	return u, errors
}

// AcceptEula accepts the Microsoft Software License Terms that are associated with Windows Update.
func (up *Update) AcceptEula() error {
	r, err := oleutil.CallMethod(up.Item, "AcceptEula")
	if err != nil {
		return fmt.Errorf("unable to accept Eula: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.EulaAccepted = true
	return nil
}

// Hide sets a Boolean value that hides the update from future search results.
func (up *Update) Hide() error {
	r, err := oleutil.PutProperty(up.Item, "IsHidden", true)
	if err != nil {
		return fmt.Errorf("unable to hide update: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.IsHidden = true
	return nil
}

// UnHide sets a Boolean value that makes the update available in future search results.
func (up *Update) UnHide() error {
	r, err := oleutil.PutProperty(up.Item, "IsHidden", false)
	if err != nil {
		return fmt.Errorf("failed to unhide update: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.IsHidden = false
	return nil
}

func (up *Update) toString(property string) (string, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return "", err
	}
	return p.ToString(), nil
}

func (up *Update) toBool(property string) (bool, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return false, err
	}
	return p.Value().(bool), nil
}

func (up *Update) toInt(property string) (int, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return 0, err
	}

	if p.Value() == nil {
		return 0, nil
	}
	return int(p.Value().(int32)), nil
}

func (up *Update) toDateTime(property string) (time.Time, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return time.Time{}, err
	}

	if p.Value() == nil {
		return time.Time{}, nil
	}
	return p.Value().(time.Time), nil
}

func (up *Update) toStringSlice(property string) ([]string, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return nil, err
	}
	pd := p.ToIDispatch()
	defer pd.Release()

	count, err := cablib.Count(pd)
	if err != nil {
		return nil, err
	}

	r := make([]string, count)
	for i := 0; i < count; i++ {
		prop, err := oleutil.GetProperty(pd, "Item", i)
		if err != nil {
			return nil, err
		}
		r[i] = prop.ToString()
	}
	return r, nil
}

func (up *Update) toCategories(property string) ([]Category, error) {
	cs := []Category{}
	cats, err := oleutil.GetProperty(up.Item, "Categories")
	if err != nil {
		return cs, err
	}
	catsd := cats.ToIDispatch()
	defer catsd.Release()

	count, err := cablib.Count(catsd)
	if err != nil {
		return cs, err
	}

	for i := 0; i < count; i++ {
		item, err := oleutil.GetProperty(catsd, "item", i)
		if err != nil {
			continue
		}
		itemd := item.ToIDispatch()

		n, err := oleutil.GetProperty(itemd, "Name")
		if err != nil {
			itemd.Release()
			continue
		}
		t, err := oleutil.GetProperty(itemd, "Type")
		if err != nil {
			itemd.Release()
			continue
		}
		c, err := oleutil.GetProperty(itemd, "CategoryID")
		if err != nil {
			itemd.Release()
			continue
		}

		cs = append(cs, Category{
			Name:       n.ToString(),
			Type:       t.ToString(),
			CategoryID: c.ToString()})
		itemd.Release()
		n.Clear()
		t.Clear()
		c.Clear()
	}

	return cs, nil
}

func (up *Update) toIdentity(property string) (Identity, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return Identity{}, err
	}
	pd := p.ToIDispatch()
	defer pd.Release()

	rn, err := oleutil.GetProperty(pd, "RevisionNumber")
	if err != nil {
		return Identity{}, err
	}
	uid, err := oleutil.GetProperty(pd, "UpdateID")
	if err != nil {
		return Identity{}, err
	}

	return Identity{RevisionNumber: int(rn.Value().(int32)),
		UpdateID: uid.ToString()}, nil
}

func (up *Update) String() string {
	return fmt.Sprintf("Title: %s\n"+
		"Categories: %+v\n"+
		"MsrcSeverity: %s\n"+
		"EulaAccepted: %t\n"+
		"KBArticleIDs: %v", up.Title, up.Categories, up.MsrcSeverity, up.EulaAccepted, up.KBArticleIDs)
}

func (up *Update) fillStruct(m map[string]interface{}) error {
	for k, v := range m {
		if err := cablib.SetField(up, k, v); err != nil {
			return err
		}
	}
	return nil
}

// InCategories determines whether or not this update is in one of the supplied categories.
func (up *Update) InCategories(categories []string) bool {
	if len(categories) == 0 {
		return true
	}

	for _, v := range up.Categories {
		if cablib.StringInSlice(v.Name, categories) {
			return true
		}
	}
	return false
}
<commit_msg>typo<commit_after>// Copyright 2019 Google LLC
//
// 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 updates expands an update item from IDispatch to a struct.
package updates

import (
	"fmt"
	"reflect"
	"time"

	"github.com/google/cabbie/cablib"
	"github.com/google/cabbie/errors"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// Identity represents the unique identifier of an update.
type Identity struct {
	RevisionNumber int
	UpdateID       string
}

// Category is information about a single Category.
type Category struct {
	Name       string
	Type       string
	CategoryID string
}

// Update contains the update interface and properties that are available to an update.
type Update struct {
	Item                     *ole.IDispatch
	Title                    string
	CanRequireSource         bool
	Categories               []Category
	Deadline                 time.Time
	Description              string
	EulaAccepted             bool
	Identity                 Identity
	IsBeta                   bool
	IsDownloaded             bool
	IsHidden                 bool
	IsInstalled              bool
	IsMandatory              bool
	IsUninstallable          bool
	LastDeploymentChangeTime time.Time
	MaxDownloadSize          int
	MinDownloadSize          int
	MsrcSeverity             string
	RecommendedCPUSpeed      int
	RecommendedHardDiskSpace int
	RecommendedMemory        int
	SecurityBulletinIDs      []string
	SupersededUpdateIDs      []string
	SupportURL               string
	Type                     string
	KBArticleIDs             []string
	RebootRequired           bool
	IsPresent                bool
	CveIDs                   []string
	BrowseOnly               bool
	PerUser                  bool
	AutoSelection            int
	AutoDownload             int
}

// New expands an IUpdate object into a usable go struct.
func New(item *ole.IDispatch) (*Update, []error) {
	var errors []error
	u := &Update{Item: item}

	fields := reflect.TypeOf(*u)
	data := make(map[string]interface{})
	var err error
	for i := 0; i < fields.NumField(); i++ {
		field := fields.Field(i)
		p := field.Name
		switch field.Type.String() {
		case "string":
			data[p], err = u.toString(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "bool":
			data[p], err = u.toBool(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "int":
			data[p], err = u.toInt(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "[]string":
			data[p], err = u.toStringSlice(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "time.Time":
			data[p], err = u.toDateTime(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "[]updates.Category":
			data[p], err = u.toCategories(p)
			if err != nil {
				errors = append(errors, err)
			}
		case "updates.Identity":
			data[p], err = u.toIdentity(p)
			if err != nil {
				errors = append(errors, err)
			}
		}
	}

	if err := u.fillStruct(data); err != nil {
		errors = append(errors, err)
	}

	return u, errors
}

// AcceptEula accepts the Microsoft Software License Terms that are associated with Windows Update.
func (up *Update) AcceptEula() error {
	r, err := oleutil.CallMethod(up.Item, "AcceptEula")
	if err != nil {
		return fmt.Errorf("unable to accept Eula: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.EulaAccepted = true
	return nil
}

// Hide sets a Boolean value that hides the update from future search results.
func (up *Update) Hide() error {
	r, err := oleutil.PutProperty(up.Item, "IsHidden", true)
	if err != nil {
		return fmt.Errorf("unable to hide update: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.IsHidden = true
	return nil
}

// UnHide sets a Boolean value that makes the update available in future search results.
func (up *Update) UnHide() error {
	r, err := oleutil.PutProperty(up.Item, "IsHidden", false)
	if err != nil {
		return fmt.Errorf("failed to unhide update: [%s] [%v]", errors.UpdateError(r.Val), err)
	}
	up.IsHidden = false
	return nil
}

func (up *Update) toString(property string) (string, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return "", err
	}
	return p.ToString(), nil
}

func (up *Update) toBool(property string) (bool, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return false, err
	}
	return p.Value().(bool), nil
}

func (up *Update) toInt(property string) (int, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return 0, err
	}

	if p.Value() == nil {
		return 0, nil
	}
	return int(p.Value().(int32)), nil
}

func (up *Update) toDateTime(property string) (time.Time, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return time.Time{}, err
	}

	if p.Value() == nil {
		return time.Time{}, nil
	}
	return p.Value().(time.Time), nil
}

func (up *Update) toStringSlice(property string) ([]string, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return nil, err
	}
	pd := p.ToIDispatch()
	defer pd.Release()

	count, err := cablib.Count(pd)
	if err != nil {
		return nil, err
	}

	r := make([]string, count)
	for i := 0; i < count; i++ {
		prop, err := oleutil.GetProperty(pd, "Item", i)
		if err != nil {
			return nil, err
		}
		r[i] = prop.ToString()
	}
	return r, nil
}

func (up *Update) toCategories(property string) ([]Category, error) {
	cs := []Category{}
	cats, err := oleutil.GetProperty(up.Item, "Categories")
	if err != nil {
		return cs, err
	}
	catsd := cats.ToIDispatch()
	defer catsd.Release()

	count, err := cablib.Count(catsd)
	if err != nil {
		return cs, err
	}

	for i := 0; i < count; i++ {
		item, err := oleutil.GetProperty(catsd, "item", i)
		if err != nil {
			continue
		}
		itemd := item.ToIDispatch()

		n, err := oleutil.GetProperty(itemd, "Name")
		if err != nil {
			itemd.Release()
			continue
		}
		t, err := oleutil.GetProperty(itemd, "Type")
		if err != nil {
			itemd.Release()
			continue
		}
		c, err := oleutil.GetProperty(itemd, "CategoryID")
		if err != nil {
			itemd.Release()
			continue
		}

		cs = append(cs, Category{
			Name:       n.ToString(),
			Type:       t.ToString(),
			CategoryID: c.ToString()})
		itemd.Release()
		n.Clear()
		t.Clear()
		c.Clear()
	}

	return cs, nil
}

func (up *Update) toIdentity(property string) (Identity, error) {
	p, err := oleutil.GetProperty(up.Item, property)
	if err != nil {
		return Identity{}, err
	}
	pd := p.ToIDispatch()
	defer pd.Release()

	rn, err := oleutil.GetProperty(pd, "RevisionNumber")
	if err != nil {
		return Identity{}, err
	}
	uid, err := oleutil.GetProperty(pd, "UpdateID")
	if err != nil {
		return Identity{}, err
	}

	return Identity{RevisionNumber: int(rn.Value().(int32)),
		UpdateID: uid.ToString()}, nil
}

func (up *Update) String() string {
	return fmt.Sprintf("Title: %s\n"+
		"Categories: %+v\n"+
		"MsrcSeverity: %s\n"+
		"EulaAccepted: %t\n"+
		"KBArticleIDs: %v", up.Title, up.Categories, up.MsrcSeverity, up.EulaAccepted, up.KBArticleIDs)
}

func (up *Update) fillStruct(m map[string]interface{}) error {
	for k, v := range m {
		if err := cablib.SetField(up, k, v); err != nil {
			return err
		}
	}
	return nil
}

// InCategories determines whether or not this update is in one of the supplied categories.
func (up *Update) InCategories(categories []string) bool {
	if len(categories) == 0 {
		return true
	}

	for _, v := range up.Categories {
		if cablib.StringInSlice(v.Name, categories) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package pack

import (
	"errors"
	"restic/backend"
)

// Loader loads data from somewhere at a given offset. In contrast to
// io.ReaderAt, off may be negative, in which case it references a position
// relative to the end of the file (similar to Seek()).
type Loader interface {
	Load(p []byte, off int64) (int, error)
}

// BackendLoader creates a Loader from a Backend and a Handle.
type BackendLoader struct {
	Backend backend.Backend
	Handle  backend.Handle
}

// Load returns data at the given offset.
func (l BackendLoader) Load(p []byte, off int64) (int, error) {
	return l.Backend.Load(l.Handle, p, off)
}

// BufferLoader allows using a buffer as a Loader.
type BufferLoader []byte

// Load returns data at the given offset.
func (b BufferLoader) Load(p []byte, off int64) (int, error) {
	switch {
	case off > int64(len(b)):
		return 0, errors.New("offset is larger than data")
	case off < -int64(len(b)):
		return 0, errors.New("offset starts before the beginning of the data")
	case off < 0:
		off = int64(len(b)) + off
	}

	b = b[off:]

	return copy(p, b), nil
}
<commit_msg>Fix BufferLoader for negative offset<commit_after>package pack

import (
	"errors"
	"restic/backend"
)

// Loader loads data from somewhere at a given offset. In contrast to
// io.ReaderAt, off may be negative, in which case it references a position
// relative to the end of the file (similar to Seek()).
type Loader interface {
	Load(p []byte, off int64) (int, error)
}

// BackendLoader creates a Loader from a Backend and a Handle.
type BackendLoader struct {
	Backend backend.Backend
	Handle  backend.Handle
}

// Load returns data at the given offset.
func (l BackendLoader) Load(p []byte, off int64) (int, error) {
	return l.Backend.Load(l.Handle, p, off)
}

// BufferLoader allows using a buffer as a Loader.
type BufferLoader []byte

// Load returns data at the given offset.
func (b BufferLoader) Load(p []byte, off int64) (int, error) {
	switch {
	case off > int64(len(b)):
		return 0, errors.New("offset is larger than data")
	case off < -int64(len(b)):
		off = 0
	case off < 0:
		off = int64(len(b)) + off
	}

	b = b[off:]

	return copy(p, b), nil
}
<|endoftext|>


<commit_before>package common

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestCertificateWithMissingData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
	data := make([]byte, 128+256)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	cert, err := keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	cert_bytes := []byte(cert)
	if assert.Equal(len(cert_data), len(cert_bytes)) {
		assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return available data when cert was missing some data")
	}
}

func TestCertificateWithValidData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	data := make([]byte, 128+256)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	cert, err := keys_and_cert.Certificate()
	assert.Nil(err)
	cert_bytes := []byte(cert)
	if assert.Equal(len(cert_data), len(cert_bytes)) {
		assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return correct data with valid cert")
	}
}

func TestPublicKeyWithBadCertificate(t *testing.T) {
}

func TestPublicKeyWithZeroLengthCertificate(t *testing.T) {
}

func TestPublicKeyWithKeyCertificate(t *testing.T) {
}

func TestPublicKeyWithOtherCertType(t *testing.T) {
}

func TestSigningPublicKeyWithBadCertificate(t *testing.T) {
}

func TestSigningPublicKeyWithZeroLengthCertificate(t *testing.T) {
}

func TestSigningPublicKeyWithKeyCertificate(t *testing.T) {
}

func TestSigningPublicKeyWithOtherCertType(t *testing.T) {
}

func TestReadKeysAndCertWithMissingData(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}

	_, err = keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	_, err = keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	_, err = keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
}

func TestReadKeysAndCertWithMissingCertData(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}

	_, err = keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	_, err = keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	_, err = keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
}

func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
}

func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
}

func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	if assert.Equal(1, len(remainder)) {
		assert.Equal("A", string(remainder[0]))
	}
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
}

func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	if assert.Equal(1, len(remainder)) {
		assert.Equal("A", string(remainder[0]))
	}
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
}
<commit_msg>more tests<commit_after>package common

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestCertificateWithMissingData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
	data := make([]byte, 128+256)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	cert, err := keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	cert_bytes := []byte(cert)
	if assert.Equal(len(cert_data), len(cert_bytes)) {
		assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return available data when cert was missing some data")
	}
}

func TestCertificateWithValidData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	data := make([]byte, 128+256)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	cert, err := keys_and_cert.Certificate()
	assert.Nil(err)
	cert_bytes := []byte(cert)
	if assert.Equal(len(cert_data), len(cert_bytes)) {
		assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return correct data with valid cert")
	}
}

func TestPublicKeyWithBadData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	pub_key_data := make([]byte, 193)
	data := make([]byte, 128)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	pub_key, err := keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	assert.Nil(pub_key)
}

func TestPublicKeyWithBadCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
	pub_key_data := make([]byte, 256)
	data := make([]byte, 128)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	pub_key, err := keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	assert.Nil(pub_key)
}

func TestPublicKeyWithNullCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x00, 0x00, 0x00}
	pub_key_data := make([]byte, 256)
	data := make([]byte, 128)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	pub_key, err := keys_and_cert.PublicKey()
	assert.Nil(err)
	assert.Equal(len(pub_key_data), pub_key.Len())
}

func TestPublicKeyWithKeyCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	pub_key_data := make([]byte, 256)
	data := make([]byte, 128)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	pub_key, err := keys_and_cert.PublicKey()
	assert.Nil(err)
	assert.Equal(len(pub_key_data), pub_key.Len())
}

func TestSigningPublicKeyWithBadData(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	pub_key_data := make([]byte, 256)
	data := make([]byte, 93)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	signing_pub_key, err := keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	assert.Nil(signing_pub_key)
}

func TestSigningPublicKeyWithBadCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
	pub_key_data := make([]byte, 256)
	data := make([]byte, 128)
	data = append(data, pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	signing_pub_key, err := keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	assert.Nil(signing_pub_key)
}

func TestSigningPublicKeyWithNullCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x00, 0x00, 0x00}
	pub_key_data := make([]byte, 256)
	signing_pub_key_data := make([]byte, 128)
	data := append(pub_key_data, signing_pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	signing_pub_key, err := keys_and_cert.SigningPublicKey()
	assert.Nil(err)
	assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
}

func TestSigningPublicKeyWithKeyCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
	pub_key_data := make([]byte, 256)
	signing_pub_key_data := make([]byte, 128)
	data := append(pub_key_data, signing_pub_key_data...)
	data = append(data, cert_data...)
	keys_and_cert := KeysAndCert(data)

	signing_pub_key, err := keys_and_cert.SigningPublicKey()
	assert.Nil(err)
	assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
}

func TestReadKeysAndCertWithMissingData(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}

	_, err = keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	_, err = keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
	_, err = keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
	}
}

func TestReadKeysAndCertWithMissingCertData(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}

	_, err = keys_and_cert.PublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	_, err = keys_and_cert.SigningPublicKey()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
	_, err = keys_and_cert.Certificate()
	if assert.NotNil(err) {
		assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
	}
}

func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
}

func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	assert.Equal(0, len(remainder))
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
}

func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	if assert.Equal(1, len(remainder)) {
		assert.Equal("A", string(remainder[0]))
	}
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
}

func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
	assert := assert.New(t)

	cert_data := make([]byte, 128+256)
	cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...)
	keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
	if assert.Equal(1, len(remainder)) {
		assert.Equal("A", string(remainder[0]))
	}
	assert.Nil(err)

	_, err = keys_and_cert.PublicKey()
	assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.SigningPublicKey()
	assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
	_, err = keys_and_cert.Certificate()
	assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
}
<|endoftext|>


<commit_before>//Package fileurl contains tools for dealing with urls that refer to files.
package fileurl

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

//IsFileURL returns whether a URL is a link to a file.
func IsFileURL(path string) bool {
	match, _ := regexp.Match(`https?:\/\/.+\..+\/.+\.[a-zA-Z0-9]+`, []byte(path))
	return match
	//starts with http:// or https://
	//any characters
	//a domain (foo.whatever.net)
	//then a slash
	//some filename
	//and then a period and file extension
	//eg: http://foo.bar.baz/someFolder/hello.tsv
}

//Copy the data located at the URL to the destination.
func Copy(url string, dst io.Writer) (n int64, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return 0, fmt.Errorf("http.Get: %v", err)
	}
	defer resp.Body.Close()
	return io.Copy(dst, resp.Body)

}

//DownloadTemp downloads the url to a temporary file starting with prefix in the current working directory.
func DownloadTemp(url string, prefix string) (file *os.File, err error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	file, err = ioutil.TempFile(cwd, prefix)
	if err != nil {
		return nil, err
	}
	_, err = Copy(url, file)
	if err != nil {
		file.Close()
	}
	return file, err
}

//Download downloads the url to the file specified by path. This will truncate an existing file!
func Download(url string, filepath string) (file *os.File, err error) {
	file, err = os.Create(filepath)
	if err != nil {
		return nil, err
	}
	_, err = Copy(url, file)
	if err != nil {
		file.Close()
	}
	return file, err
}

//DownloadClose downloads the url to the file specified by path, then closes it. This will truncate an existing file!
func DownloadClose(url string, filepath string) error {
	file, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = Copy(url, file)
	return err
}
<commit_msg>clearer explanatory comment<commit_after>//Package fileurl contains tools for dealing with urls that refer to files.
package fileurl

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

//IsFileURL returns whether a URL is a link to a file.
func IsFileURL(path string) bool {
	match, _ := regexp.Match(`https?:\/\/.+\..+\/.+\.[a-zA-Z0-9]+`, []byte(path))
	return match
	//starts with http:// or https://
	//any characters
	//a domain (foo.whatever.net)
	//then a slash
	//any additional characters
	//some file-path
	//and then a period and file extension
	//eg: http://foo.bar.baz/someFolder/hello.tsv
}

//Copy the data located at the URL to the destination.
func Copy(url string, dst io.Writer) (n int64, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return 0, fmt.Errorf("http.Get: %v", err)
	}
	defer resp.Body.Close()
	return io.Copy(dst, resp.Body)

}

//DownloadTemp downloads the url to a temporary file starting with prefix in the current working directory.
func DownloadTemp(url string, prefix string) (file *os.File, err error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	file, err = ioutil.TempFile(cwd, prefix)
	if err != nil {
		return nil, err
	}
	_, err = Copy(url, file)
	if err != nil {
		file.Close()
	}
	return file, err
}

//Download downloads the url to the file specified by path. This will truncate an existing file!
func Download(url string, filepath string) (file *os.File, err error) {
	file, err = os.Create(filepath)
	if err != nil {
		return nil, err
	}
	_, err = Copy(url, file)
	if err != nil {
		file.Close()
	}
	return file, err
}

//DownloadClose downloads the url to the file specified by path, then closes it. This will truncate an existing file!
func DownloadClose(url string, filepath string) error {
	file, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = Copy(url, file)
	return err
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"

	security "github.com/libp2p/go-conn-security"
	csms "github.com/libp2p/go-conn-security-multistream"
	insecure "github.com/libp2p/go-conn-security/insecure"
	host "github.com/libp2p/go-libp2p-host"
	peer "github.com/libp2p/go-libp2p-peer"
)

// SecC is a security transport constructor
type SecC func(h host.Host) (security.Transport, error)

// MsSecC is a tuple containing a security transport constructor and a protocol
// ID.
type MsSecC struct {
	SecC
	ID string
}

var securityArgTypes = newArgTypeSet(
	hostType, networkType, peerIDType,
	privKeyType, pubKeyType, pstoreType,
)

// SecurityConstructor creates a security constructor from the passed parameter
// using reflection.
func SecurityConstructor(sec interface{}) (SecC, error) {
	// Already constructed?
	if t, ok := sec.(security.Transport); ok {
		return func(_ host.Host) (security.Transport, error) {
			return t, nil
		}, nil
	}

	ctor, err := makeConstructor(sec, securityType, securityArgTypes)
	if err != nil {
		return nil, err
	}
	return func(h host.Host) (security.Transport, error) {
		t, err := ctor(h, nil)
		if err != nil {
			return nil, err
		}
		return t.(security.Transport), nil
	}, nil
}

func makeInsecureTransport(id peer.ID) security.Transport {
	secMuxer := new(csms.SSMuxer)
	secMuxer.AddTransport(insecure.ID, insecure.New(id))
	return secMuxer
}

func makeSecurityTransport(h host.Host, tpts []MsSecC) (security.Transport, error) {
	secMuxer := new(csms.SSMuxer)
	transportSet := make(map[string]struct{}, len(tpts))
	for _, tptC := range tpts {
		if _, ok := transportSet[tptC.ID]; ok {
			return nil, fmt.Errorf("duplicate security transport: %s", tptC.ID)
		}
		transportSet[tptC.ID] = tptC
	}
	for _, tptC := range tpts {
		tpt, err := tptC.SecC(h)
		if err != nil {
			return nil, err
		}
		if _, ok := tpt.(*insecure.Transport); ok {
			return nil, fmt.Errorf("cannot construct libp2p with an insecure transport, set the Insecure config option instead")
		}
		secMuxer.AddTransport(tptC.ID, tpt)
	}
	return secMuxer, nil
}
<commit_msg>Change value to struct<commit_after>package config

import (
	"fmt"

	security "github.com/libp2p/go-conn-security"
	csms "github.com/libp2p/go-conn-security-multistream"
	insecure "github.com/libp2p/go-conn-security/insecure"
	host "github.com/libp2p/go-libp2p-host"
	peer "github.com/libp2p/go-libp2p-peer"
)

// SecC is a security transport constructor
type SecC func(h host.Host) (security.Transport, error)

// MsSecC is a tuple containing a security transport constructor and a protocol
// ID.
type MsSecC struct {
	SecC
	ID string
}

var securityArgTypes = newArgTypeSet(
	hostType, networkType, peerIDType,
	privKeyType, pubKeyType, pstoreType,
)

// SecurityConstructor creates a security constructor from the passed parameter
// using reflection.
func SecurityConstructor(sec interface{}) (SecC, error) {
	// Already constructed?
	if t, ok := sec.(security.Transport); ok {
		return func(_ host.Host) (security.Transport, error) {
			return t, nil
		}, nil
	}

	ctor, err := makeConstructor(sec, securityType, securityArgTypes)
	if err != nil {
		return nil, err
	}
	return func(h host.Host) (security.Transport, error) {
		t, err := ctor(h, nil)
		if err != nil {
			return nil, err
		}
		return t.(security.Transport), nil
	}, nil
}

func makeInsecureTransport(id peer.ID) security.Transport {
	secMuxer := new(csms.SSMuxer)
	secMuxer.AddTransport(insecure.ID, insecure.New(id))
	return secMuxer
}

func makeSecurityTransport(h host.Host, tpts []MsSecC) (security.Transport, error) {
	secMuxer := new(csms.SSMuxer)
	transportSet := make(map[string]struct{}, len(tpts))
	for _, tptC := range tpts {
		if _, ok := transportSet[tptC.ID]; ok {
			return nil, fmt.Errorf("duplicate security transport: %s", tptC.ID)
		}
		transportSet[tptC.ID] = struct{}{}
	}
	for _, tptC := range tpts {
		tpt, err := tptC.SecC(h)
		if err != nil {
			return nil, err
		}
		if _, ok := tpt.(*insecure.Transport); ok {
			return nil, fmt.Errorf("cannot construct libp2p with an insecure transport, set the Insecure config option instead")
		}
		secMuxer.AddTransport(tptC.ID, tpt)
	}
	return secMuxer, nil
}
<|endoftext|>


<commit_before>package amazon

import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/supergiant/supergiant/pkg/clouds"
	"github.com/supergiant/supergiant/pkg/node"
	"github.com/supergiant/supergiant/pkg/util"
	"github.com/supergiant/supergiant/pkg/workflows/steps"
)

const StepCreateMachine = "aws_create_instance"
const (
	IPAttempts             = 12
	SleepSecondsPerAttempt = 6
)

type StepCreateInstance struct {
	GetEC2 GetEC2Fn
}

//InitCreateMachine adds the step to the registry
func InitCreateMachine(fn func(steps.AWSConfig) (ec2iface.EC2API, error)) {
	steps.RegisterStep(StepCreateMachine, NewCreateInstance(fn))
}

func NewCreateInstance(fn GetEC2Fn) *StepCreateInstance {
	return &StepCreateInstance{
		GetEC2: fn,
	}
}

func (s *StepCreateInstance) Run(ctx context.Context, w io.Writer, cfg *steps.Config) error {
	log := util.GetLogger(w)
	log.Infof("[%s] - started", StepCreateMachine)

	var secGroupID *string

	//Determining a sec group in AWS for EC2 instance to be spawned.
	if cfg.IsMaster {
		secGroupID = &cfg.AWSConfig.MastersSecurityGroupID
	} else {
		secGroupID = &cfg.AWSConfig.NodesSecurityGroupID
	}

	EC2, err := s.GetEC2(cfg.AWSConfig)
	if err != nil {
		logrus.Errorf("[%s] - failed to authorize in AWS: %v", s.Name(), err)
		return errors.Wrap(ErrAuthorization, err.Error())
	}

	amiID, err := s.FindAMI(ctx, w, EC2)
	if err != nil {
		logrus.Errorf("[%s] - failed to find AMI for Ubuntu: %v", s.Name(), err)
		return errors.Wrap(err, "failed to find AMI")
	}

	isEbs, err := strconv.ParseBool(cfg.AWSConfig.EbsOptimized)
	volumeSize, err := strconv.Atoi(cfg.AWSConfig.VolumeSize)
	hasPublicAddress, err := strconv.ParseBool(cfg.AWSConfig.HasPublicAddr)

	nodeName := util.MakeNodeName(cfg.ClusterName, cfg.TaskID, cfg.IsMaster)
	runInstanceInput := &ec2.RunInstancesInput{
		BlockDeviceMappings: []*ec2.BlockDeviceMapping{
			{
				DeviceName: aws.String("/dev/xvda"),
				Ebs: &ec2.EbsBlockDevice{
					DeleteOnTermination: aws.Bool(true),
					VolumeType:          aws.String("gp2"),
					VolumeSize:          aws.Int64(int64(volumeSize)),
				},
			},
		},
		EbsOptimized: &isEbs,
		ImageId:      &amiID,
		InstanceType: &cfg.AWSConfig.InstanceType,
		KeyName:      &cfg.AWSConfig.KeyPairName,
		MaxCount:     aws.Int64(1),
		MinCount:     aws.Int64(1),
		//	SecurityGroupIds: []*string{secGroupID},
		//SubnetId: aws.String(cfg.AWSConfig.SubnetID),

		//TODO add custom TAGS
		TagSpecifications: []*ec2.TagSpecification{
			{
				ResourceType: aws.String("instance"),
				Tags: []*ec2.Tag{
					{
						Key:   aws.String("KubernetesCluster"),
						Value: aws.String(cfg.ClusterName),
					},
					{
						Key:   aws.String("Name"),
						Value: aws.String(nodeName),
					},
					{
						Key:   aws.String("Role"),
						Value: aws.String(util.MakeRole(cfg.IsMaster)),
					},
				},
			},
		},
	}
	if hasPublicAddress {
		runInstanceInput.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
			{
				DeviceIndex:              aws.Int64(0),
				AssociatePublicIpAddress: aws.Bool(true),
				DeleteOnTermination:      aws.Bool(true),
				SubnetId:                 aws.String(cfg.AWSConfig.SubnetID),
				Groups:                   []*string{secGroupID},
			},
		}
	}

	role := node.RoleMaster
	if !cfg.IsMaster {
		role = node.RoleNode
	}

	cfg.Node = node.Node{
		TaskID:   cfg.TaskID,
		Region:   cfg.AWSConfig.Region,
		Role:     role,
		Provider: clouds.AWS,
		State:    node.StateBuilding,
	}

	// Update node state in cluster
	cfg.NodeChan() <- cfg.Node

	res, err := EC2.RunInstancesWithContext(ctx, runInstanceInput)
	if err != nil {
		cfg.Node.State = node.StateError
		cfg.NodeChan() <- cfg.Node

		log.Errorf("[%s] - failed to create ec2 instance: %v", StepCreateMachine, err)
		return errors.Wrap(ErrCreateInstance, err.Error())
	}

	if len(res.Instances) == 0 {
		cfg.Node.State = node.StateError
		cfg.NodeChan() <- cfg.Node

		return errors.Wrap(ErrCreateInstance, "no instances created")
	}

	instance := res.Instances[0]

	cfg.Node.Region = cfg.AWSConfig.Region
	cfg.Node.CreatedAt = instance.LaunchTime.Unix()
	cfg.Node.ID = *instance.InstanceId

	// Update node state in cluster
	cfg.NodeChan() <- cfg.Node

	if hasPublicAddress {
		log.Infof("[%s] - waiting to obtain public IP...", s.Name())

		//Waiting for AWS to assign public IP requires to poll an describe ec2 endpoint several times
		found := false
		for i := 0; i < IPAttempts; i++ {
			lookup := &ec2.DescribeInstancesInput{
				Filters: []*ec2.Filter{
					{
						Name:   aws.String("tag:Name"),
						Values: []*string{aws.String(nodeName)},
					},
					{
						Name:   aws.String("tag:KubernetesCluster"),
						Values: []*string{aws.String(cfg.ClusterName)},
					},
				},
			}
			out, err := EC2.DescribeInstancesWithContext(ctx, lookup)
			if err != nil {
				cfg.Node.State = node.StateError
				cfg.NodeChan() <- cfg.Node
				log.Errorf("[%s] - failed to obtain public IP for node %s: %v", s.Name(), nodeName, err)
				return errors.Wrap(ErrNoPublicIP, err.Error())
			}

			if len(out.Reservations) == 0 {
				log.Infof("[%s] - found 0 ec2 instances, attempt %d", s.Name(), i)
				time.Sleep(time.Duration(SleepSecondsPerAttempt) * time.Second)
				continue
			}

			if i := findInstanceWithPublicAddr(out.Reservations); i != nil {
				cfg.Node.PublicIp = *i.PublicIpAddress
				cfg.Node.PrivateIp = *i.PrivateIpAddress
				log.Infof("[%s] - found public ip - %s for node %s", s.Name(), cfg.Node.PublicIp, nodeName)
				found = true
				break
			}
			time.Sleep(2 * time.Second)
		}
		if !found {
			log.Errorf("[%s] - failed to find public IP address after %d attempts", s.Name(), IPAttempts)
			cfg.Node.State = node.StateError
			cfg.NodeChan() <- cfg.Node
			return ErrNoPublicIP
		}
	}

	if cfg.IsMaster {
		cfg.AddMaster(&cfg.Node)
	}
	cfg.Node.State = node.StateProvisioning
	cfg.NodeChan() <- cfg.Node

	log.Infof("[%s] - success! Created node %s with instanceID %s", s.Name(), nodeName, cfg.Node.ID)
	logrus.Debugf("%v", *instance)

	return nil
}

func (s *StepCreateInstance) FindAMI(ctx context.Context, w io.Writer, EC2 ec2iface.EC2API) (string, error) {
	out, err := EC2.DescribeImagesWithContext(ctx, &ec2.DescribeImagesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("architecture"),
				Values: []*string{
					aws.String("x86_64"),
				},
			},
			{
				Name: aws.String("virtualization-type"),
				Values: []*string{
					aws.String("hvm"),
				},
			},
			{
				Name: aws.String("root-device-type"),
				Values: []*string{
					aws.String("ebs"),
				},
			},
			//Owner should be Canonical
			{
				Name: aws.String("owner-id"),
				Values: []*string{
					aws.String("099720109477"),
				},
			},
			{
				Name: aws.String("description"),
				Values: []*string{
					aws.String("Canonical, Ubuntu, 17.10*"),
				},
			},
		},
	})
	if err != nil {
		return "", err
	}
	amiID := ""

	log := util.GetLogger(w)

	for _, img := range out.Images {
		if img.Description == nil {
			continue
		}
		if strings.Contains(*img.Description, "UNSUPPORTED") {
			continue
		}
		amiID = *img.ImageId

		logMessage := fmt.Sprintf("[%s] - using AMI (ID: %s) %s", s.Name(), amiID, *img.Description)
		log.Info(logMessage)
		logrus.Info(logMessage)

		break
	}

	return amiID, nil
}

func (s *StepCreateInstance) Rollback(ctx context.Context, w io.Writer, cfg *steps.Config) error {
	log := util.GetLogger(w)
	log.Infof("[%s] - rollback initiated", s.Name())

	EC2, err := s.GetEC2(cfg.AWSConfig)
	if err != nil {
		return errors.New("aws: authorization")
	}

	if cfg.Node.ID != "" {
		_, err := EC2.TerminateInstancesWithContext(ctx, &ec2.TerminateInstancesInput{
			InstanceIds: []*string{
				aws.String(cfg.Node.ID),
			},
		})
		if err != nil {
			return err
		}
		log.Infof("[%s] - deleted ec2 instance %s", s.Name(), cfg.Node.ID)
		return nil
	}
	return nil
}

func findInstanceWithPublicAddr(reservations []*ec2.Reservation) *ec2.Instance {
	for _, r := range reservations {
		for _, i := range r.Instances {
			if i.PublicIpAddress != nil {
				return i
			}
		}
	}
	return nil
}

func (*StepCreateInstance) Name() string {
	return StepCreateMachine
}

func (*StepCreateInstance) Description() string {
	return "Create EC2 Instance"
}

func (*StepCreateInstance) Depends() []string {
	return nil
}
<commit_msg>Fixed issue with AWS provisioning status reporting (#698)<commit_after>package amazon

import (
	"context"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/supergiant/supergiant/pkg/clouds"
	"github.com/supergiant/supergiant/pkg/node"
	"github.com/supergiant/supergiant/pkg/util"
	"github.com/supergiant/supergiant/pkg/workflows/steps"
)

const StepCreateMachine = "aws_create_instance"
const (
	IPAttempts             = 12
	SleepSecondsPerAttempt = 6
)

type StepCreateInstance struct {
	GetEC2 GetEC2Fn
}

//InitCreateMachine adds the step to the registry
func InitCreateMachine(fn func(steps.AWSConfig) (ec2iface.EC2API, error)) {
	steps.RegisterStep(StepCreateMachine, NewCreateInstance(fn))
}

func NewCreateInstance(fn GetEC2Fn) *StepCreateInstance {
	return &StepCreateInstance{
		GetEC2: fn,
	}
}

func (s *StepCreateInstance) Run(ctx context.Context, w io.Writer, cfg *steps.Config) error {
	log := util.GetLogger(w)
	log.Infof("[%s] - started", StepCreateMachine)

	var secGroupID *string

	//Determining a sec group in AWS for EC2 instance to be spawned.
	if cfg.IsMaster {
		secGroupID = &cfg.AWSConfig.MastersSecurityGroupID
	} else {
		secGroupID = &cfg.AWSConfig.NodesSecurityGroupID
	}

	EC2, err := s.GetEC2(cfg.AWSConfig)
	if err != nil {
		logrus.Errorf("[%s] - failed to authorize in AWS: %v", s.Name(), err)
		return errors.Wrap(ErrAuthorization, err.Error())
	}

	amiID, err := s.FindAMI(ctx, w, EC2)
	if err != nil {
		logrus.Errorf("[%s] - failed to find AMI for Ubuntu: %v", s.Name(), err)
		return errors.Wrap(err, "failed to find AMI")
	}

	isEbs, err := strconv.ParseBool(cfg.AWSConfig.EbsOptimized)
	volumeSize, err := strconv.Atoi(cfg.AWSConfig.VolumeSize)
	hasPublicAddress, err := strconv.ParseBool(cfg.AWSConfig.HasPublicAddr)

	nodeName := util.MakeNodeName(cfg.ClusterName, cfg.TaskID, cfg.IsMaster)
	runInstanceInput := &ec2.RunInstancesInput{
		BlockDeviceMappings: []*ec2.BlockDeviceMapping{
			{
				DeviceName: aws.String("/dev/xvda"),
				Ebs: &ec2.EbsBlockDevice{
					DeleteOnTermination: aws.Bool(true),
					VolumeType:          aws.String("gp2"),
					VolumeSize:          aws.Int64(int64(volumeSize)),
				},
			},
		},
		EbsOptimized: &isEbs,
		ImageId:      &amiID,
		InstanceType: &cfg.AWSConfig.InstanceType,
		KeyName:      &cfg.AWSConfig.KeyPairName,
		MaxCount:     aws.Int64(1),
		MinCount:     aws.Int64(1),
		//	SecurityGroupIds: []*string{secGroupID},
		//SubnetId: aws.String(cfg.AWSConfig.SubnetID),

		//TODO add custom TAGS
		TagSpecifications: []*ec2.TagSpecification{
			{
				ResourceType: aws.String("instance"),
				Tags: []*ec2.Tag{
					{
						Key:   aws.String("KubernetesCluster"),
						Value: aws.String(cfg.ClusterName),
					},
					{
						Key:   aws.String("Name"),
						Value: aws.String(nodeName),
					},
					{
						Key:   aws.String("Role"),
						Value: aws.String(util.MakeRole(cfg.IsMaster)),
					},
				},
			},
		},
	}
	if hasPublicAddress {
		runInstanceInput.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{
			{
				DeviceIndex:              aws.Int64(0),
				AssociatePublicIpAddress: aws.Bool(true),
				DeleteOnTermination:      aws.Bool(true),
				SubnetId:                 aws.String(cfg.AWSConfig.SubnetID),
				Groups:                   []*string{secGroupID},
			},
		}
	}

	role := node.RoleMaster
	if !cfg.IsMaster {
		role = node.RoleNode
	}

	cfg.Node = node.Node{
		Name:     nodeName,
		TaskID:   cfg.TaskID,
		Region:   cfg.AWSConfig.Region,
		Role:     role,
		Provider: clouds.AWS,
		State:    node.StateBuilding,
	}

	// Update node state in cluster
	cfg.NodeChan() <- cfg.Node

	res, err := EC2.RunInstancesWithContext(ctx, runInstanceInput)
	if err != nil {
		cfg.Node.State = node.StateError
		cfg.NodeChan() <- cfg.Node

		log.Errorf("[%s] - failed to create ec2 instance: %v", StepCreateMachine, err)
		return errors.Wrap(ErrCreateInstance, err.Error())
	}

	if len(res.Instances) == 0 {
		cfg.Node.State = node.StateError
		cfg.NodeChan() <- cfg.Node

		return errors.Wrap(ErrCreateInstance, "no instances created")
	}

	instance := res.Instances[0]

	if hasPublicAddress {
		log.Infof("[%s] - waiting to obtain public IP...", s.Name())

		//Waiting for AWS to assign public IP requires to poll an describe ec2 endpoint several times
		found := false
		for i := 0; i < IPAttempts; i++ {
			lookup := &ec2.DescribeInstancesInput{
				Filters: []*ec2.Filter{
					{
						Name:   aws.String("tag:Name"),
						Values: []*string{aws.String(nodeName)},
					},
					{
						Name:   aws.String("tag:KubernetesCluster"),
						Values: []*string{aws.String(cfg.ClusterName)},
					},
				},
			}
			out, err := EC2.DescribeInstancesWithContext(ctx, lookup)
			if err != nil {
				cfg.Node.State = node.StateError
				cfg.NodeChan() <- cfg.Node
				log.Errorf("[%s] - failed to obtain public IP for node %s: %v", s.Name(), nodeName, err)
				return errors.Wrap(ErrNoPublicIP, err.Error())
			}

			if len(out.Reservations) == 0 {
				log.Infof("[%s] - found 0 ec2 instances, attempt %d", s.Name(), i)
				time.Sleep(time.Duration(SleepSecondsPerAttempt) * time.Second)
				continue
			}

			if i := findInstanceWithPublicAddr(out.Reservations); i != nil {
				cfg.Node.PublicIp = *i.PublicIpAddress
				cfg.Node.PrivateIp = *i.PrivateIpAddress
				log.Infof("[%s] - found public ip - %s for node %s", s.Name(), cfg.Node.PublicIp, nodeName)
				found = true
				break
			}
			time.Sleep(2 * time.Second)
		}
		if !found {
			log.Errorf("[%s] - failed to find public IP address after %d attempts", s.Name(), IPAttempts)
			cfg.Node.State = node.StateError
			cfg.NodeChan() <- cfg.Node
			return ErrNoPublicIP
		}
	}

	cfg.Node.Region = cfg.AWSConfig.Region
	cfg.Node.CreatedAt = instance.LaunchTime.Unix()
	cfg.Node.ID = *instance.InstanceId
	cfg.Node.State = node.StateProvisioning

	cfg.NodeChan() <- cfg.Node
	if cfg.IsMaster {
		cfg.AddMaster(&cfg.Node)
	} else {
		cfg.AddNode(&cfg.Node)
	}

	log.Infof("[%s] - success! Created node %s with instanceID %s", s.Name(), nodeName, cfg.Node.ID)
	logrus.Debugf("%v", *instance)

	return nil
}

func (s *StepCreateInstance) FindAMI(ctx context.Context, w io.Writer, EC2 ec2iface.EC2API) (string, error) {
	out, err := EC2.DescribeImagesWithContext(ctx, &ec2.DescribeImagesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("architecture"),
				Values: []*string{
					aws.String("x86_64"),
				},
			},
			{
				Name: aws.String("virtualization-type"),
				Values: []*string{
					aws.String("hvm"),
				},
			},
			{
				Name: aws.String("root-device-type"),
				Values: []*string{
					aws.String("ebs"),
				},
			},
			//Owner should be Canonical
			{
				Name: aws.String("owner-id"),
				Values: []*string{
					aws.String("099720109477"),
				},
			},
			{
				Name: aws.String("description"),
				Values: []*string{
					aws.String("Canonical, Ubuntu, 17.10*"),
				},
			},
		},
	})
	if err != nil {
		return "", err
	}
	amiID := ""

	log := util.GetLogger(w)

	for _, img := range out.Images {
		if img.Description == nil {
			continue
		}
		if strings.Contains(*img.Description, "UNSUPPORTED") {
			continue
		}
		amiID = *img.ImageId

		logMessage := fmt.Sprintf("[%s] - using AMI (ID: %s) %s", s.Name(), amiID, *img.Description)
		log.Info(logMessage)
		logrus.Info(logMessage)

		break
	}

	return amiID, nil
}

func (s *StepCreateInstance) Rollback(ctx context.Context, w io.Writer, cfg *steps.Config) error {
	log := util.GetLogger(w)
	log.Infof("[%s] - rollback initiated", s.Name())

	EC2, err := s.GetEC2(cfg.AWSConfig)
	if err != nil {
		return errors.New("aws: authorization")
	}

	if cfg.Node.ID != "" {
		_, err := EC2.TerminateInstancesWithContext(ctx, &ec2.TerminateInstancesInput{
			InstanceIds: []*string{
				aws.String(cfg.Node.ID),
			},
		})
		if err != nil {
			return err
		}
		log.Infof("[%s] - deleted ec2 instance %s", s.Name(), cfg.Node.ID)
		return nil
	}
	return nil
}

func findInstanceWithPublicAddr(reservations []*ec2.Reservation) *ec2.Instance {
	for _, r := range reservations {
		for _, i := range r.Instances {
			if i.PublicIpAddress != nil {
				return i
			}
		}
	}
	return nil
}

func (*StepCreateInstance) Name() string {
	return StepCreateMachine
}

func (*StepCreateInstance) Description() string {
	return "Create EC2 Instance"
}

func (*StepCreateInstance) Depends() []string {
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Uses jazzrobot object instead of direct functions<commit_after><|endoftext|>