<commit_before>package app

import (
	"bytes"
	"compress/flate"
	"encoding/json"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/paulmach/go.geojson"
	"gospatial/utils"
	"io"
	"log"
	"os"
	"sync"
	"time"
)

// DB application Database
var (
	DB              Database
	COMMIT_LOG_FILE string = "commit.log"
)

// LayerCache keeps track of Database's loaded geojson layers
type LayerCache struct {
	Geojson *geojson.FeatureCollection
	Time    time.Time
}

// Database strust for application.
type Database struct {
	File             string
	Cache            map[string]*LayerCache
	Apikeys          map[string]Customer
	guard            sync.RWMutex
	commit_log_queue chan string
}

// Connect to bolt database. Returns open database connection.
// @returns *bolt.DB
func (self *Database) connect() *bolt.DB {
	conn, err := bolt.Open(self.File, 0644, nil)
	if err != nil {
		conn.Close()
		//log.Fatal(err)
		panic(err)
	}
	return conn
}

// Init creates bolt database if existing one not found.
// Creates layers and apikey tables. Starts database caching for layers
// @returns Error
func (self *Database) Init() error {
	// Start db caching
	m := make(map[string]*LayerCache)
	self.Cache = m
	go self.cacheManager()
	go self.startCommitLog()
	// connect to db
	conn := self.connect()
	defer conn.Close()
	// datasources
	err := self.CreateTable(conn, "layers")
	if err != nil {
		panic(err)
		return err
	}
	// Add table for datasource owner
	// permissions
	err = self.CreateTable(conn, "apikeys")
	if err != nil {
		panic(err)
		return err
	}
	// create apikey/customer cache
	self.Apikeys = make(map[string]Customer)
	// close and return err
	return err
}

// Starts Database commit log
func (self *Database) startCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open files r and w
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

func (self *Database) CreateTable(conn *bolt.DB, table string) error {
	err := conn.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(table))
		return err
	})
	return err
}

// InsertCustomer inserts customer into apikeys table
// @param customer {Customer}
// @returns Error
func (self *Database) InsertCustomer(customer Customer) error {
	self.Apikeys[customer.Apikey] = customer
	value, err := json.Marshal(customer)
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_apikey", "data":` + string(value) + `}`
	// Insert customer into database
	err = self.Insert("apikeys", customer.Apikey, value)
	if err != nil {
		panic(err)
	}
	return err
}

// GetCustomer returns customer from database
// @param apikey {string}
// @returns Customer
// @returns Error
func (self *Database) GetCustomer(apikey string) (Customer, error) {
	// Check apikey cache
	if _, ok := self.Apikeys[apikey]; ok {
		return self.Apikeys[apikey], nil
	}
	// If customer not found get from database
	val, err := self.Select("apikeys", apikey)
	if err != nil {
		panic(err)
	}
	// datasource not found
	if val == nil {
		return Customer{}, fmt.Errorf("Apikey not found")
	}
	// Read to struct
	customer := Customer{}
	err = json.Unmarshal(val, &customer)
	if err != nil {
		return Customer{}, err
	}
	// Put apikey into cache
	self.Apikeys[apikey] = customer
	// Close database connection
	return customer, nil
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource, _ := utils.NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "new_layer", "data": { "datasource": "` + datasource + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.Insert("layers", datasource, value)
	if err != nil {
		panic(err)
	}
	return datasource, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource string, geojs *geojson.FeatureCollection) error {
	// Caching layer
	if v, ok := self.Cache[datasource]; ok {
		self.guard.Lock()
		v.Geojson = geojs
		v.Time = time.Now()
		self.guard.Unlock()
	} else {
		pgc := &LayerCache{Geojson: geojs, Time: time.Now()}
		self.Cache[datasource] = pgc
	}
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}
	err = self.Insert("layers", datasource, value)
	if err != nil {
		panic(err)
	}
	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource string) (*geojson.FeatureCollection, error) {
	// Caching layer
	if v, ok := self.Cache[datasource]; ok {
		self.guard.RLock()
		v.Time = time.Now()
		self.guard.RUnlock()
		return v.Geojson, nil
	}
	// If page not found get from database
	val, err := self.Select("layers", datasource)
	if err != nil {
		return nil, err
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	// Store page in memory cache
	pgc := &LayerCache{Geojson: geojs, Time: time.Now()}
	self.Cache[datasource] = pgc
	return geojs, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource string) error {
	conn := self.connect()
	defer conn.Close()
	key := []byte(datasource)
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource + `"}}`
	err := conn.Update(func(tx *bolt.Tx) error {
		//bucket, err := tx.CreateBucketIfNotExists([]byte("layers"))
		//if err != nil {
		//	return err
		//}
		//err = bucket.Delete(key)
		bucket := tx.Bucket([]byte("layers"))
		err := bucket.Delete(key)
		return err
	})
	if err != nil {
		panic(err)
	}
	self.guard.Lock()
	delete(self.Cache, datasource)
	self.guard.Unlock()
	return err
}

func (self *Database) Insert(table string, key string, value []byte) error {
	conn := self.connect()
	defer conn.Close()
	err := conn.Update(func(tx *bolt.Tx) error {
		//bucket, err := tx.CreateBucketIfNotExists([]byte(table))
		//if err != nil {
		//	return err
		//}
		//err = bucket.Put([]byte(key), self.compressByte(value))
		bucket := tx.Bucket([]byte(table))
		err := bucket.Put([]byte(key), self.compressByte(value))
		return err
	})
	return err
}

func (self *Database) Select(table string, key string) ([]byte, error) {
	conn := self.connect()
	defer conn.Close()
	val := []byte{}
	err := conn.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(table))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", bucket)
		}
		val = self.decompressByte(bucket.Get([]byte(key)))
		return nil
	})
	return val, err
}

func (self *Database) SelectAll(table string) ([]string, error) {
	conn := self.connect()
	defer conn.Close()
	data := []string{}
	err := conn.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(table))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", bucket)
		}
		bucket.ForEach(func(key, _ []byte) error {
			data = append(data, string(key))
			return nil
		})
		return nil
	})
	return data, err
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource string, feat *geojson.Feature) error {
	// Get layer from database
	featCollection, err := self.GetLayer(datasource)
	if err != nil {
		return err
	}
	// Add new feature to layer
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource + `", "feature": ` + string(value) + `}}`
	featCollection.AddFeature(feat)
	err = self.InsertLayer(datasource, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// cacheManager for Database. Stores layers in memory.
//		Unloads layers older than 90 sec
//		When empty --> 60 sec timer
//		When items in cache --> 15 sec timer
func (self *Database) cacheManager() {
	for {
		n := float64(len(self.Cache))
		if n != 0 {
			for key := range self.Cache {
				// CHECK AVAILABLE SYSTEM MEMORY
				f := float64(len(self.Cache[key].Geojson.Features))
				limit := (300.0 - (f * (f * 0.25))) - (n * 2.0)
				if limit < 0.0 {
					limit = 10.0
				}
				if time.Since(self.Cache[key].Time).Seconds() > limit {
					self.guard.Lock()
					delete(self.Cache, key)
					self.guard.Unlock()
				}
			}
		}
		time.Sleep(10000 * time.Millisecond)
	}
}

// Methods: Compression
// Source: https://github.com/schollz/gofind/blob/master/utils.go#L146-L169
//         https://github.com/schollz/gofind/blob/master/fingerprint.go#L43-L54
// Description:
//		Compress and Decompress bytes
func (self *Database) compressByte(src []byte) []byte {
	compressedData := new(bytes.Buffer)
	self.compress(src, compressedData, 9)
	return compressedData.Bytes()
}

func (self *Database) decompressByte(src []byte) []byte {
	compressedData := bytes.NewBuffer(src)
	deCompressedData := new(bytes.Buffer)
	self.decompress(compressedData, deCompressedData)
	return deCompressedData.Bytes()
}

func (self *Database) compress(src []byte, dest io.Writer, level int) {
	compressor, _ := flate.NewWriter(dest, level)
	compressor.Write(src)
	compressor.Close()
}

func (self *Database) decompress(src io.Reader, dest io.Writer) {
	decompressor := flate.NewReader(src)
	io.Copy(dest, decompressor)
	decompressor.Close()
}
<commit_msg>database crash errors<commit_after>package app

import (
	"bytes"
	"compress/flate"
	"encoding/json"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/paulmach/go.geojson"
	"gospatial/utils"
	"io"
	"log"
	"os"
	"sync"
	"time"
)

// DB application Database
var (
	DB              Database
	COMMIT_LOG_FILE string = "commit.log"
)

// LayerCache keeps track of Database's loaded geojson layers
type LayerCache struct {
	Geojson *geojson.FeatureCollection
	Time    time.Time
}

// Database strust for application.
type Database struct {
	File             string
	Cache            map[string]*LayerCache
	Apikeys          map[string]Customer
	guard            sync.RWMutex
	commit_log_queue chan string
}

// Create to bolt database. Returns open database connection.
// @returns *bolt.DB
func (self *Database) createDb() *bolt.DB {
	conn, err := bolt.Open(self.File, 0644, nil)
	if err != nil {
		conn.Close()
		panic(err)
	}
	return conn
}

// Connect to bolt database. Returns open database connection.
// @returns *bolt.DB
func (self *Database) connect() *bolt.DB {
	// Check if file exists
	_, err := os.Stat(self.File)
	if err != nil {
		panic("Database not found!")
	}
	// Open database connection
	conn, err := bolt.Open(self.File, 0644, nil)
	if err != nil {
		conn.Close()
		//log.Fatal(err)
		panic(err)
	}
	return conn
}

// Init creates bolt database if existing one not found.
// Creates layers and apikey tables. Starts database caching for layers
// @returns Error
func (self *Database) Init() error {
	// Start db caching
	m := make(map[string]*LayerCache)
	self.Cache = m
	go self.cacheManager()
	go self.startCommitLog()
	// connect to db
	//conn := self.connect()
	conn := self.createDb()
	defer conn.Close()
	// datasources
	err := self.CreateTable(conn, "layers")
	if err != nil {
		panic(err)
		return err
	}
	// Add table for datasource owner
	// permissions
	err = self.CreateTable(conn, "apikeys")
	if err != nil {
		panic(err)
		return err
	}
	// create apikey/customer cache
	self.Apikeys = make(map[string]Customer)
	// close and return err
	return err
}

// Starts Database commit log
func (self *Database) startCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open files r and w
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

func (self *Database) CreateTable(conn *bolt.DB, table string) error {
	err := conn.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(table))
		return err
	})
	return err
}

// InsertCustomer inserts customer into apikeys table
// @param customer {Customer}
// @returns Error
func (self *Database) InsertCustomer(customer Customer) error {
	self.Apikeys[customer.Apikey] = customer
	value, err := json.Marshal(customer)
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_apikey", "data":` + string(value) + `}`
	// Insert customer into database
	err = self.Insert("apikeys", customer.Apikey, value)
	if err != nil {
		panic(err)
	}
	return err
}

// GetCustomer returns customer from database
// @param apikey {string}
// @returns Customer
// @returns Error
func (self *Database) GetCustomer(apikey string) (Customer, error) {
	// Check apikey cache
	if _, ok := self.Apikeys[apikey]; ok {
		return self.Apikeys[apikey], nil
	}
	// If customer not found get from database
	val, err := self.Select("apikeys", apikey)
	if err != nil {
		panic(err)
	}
	// datasource not found
	if val == nil {
		return Customer{}, fmt.Errorf("Apikey not found")
	}
	// Read to struct
	customer := Customer{}
	err = json.Unmarshal(val, &customer)
	if err != nil {
		return Customer{}, err
	}
	// Put apikey into cache
	self.Apikeys[apikey] = customer
	// Close database connection
	return customer, nil
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource, _ := utils.NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "new_layer", "data": { "datasource": "` + datasource + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.Insert("layers", datasource, value)
	if err != nil {
		panic(err)
	}
	return datasource, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource string, geojs *geojson.FeatureCollection) error {
	// Update caching layer
	if v, ok := self.Cache[datasource]; ok {
		self.guard.Lock()
		v.Geojson = geojs
		v.Time = time.Now()
		self.guard.Unlock()
	} else {
		pgc := &LayerCache{Geojson: geojs, Time: time.Now()}
		self.Cache[datasource] = pgc
	}
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}
	err = self.Insert("layers", datasource, value)
	if err != nil {
		panic(err)
	}
	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource string) (*geojson.FeatureCollection, error) {
	// Caching layer
	if v, ok := self.Cache[datasource]; ok {
		self.guard.RLock()
		v.Time = time.Now()
		self.guard.RUnlock()
		return v.Geojson, nil
	}
	// If page not found get from database
	val, err := self.Select("layers", datasource)
	if err != nil {
		return nil, err
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	// Store page in memory cache
	pgc := &LayerCache{Geojson: geojs, Time: time.Now()}
	self.Cache[datasource] = pgc
	return geojs, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource string) error {
	conn := self.connect()
	defer conn.Close()
	key := []byte(datasource)
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource + `"}}`
	err := conn.Update(func(tx *bolt.Tx) error {
		//bucket, err := tx.CreateBucketIfNotExists([]byte("layers"))
		//if err != nil {
		//	return err
		//}
		//err = bucket.Delete(key)
		bucket := tx.Bucket([]byte("layers"))
		if bucket == nil {
			return fmt.Errorf("Bucket layers not found!")
		}
		err := bucket.Delete(key)
		return err
	})
	if err != nil {
		panic(err)
	}
	self.guard.Lock()
	delete(self.Cache, datasource)
	self.guard.Unlock()
	return err
}

func (self *Database) Insert(table string, key string, value []byte) error {
	conn := self.connect()
	defer conn.Close()
	err := conn.Update(func(tx *bolt.Tx) error {
		//bucket, err := tx.CreateBucketIfNotExists([]byte(table))
		//if err != nil {
		//	return err
		//}
		//err = bucket.Put([]byte(key), self.compressByte(value))
		bucket := tx.Bucket([]byte(table))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", table)
		}
		err := bucket.Put([]byte(key), self.compressByte(value))
		return err
	})
	return err
}

func (self *Database) Select(table string, key string) ([]byte, error) {
	conn := self.connect()
	defer conn.Close()
	val := []byte{}
	err := conn.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(table))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", table)
		}
		val = self.decompressByte(bucket.Get([]byte(key)))
		return nil
	})
	return val, err
}

func (self *Database) SelectAll(table string) ([]string, error) {
	conn := self.connect()
	defer conn.Close()
	data := []string{}
	err := conn.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(table))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", table)
		}
		bucket.ForEach(func(key, _ []byte) error {
			data = append(data, string(key))
			return nil
		})
		return nil
	})
	return data, err
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource string, feat *geojson.Feature) error {
	// Get layer from database
	featCollection, err := self.GetLayer(datasource)
	if err != nil {
		return err
	}
	// Add new feature to layer
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource + `", "feature": ` + string(value) + `}}`
	featCollection.AddFeature(feat)
	// insert layer
	err = self.InsertLayer(datasource, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// cacheManager for Database. Stores layers in memory.
//		Unloads layers older than 90 sec
//		When empty --> 60 sec timer
//		When items in cache --> 15 sec timer
func (self *Database) cacheManager() {
	for {
		n := float64(len(self.Cache))
		if n != 0 {
			for key := range self.Cache {
				// CHECK AVAILABLE SYSTEM MEMORY
				f := float64(len(self.Cache[key].Geojson.Features))
				limit := (300.0 - (f * (f * 0.25))) - (n * 2.0)
				if limit < 0.0 {
					limit = 10.0
				}
				if time.Since(self.Cache[key].Time).Seconds() > limit {
					self.guard.Lock()
					delete(self.Cache, key)
					self.guard.Unlock()
				}
			}
		}
		time.Sleep(10000 * time.Millisecond)
	}
}

// Methods: Compression
// Source: https://github.com/schollz/gofind/blob/master/utils.go#L146-L169
//         https://github.com/schollz/gofind/blob/master/fingerprint.go#L43-L54
// Description:
//		Compress and Decompress bytes
func (self *Database) compressByte(src []byte) []byte {
	compressedData := new(bytes.Buffer)
	self.compress(src, compressedData, 9)
	return compressedData.Bytes()
}

func (self *Database) decompressByte(src []byte) []byte {
	compressedData := bytes.NewBuffer(src)
	deCompressedData := new(bytes.Buffer)
	self.decompress(compressedData, deCompressedData)
	return deCompressedData.Bytes()
}

func (self *Database) compress(src []byte, dest io.Writer, level int) {
	compressor, _ := flate.NewWriter(dest, level)
	compressor.Write(src)
	compressor.Close()
}

func (self *Database) decompress(src io.Reader, dest io.Writer) {
	decompressor := flate.NewReader(src)
	io.Copy(dest, decompressor)
	decompressor.Close()
}
<|endoftext|>


<commit_before>package gqt_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Surviving Restarts", func() {
	var (
		args   []string
		client *runner.RunningGarden
	)

	BeforeEach(func() {
		args = []string{}
	})

	JustBeforeEach(func() {
		client = startGarden(args...)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	const (
		subnetName string = "177-100-10-0"
	)

	Context("when a container is created and then garden is restarted", func() {
		var (
			container        garden.Container
			hostNetInPort    uint32
			externalIP       string
			interfacePrefix  string
			propertiesDir    string
			existingProc     garden.Process
			containerSpec    garden.ContainerSpec
			restartArgs      []string
			gracefulShutdown bool
		)

		BeforeEach(func() {
			var err error
			propertiesDir, err = ioutil.TempDir("", "props")
			Expect(err).NotTo(HaveOccurred())
			args = append(args, "--properties-path", path.Join(propertiesDir, "props.json"))

			containerSpec = garden.ContainerSpec{
				Network: "177.100.10.30/30",
			}

			restartArgs = []string{}
			gracefulShutdown = true
		})

		JustBeforeEach(func() {
			var err error
			container, err = client.Create(containerSpec)
			Expect(err).NotTo(HaveOccurred())

			hostNetInPort, _, err = container.NetIn(hostNetInPort, 8080)
			Expect(err).NotTo(HaveOccurred())

			container.NetOut(garden.NetOutRule{
				Networks: []garden.IPRange{
					garden.IPRangeFromIP(net.ParseIP("8.8.8.8")),
				},
			})

			info, err := container.Info()
			Expect(err).NotTo(HaveOccurred())
			externalIP = info.ExternalIP
			interfacePrefix = info.Properties["kawasaki.iptable-prefix"]

			out := gbytes.NewBuffer()
			existingProc, err = container.Run(
				garden.ProcessSpec{
					Path: "/bin/sh",
					Args: []string{"-c", "while true; do echo hello; sleep 1; done;"},
				},
				garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, out),
					Stderr: io.MultiWriter(GinkgoWriter, out),
				})
			Expect(err).NotTo(HaveOccurred())

			if gracefulShutdown {
				Expect(client.Stop()).To(Succeed())
			} else {
				Expect(client.Kill()).To(MatchError("exit status 137"))
			}

			if len(restartArgs) == 0 {
				restartArgs = args
			}
			client = startGarden(restartArgs...)
		})

		AfterEach(func() {
			Expect(os.RemoveAll(propertiesDir)).To(Succeed())
		})

		Context("when the destroy-containers-on-startup flag is passed", func() {
			BeforeEach(func() {
				args = append(args, "--destroy-containers-on-startup")
			})

			It("destroys the remaining containers in the depotDir", func() {
				Expect(ioutil.ReadDir(client.DepotDir)).To(BeEmpty())
			})

			It("destroys the remaining containers' iptables", func() {
				out, err := exec.Command("iptables", "-w", "-S", "-t", "filter").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())
				Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("w-%d-instance.* 177.100.10.0/30", GinkgoParallelNode())))
			})

			It("destroys the remaining containers' bridges", func() {
				out, err := exec.Command("ifconfig").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())

				pattern := fmt.Sprintf(".*w%d%s.*", GinkgoParallelNode(), subnetName)
				Expect(string(out)).NotTo(MatchRegexp(pattern))
			})

			It("kills the container processes", func() {
				processes, err := exec.Command("ps", "aux").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())

				Expect(string(processes)).NotTo(ContainSubstring(fmt.Sprintf("run runc /tmp/test-garden-%d/containers/%s", GinkgoParallelNode(), container.Handle())))
			})

			Context("when the garden server does not shut down gracefully", func() {
				BeforeEach(func() {
					gracefulShutdown = false
				})

				It("destroys orphaned containers' iptables filter rules", func() {
					out, err := exec.Command("iptables", "-w", "-S", "-t", "filter").CombinedOutput()
					Expect(err).NotTo(HaveOccurred())
					Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("%sinstance.*", interfacePrefix)))
				})

				It("destroys orphaned containers' iptables nat rules", func() {
					out, err := exec.Command("iptables", "-w", "-S", "-t", "nat").CombinedOutput()
					Expect(err).NotTo(HaveOccurred())
					Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("%sinstance.*", interfacePrefix)))
				})
			})

			Context("when a container is created after restart", func() {
				It("can be created with the same network reservation", func() {
					_, err := client.Create(containerSpec)
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when the destroy-containers-on-startup flag is not passed", func() {
			Describe("on th pre-existing VM", func() {
				It("does not destroy the depot", func() {
					Expect(filepath.Join(client.DepotDir, container.Handle())).To(BeADirectory())
				})

				It("can still run processes", func() {
					out := gbytes.NewBuffer()
					proc, err := container.Run(
						garden.ProcessSpec{
							Path: "/bin/sh",
							Args: []string{"-c", "echo hello; exit 12"},
						},
						garden.ProcessIO{
							Stdout: io.MultiWriter(GinkgoWriter, out),
							Stderr: io.MultiWriter(GinkgoWriter, out),
						})
					Expect(err).NotTo(HaveOccurred())
					exitCode, err := proc.Wait()
					Expect(err).NotTo(HaveOccurred())

					Expect(exitCode).To(Equal(12))
					Expect(out).To(gbytes.Say("hello"))
				})

				It("can reattach to processes that are still running", func() {
					out := gbytes.NewBuffer()
					procId := existingProc.ID()
					process, err := container.Attach(procId, garden.ProcessIO{
						Stdout: io.MultiWriter(GinkgoWriter, out),
						Stderr: io.MultiWriter(GinkgoWriter, out),
					})
					Expect(err).NotTo(HaveOccurred())
					Eventually(out).Should(gbytes.Say("hello"))

					Expect(process.Signal(garden.SignalKill)).To(Succeed())

					exited := make(chan struct{})
					go func() {
						process.Wait()
						close(exited)
					}()

					Eventually(exited).Should(BeClosed())
				})

				It("can still destroy the container", func() {
					Expect(client.Destroy(container.Handle())).To(Succeed())
				})

				It("can still be able to access the internet", func() {
					Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
				})

				It("can still be accessible from the outside", func() {
					Expect(listenInContainer(container, 8080)).To(Succeed())

					info, err := container.Info()
					Expect(err).NotTo(HaveOccurred())
					externalIP := info.ExternalIP

					// retry because listener process inside other container
					// may not start immediately
					Eventually(func() int {
						session := sendRequest(externalIP, hostNetInPort)
						return session.Wait().ExitCode()
					}).Should(Equal(0))
				})

				Context("when the server denies all the networks", func() {
					BeforeEach(func() {
						args = append(args, "--deny-network", "0.0.0.0/0")
					})

					It("still can't access disallowed IPs", func() {
						Expect(checkConnection(container, "8.8.4.4", 53)).NotTo(Succeed())
					})

					It("can still be able to access the allowed IPs", func() {
						Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
					})
				})

				Context("when the server is restarted without deny networks applied", func() {
					BeforeEach(func() {
						restartArgs = args[:]
						args = append(args, "--deny-network", "0.0.0.0/0")
					})

					It("is able to access the internet", func() {
						Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
						Expect(checkConnection(container, "8.8.4.4", 53)).To(Succeed())
					})
				})
			})

			Context("when creating a container after restart", func() {
				It("should not allocate ports used before restart", func() {
					secondContainer, err := client.Create(garden.ContainerSpec{})
					secondContainerHostPort, _, err := secondContainer.NetIn(0, 8080)
					Expect(err).NotTo(HaveOccurred())
					Expect(hostNetInPort).NotTo(Equal(secondContainerHostPort))
				})

				Context("with a subnet used before restart", func() {
					It("will not allocate an IP", func() {
						_, err := client.Create(containerSpec)
						Expect(err).To(MatchError("the requested IP is already allocated"))
					})
				})

				Context("with an IP used before restart", func() {
					BeforeEach(func() {
						containerSpec = garden.ContainerSpec{
							// Specifying a CIDR of < 30 will make garden give us exactly 177.100.10.5
							Network: "177.100.10.5/29",
						}
					})

					It("should not allocate the IP", func() {
						_, err := client.Create(containerSpec)
						Expect(err).To(MatchError("the requested IP is already allocated"))
					})
				})

				Context("with no network specified", func() {
					BeforeEach(func() {
						containerSpec = garden.ContainerSpec{}
					})

					It("successfully creates another container with no network specified", func() {
						_, err := client.Create(containerSpec)
						Expect(err).NotTo(HaveOccurred())
					})
				})
			})
		})
	})
})
<commit_msg>Make test less fragile<commit_after>package gqt_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Surviving Restarts", func() {
	var (
		args   []string
		client *runner.RunningGarden
	)

	BeforeEach(func() {
		args = []string{}
	})

	JustBeforeEach(func() {
		client = startGarden(args...)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	const (
		subnetName string = "177-100-10-0"
	)

	Context("when a container is created and then garden is restarted", func() {
		var (
			container        garden.Container
			hostNetInPort    uint32
			externalIP       string
			interfacePrefix  string
			propertiesDir    string
			existingProc     garden.Process
			containerSpec    garden.ContainerSpec
			restartArgs      []string
			gracefulShutdown bool
		)

		BeforeEach(func() {
			var err error
			propertiesDir, err = ioutil.TempDir("", "props")
			Expect(err).NotTo(HaveOccurred())
			args = append(args, "--properties-path", path.Join(propertiesDir, "props.json"))

			containerSpec = garden.ContainerSpec{
				Network: "177.100.10.30/30",
			}

			restartArgs = []string{}
			gracefulShutdown = true
		})

		JustBeforeEach(func() {
			var err error
			container, err = client.Create(containerSpec)
			Expect(err).NotTo(HaveOccurred())

			hostNetInPort, _, err = container.NetIn(hostNetInPort, 8080)
			Expect(err).NotTo(HaveOccurred())

			container.NetOut(garden.NetOutRule{
				Networks: []garden.IPRange{
					garden.IPRangeFromIP(net.ParseIP("8.8.8.8")),
				},
			})

			info, err := container.Info()
			Expect(err).NotTo(HaveOccurred())
			externalIP = info.ExternalIP
			interfacePrefix = info.Properties["kawasaki.iptable-prefix"]

			out := gbytes.NewBuffer()
			existingProc, err = container.Run(
				garden.ProcessSpec{
					Path: "/bin/sh",
					Args: []string{"-c", "while true; do echo hello; sleep 1; done;"},
				},
				garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, out),
					Stderr: io.MultiWriter(GinkgoWriter, out),
				})
			Expect(err).NotTo(HaveOccurred())

			if gracefulShutdown {
				Expect(client.Stop()).To(Succeed())
			} else {
				Expect(client.Kill()).To(MatchError("exit status 137"))
			}

			if len(restartArgs) == 0 {
				restartArgs = args
			}
			client = startGarden(restartArgs...)
		})

		AfterEach(func() {
			Expect(os.RemoveAll(propertiesDir)).To(Succeed())
		})

		Context("when the destroy-containers-on-startup flag is passed", func() {
			BeforeEach(func() {
				args = append(args, "--destroy-containers-on-startup")
			})

			It("destroys the remaining containers in the depotDir", func() {
				Expect(ioutil.ReadDir(client.DepotDir)).To(BeEmpty())
			})

			It("destroys the remaining containers' iptables", func() {
				out, err := exec.Command("iptables", "-w", "-S", "-t", "filter").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())
				Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("%sinstance.*", interfacePrefix)))
			})

			It("destroys the remaining containers' bridges", func() {
				out, err := exec.Command("ifconfig").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())

				pattern := fmt.Sprintf(".*w%d%s.*", GinkgoParallelNode(), subnetName)
				Expect(string(out)).NotTo(MatchRegexp(pattern))
			})

			It("kills the container processes", func() {
				processes, err := exec.Command("ps", "aux").CombinedOutput()
				Expect(err).NotTo(HaveOccurred())

				Expect(string(processes)).NotTo(ContainSubstring(fmt.Sprintf("run runc /tmp/test-garden-%d/containers/%s", GinkgoParallelNode(), container.Handle())))
			})

			Context("when the garden server does not shut down gracefully", func() {
				BeforeEach(func() {
					gracefulShutdown = false
				})

				It("destroys orphaned containers' iptables filter rules", func() {
					out, err := exec.Command("iptables", "-w", "-S", "-t", "filter").CombinedOutput()
					Expect(err).NotTo(HaveOccurred())
					Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("%sinstance.*", interfacePrefix)))
				})

				It("destroys orphaned containers' iptables nat rules", func() {
					out, err := exec.Command("iptables", "-w", "-S", "-t", "nat").CombinedOutput()
					Expect(err).NotTo(HaveOccurred())
					Expect(string(out)).NotTo(MatchRegexp(fmt.Sprintf("%sinstance.*", interfacePrefix)))
				})
			})

			Context("when a container is created after restart", func() {
				It("can be created with the same network reservation", func() {
					_, err := client.Create(containerSpec)
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when the destroy-containers-on-startup flag is not passed", func() {
			Describe("on th pre-existing VM", func() {
				It("does not destroy the depot", func() {
					Expect(filepath.Join(client.DepotDir, container.Handle())).To(BeADirectory())
				})

				It("can still run processes", func() {
					out := gbytes.NewBuffer()
					proc, err := container.Run(
						garden.ProcessSpec{
							Path: "/bin/sh",
							Args: []string{"-c", "echo hello; exit 12"},
						},
						garden.ProcessIO{
							Stdout: io.MultiWriter(GinkgoWriter, out),
							Stderr: io.MultiWriter(GinkgoWriter, out),
						})
					Expect(err).NotTo(HaveOccurred())
					exitCode, err := proc.Wait()
					Expect(err).NotTo(HaveOccurred())

					Expect(exitCode).To(Equal(12))
					Expect(out).To(gbytes.Say("hello"))
				})

				It("can reattach to processes that are still running", func() {
					out := gbytes.NewBuffer()
					procId := existingProc.ID()
					process, err := container.Attach(procId, garden.ProcessIO{
						Stdout: io.MultiWriter(GinkgoWriter, out),
						Stderr: io.MultiWriter(GinkgoWriter, out),
					})
					Expect(err).NotTo(HaveOccurred())
					Eventually(out).Should(gbytes.Say("hello"))

					Expect(process.Signal(garden.SignalKill)).To(Succeed())

					exited := make(chan struct{})
					go func() {
						process.Wait()
						close(exited)
					}()

					Eventually(exited).Should(BeClosed())
				})

				It("can still destroy the container", func() {
					Expect(client.Destroy(container.Handle())).To(Succeed())
				})

				It("can still be able to access the internet", func() {
					Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
				})

				It("can still be accessible from the outside", func() {
					Expect(listenInContainer(container, 8080)).To(Succeed())

					info, err := container.Info()
					Expect(err).NotTo(HaveOccurred())
					externalIP := info.ExternalIP

					// retry because listener process inside other container
					// may not start immediately
					Eventually(func() int {
						session := sendRequest(externalIP, hostNetInPort)
						return session.Wait().ExitCode()
					}).Should(Equal(0))
				})

				Context("when the server denies all the networks", func() {
					BeforeEach(func() {
						args = append(args, "--deny-network", "0.0.0.0/0")
					})

					It("still can't access disallowed IPs", func() {
						Expect(checkConnection(container, "8.8.4.4", 53)).NotTo(Succeed())
					})

					It("can still be able to access the allowed IPs", func() {
						Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
					})
				})

				Context("when the server is restarted without deny networks applied", func() {
					BeforeEach(func() {
						restartArgs = args[:]
						args = append(args, "--deny-network", "0.0.0.0/0")
					})

					It("is able to access the internet", func() {
						Expect(checkConnection(container, "8.8.8.8", 53)).To(Succeed())
						Expect(checkConnection(container, "8.8.4.4", 53)).To(Succeed())
					})
				})
			})

			Context("when creating a container after restart", func() {
				It("should not allocate ports used before restart", func() {
					secondContainer, err := client.Create(garden.ContainerSpec{})
					secondContainerHostPort, _, err := secondContainer.NetIn(0, 8080)
					Expect(err).NotTo(HaveOccurred())
					Expect(hostNetInPort).NotTo(Equal(secondContainerHostPort))
				})

				Context("with a subnet used before restart", func() {
					It("will not allocate an IP", func() {
						_, err := client.Create(containerSpec)
						Expect(err).To(MatchError("the requested IP is already allocated"))
					})
				})

				Context("with an IP used before restart", func() {
					BeforeEach(func() {
						containerSpec = garden.ContainerSpec{
							// Specifying a CIDR of < 30 will make garden give us exactly 177.100.10.5
							Network: "177.100.10.5/29",
						}
					})

					It("should not allocate the IP", func() {
						_, err := client.Create(containerSpec)
						Expect(err).To(MatchError("the requested IP is already allocated"))
					})
				})

				Context("with no network specified", func() {
					BeforeEach(func() {
						containerSpec = garden.ContainerSpec{}
					})

					It("successfully creates another container with no network specified", func() {
						_, err := client.Create(containerSpec)
						Expect(err).NotTo(HaveOccurred())
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package dockerbuild

import (
	"archive/tar"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dynport/dgtk/dockerclient"
	"github.com/dynport/dgtk/git"
)

type Build struct {
	Proxy string // http proxy to use for faster builds in local environment
	Root  string // root directory used for archive (should contain a dockerfile at least)

	GitRepository string // repository to check out into the docker build archive
	GitRevision   string // revision of the repository to use

	// If this is a ruby project then add the Gemfiles to the archive separately. That way bundler's inefficiency can be
	// mitigated using docker's caching strategy. Just call copy the Gemfile's somewhere (using the 'ADD' command) and
	// run bundler on them. Then extract the sources and use the app. This way only changes to the Gemfiles will result
	// in a rerun of bundler.
	RubyProject bool

	DockerHost         string // IP of the host running docker.
	DockerPort         int    // Port docker is listening on.
	DockerHostUser     string // If set an SSH tunnel will be setup and used for communication.
	DockerHostPassword string // Password of the user, if required for SSH (public key authentication should be preferred).
	DockerImageTag     string // tag used for the resulting image

	ForceBuild bool // Build even if image already exists.
	Verbose    bool

	msgList []message

	client *dockerclient.DockerHost
}

type message struct {
	Step     int
	Cached   bool
	Command  string
	Image    string
	Started  time.Time
	Finished time.Time
}

type progress struct {
	total   int64
	current int64
	started time.Time
}

func newProgress(total int64) *progress {
	return &progress{started: time.Now(), total: total}
}

func (p *progress) Write(b []byte) (int, error) {
	i := len(b)
	p.current += int64(i)
	fmt.Printf("\rupload progress %.1f%%", 100.0*float64(p.current)/float64(p.total))
	if p.current == p.total {
		fmt.Printf("\nuploaded total_size=%.3fMB in total_time%.3fs\n", float64(p.total)/(1024.0*1024.0), time.Since(p.started).Seconds())
	}
	return i, nil
}

func (b *Build) connectToDockerHost() (e error) {
	if b.client != nil {
		return nil
	}

	if b.DockerHostUser == "" {
		port := b.DockerPort
		if port == 0 {
			port = 4243
		}
		b.client, e = dockerclient.New(b.DockerHost, port)
		return e
	}
	b.client, e = dockerclient.NewViaTunnel(b.DockerHost, b.DockerHostUser, b.DockerHostPassword)
	return e
}

func (b *Build) BuildAndPush() (string, error) {
	imageId, e := b.Build()
	if e != nil {
		return imageId, e
	}
	// build has connected so we can assume b.client is set
	return imageId, b.client.PushImage(b.DockerImageTag, &dockerclient.PushImageOptions{Callback: b.callbackForPush})
}

func (b *Build) Build() (string, error) {
	if e := b.connectToDockerHost(); e != nil {
		return "", e
	}

	if !b.ForceBuild {
		details, e := b.client.ImageDetails(b.DockerImageTag)
		switch e {
		case nil:
			log.Printf("image for %q already exists", b.DockerImageTag)
			return details.Id, nil
		case dockerclient.ErrorNotFound:
			// ignore
		default:
			return "", e
		}
	}

	f, e := b.buildArchive()
	if e != nil {
		return "", e
	}
	defer func() { os.Remove(f.Name()) }()

	f, e = os.Open(f.Name())
	if e != nil {
		return "", e
	}
	stat, e := f.Stat()
	if e != nil {
		return "", e
	}
	progress := newProgress(stat.Size())
	r := io.TeeReader(f, progress)

	return b.client.Build(r, &dockerclient.BuildImageOptions{
		Tag:      b.DockerImageTag,
		Callback: b.callbackForBuild,
	})
}

func (b *Build) buildArchive() (*os.File, error) {
	f, e := ioutil.TempFile("/tmp", "docker_build")
	if e != nil {
		return nil, e
	}
	defer f.Close()

	t := tar.NewWriter(f)
	defer t.Flush()
	defer t.Close()

	if b.GitRepository != "" {
		repo := &git.Repository{Origin: b.GitRepository}
		e := repo.Init()
		if e != nil {
			return nil, e
		}
		e = repo.Fetch()
		if e != nil {
			return nil, e
		}
		if e := repo.WriteArchiveToTar(b.GitRevision, t); e != nil {
			return nil, e
		}
		if b.RubyProject {
			if e := repo.WriteFilesToTar(b.GitRevision, t, "Gemfile", "Gemfile.lock"); e != nil {
				return nil, e
			}
		}
	}
	if withDockerFile, e := b.addFilesToArchive(b.Root, t); e != nil {
		return nil, e
	} else if !withDockerFile {
		return nil, fmt.Errorf("archive must contain a Dockerfile")
	}
	return f, nil
}

func (build *Build) addFilesToArchive(root string, t *tar.Writer) (withDockerFile bool, e error) {
	return withDockerFile, filepath.Walk(root, func(p string, info os.FileInfo, e error) error {
		if e == nil && p != root {
			var e error
			name := strings.TrimPrefix(p, root+"/")
			header := &tar.Header{Name: name, ModTime: info.ModTime().UTC()}
			switch {
			case info.IsDir():
				if name == ".git" {
					return filepath.SkipDir
				}
				header.Typeflag = tar.TypeDir
				header.Mode = 0755
				e = t.WriteHeader(header)
			default:
				b, e := ioutil.ReadFile(p)
				if e != nil {
					return e
				}

				if name == "Dockerfile" {
					withDockerFile = true
					if build.Proxy != "" {
						b = NewDockerfile(b).MixinProxy(build.Proxy)
					}
				}

				header.Mode = 0644
				header.Size = int64(len(b))
				e = t.WriteHeader(header)
				if e != nil {
					return e
				}
				_, e = t.Write(b)
				if e != nil {
					return e
				}
			}
			if e != nil {
				return e
			}
		}
		return nil
	})
}

func (b *Build) callbackForBuild(msg *dockerclient.JSONMessage) {
	if e := msg.Err(); e != nil {
		log.Printf("%s", e)
		return
	}
	msg.Stream = strings.TrimSpace(msg.Stream)
	switch {
	case msg.Stream == "":
		// ignore
	case b.Verbose:
		fmt.Println(msg.Stream)
	case strings.HasPrefix(msg.Stream, "Step "):
		parts := strings.SplitN(msg.Stream[5:], " : ", 2)
		if len(parts) != 2 { // ignore message
			return
		}
		step, e := strconv.Atoi(parts[0])
		if e != nil {
			return
		}
		newMessage := message{
			Step:    step,
			Command: parts[1],
			Started: time.Now(),
		}
		b.msgList = append(b.msgList, newMessage)
		fmt.Printf("%.3d %q", newMessage.Step, newMessage.Command)
	case msg.Stream == "---> Using cache":
		if len(b.msgList) > 0 {
			b.msgList[len(b.msgList)-1].Cached = true
		}
	case strings.HasPrefix(msg.Stream, "---> "):
		if len(b.msgList) > 0 && len(msg.Stream) == 17 {
			i := len(b.msgList) - 1
			b.msgList[i].Finished = time.Now()
			b.msgList[i].Image = msg.Stream[5:]
			status := "BUILT "
			if b.msgList[i].Cached {
				status = "CACHED"
			}
			duration := b.msgList[i].Finished.Sub(b.msgList[i].Started)
			fmt.Printf("\r%.3d [%s] [%s] %q took %5.2fs\n", b.msgList[i].Step, status, b.msgList[i].Image, b.msgList[i].Command, duration.Seconds())
		}
	}
}

func (b *Build) callbackForPush(msg *dockerclient.JSONMessage) {
	if e := msg.Err(); e != nil {
		log.Printf("%s", e)
		return
	}
	msg.Status = strings.TrimSpace(msg.Status)
	switch {
	case msg.Status == "":
		// ignore
	case b.Verbose:
		fmt.Printf("%s\n", msg.Status)
	case msg.Status == "Buffering to disk" && msg.Progress.Total > 0:
		fmt.Printf("\r[%s] Buffering: %4.2f %%", msg.Id, 100.0*float32(msg.Progress.Current)/float32(msg.Progress.Total))
	case msg.Status == "Pushing" && msg.Progress.Total > 0:
		fmt.Printf("\r[%s] Pushing:   %4.2f %%", msg.Id, 100.0*float32(msg.Progress.Current)/float32(msg.Progress.Total))
	case msg.Status == "Image successfully pushed":
		fmt.Printf("\r[%s] Pushing:   ... done\n", msg.Id)
	}
}
<commit_msg>added missing "=" in output<commit_after>package dockerbuild

import (
	"archive/tar"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dynport/dgtk/dockerclient"
	"github.com/dynport/dgtk/git"
)

type Build struct {
	Proxy string // http proxy to use for faster builds in local environment
	Root  string // root directory used for archive (should contain a dockerfile at least)

	GitRepository string // repository to check out into the docker build archive
	GitRevision   string // revision of the repository to use

	// If this is a ruby project then add the Gemfiles to the archive separately. That way bundler's inefficiency can be
	// mitigated using docker's caching strategy. Just call copy the Gemfile's somewhere (using the 'ADD' command) and
	// run bundler on them. Then extract the sources and use the app. This way only changes to the Gemfiles will result
	// in a rerun of bundler.
	RubyProject bool

	DockerHost         string // IP of the host running docker.
	DockerPort         int    // Port docker is listening on.
	DockerHostUser     string // If set an SSH tunnel will be setup and used for communication.
	DockerHostPassword string // Password of the user, if required for SSH (public key authentication should be preferred).
	DockerImageTag     string // tag used for the resulting image

	ForceBuild bool // Build even if image already exists.
	Verbose    bool

	msgList []message

	client *dockerclient.DockerHost
}

type message struct {
	Step     int
	Cached   bool
	Command  string
	Image    string
	Started  time.Time
	Finished time.Time
}

type progress struct {
	total   int64
	current int64
	started time.Time
}

func newProgress(total int64) *progress {
	return &progress{started: time.Now(), total: total}
}

func (p *progress) Write(b []byte) (int, error) {
	i := len(b)
	p.current += int64(i)
	fmt.Printf("\rupload progress %.1f%%", 100.0*float64(p.current)/float64(p.total))
	if p.current == p.total {
		fmt.Printf("\nuploaded total_size=%.3fMB in total_time=%.3fs\n", float64(p.total)/(1024.0*1024.0), time.Since(p.started).Seconds())
	}
	return i, nil
}

func (b *Build) connectToDockerHost() (e error) {
	if b.client != nil {
		return nil
	}

	if b.DockerHostUser == "" {
		port := b.DockerPort
		if port == 0 {
			port = 4243
		}
		b.client, e = dockerclient.New(b.DockerHost, port)
		return e
	}
	b.client, e = dockerclient.NewViaTunnel(b.DockerHost, b.DockerHostUser, b.DockerHostPassword)
	return e
}

func (b *Build) BuildAndPush() (string, error) {
	imageId, e := b.Build()
	if e != nil {
		return imageId, e
	}
	// build has connected so we can assume b.client is set
	return imageId, b.client.PushImage(b.DockerImageTag, &dockerclient.PushImageOptions{Callback: b.callbackForPush})
}

func (b *Build) Build() (string, error) {
	if e := b.connectToDockerHost(); e != nil {
		return "", e
	}

	if !b.ForceBuild {
		details, e := b.client.ImageDetails(b.DockerImageTag)
		switch e {
		case nil:
			log.Printf("image for %q already exists", b.DockerImageTag)
			return details.Id, nil
		case dockerclient.ErrorNotFound:
			// ignore
		default:
			return "", e
		}
	}

	f, e := b.buildArchive()
	if e != nil {
		return "", e
	}
	defer func() { os.Remove(f.Name()) }()

	f, e = os.Open(f.Name())
	if e != nil {
		return "", e
	}
	stat, e := f.Stat()
	if e != nil {
		return "", e
	}
	progress := newProgress(stat.Size())
	r := io.TeeReader(f, progress)

	return b.client.Build(r, &dockerclient.BuildImageOptions{
		Tag:      b.DockerImageTag,
		Callback: b.callbackForBuild,
	})
}

func (b *Build) buildArchive() (*os.File, error) {
	f, e := ioutil.TempFile("/tmp", "docker_build")
	if e != nil {
		return nil, e
	}
	defer f.Close()

	t := tar.NewWriter(f)
	defer t.Flush()
	defer t.Close()

	if b.GitRepository != "" {
		repo := &git.Repository{Origin: b.GitRepository}
		e := repo.Init()
		if e != nil {
			return nil, e
		}
		e = repo.Fetch()
		if e != nil {
			return nil, e
		}
		if e := repo.WriteArchiveToTar(b.GitRevision, t); e != nil {
			return nil, e
		}
		if b.RubyProject {
			if e := repo.WriteFilesToTar(b.GitRevision, t, "Gemfile", "Gemfile.lock"); e != nil {
				return nil, e
			}
		}
	}
	if withDockerFile, e := b.addFilesToArchive(b.Root, t); e != nil {
		return nil, e
	} else if !withDockerFile {
		return nil, fmt.Errorf("archive must contain a Dockerfile")
	}
	return f, nil
}

func (build *Build) addFilesToArchive(root string, t *tar.Writer) (withDockerFile bool, e error) {
	return withDockerFile, filepath.Walk(root, func(p string, info os.FileInfo, e error) error {
		if e == nil && p != root {
			var e error
			name := strings.TrimPrefix(p, root+"/")
			header := &tar.Header{Name: name, ModTime: info.ModTime().UTC()}
			switch {
			case info.IsDir():
				if name == ".git" {
					return filepath.SkipDir
				}
				header.Typeflag = tar.TypeDir
				header.Mode = 0755
				e = t.WriteHeader(header)
			default:
				b, e := ioutil.ReadFile(p)
				if e != nil {
					return e
				}

				if name == "Dockerfile" {
					withDockerFile = true
					if build.Proxy != "" {
						b = NewDockerfile(b).MixinProxy(build.Proxy)
					}
				}

				header.Mode = 0644
				header.Size = int64(len(b))
				e = t.WriteHeader(header)
				if e != nil {
					return e
				}
				_, e = t.Write(b)
				if e != nil {
					return e
				}
			}
			if e != nil {
				return e
			}
		}
		return nil
	})
}

func (b *Build) callbackForBuild(msg *dockerclient.JSONMessage) {
	if e := msg.Err(); e != nil {
		log.Printf("%s", e)
		return
	}
	msg.Stream = strings.TrimSpace(msg.Stream)
	switch {
	case msg.Stream == "":
		// ignore
	case b.Verbose:
		fmt.Println(msg.Stream)
	case strings.HasPrefix(msg.Stream, "Step "):
		parts := strings.SplitN(msg.Stream[5:], " : ", 2)
		if len(parts) != 2 { // ignore message
			return
		}
		step, e := strconv.Atoi(parts[0])
		if e != nil {
			return
		}
		newMessage := message{
			Step:    step,
			Command: parts[1],
			Started: time.Now(),
		}
		b.msgList = append(b.msgList, newMessage)
		fmt.Printf("%.3d %q", newMessage.Step, newMessage.Command)
	case msg.Stream == "---> Using cache":
		if len(b.msgList) > 0 {
			b.msgList[len(b.msgList)-1].Cached = true
		}
	case strings.HasPrefix(msg.Stream, "---> "):
		if len(b.msgList) > 0 && len(msg.Stream) == 17 {
			i := len(b.msgList) - 1
			b.msgList[i].Finished = time.Now()
			b.msgList[i].Image = msg.Stream[5:]
			status := "BUILT "
			if b.msgList[i].Cached {
				status = "CACHED"
			}
			duration := b.msgList[i].Finished.Sub(b.msgList[i].Started)
			fmt.Printf("\r%.3d [%s] [%s] %q took %5.2fs\n", b.msgList[i].Step, status, b.msgList[i].Image, b.msgList[i].Command, duration.Seconds())
		}
	}
}

func (b *Build) callbackForPush(msg *dockerclient.JSONMessage) {
	if e := msg.Err(); e != nil {
		log.Printf("%s", e)
		return
	}
	msg.Status = strings.TrimSpace(msg.Status)
	switch {
	case msg.Status == "":
		// ignore
	case b.Verbose:
		fmt.Printf("%s\n", msg.Status)
	case msg.Status == "Buffering to disk" && msg.Progress.Total > 0:
		fmt.Printf("\r[%s] Buffering: %4.2f %%", msg.Id, 100.0*float32(msg.Progress.Current)/float32(msg.Progress.Total))
	case msg.Status == "Pushing" && msg.Progress.Total > 0:
		fmt.Printf("\r[%s] Pushing:   %4.2f %%", msg.Id, 100.0*float32(msg.Progress.Current)/float32(msg.Progress.Total))
	case msg.Status == "Image successfully pushed":
		fmt.Printf("\r[%s] Pushing:   ... done\n", msg.Id)
	}
}
<|endoftext|>


<commit_before>package trackello

import (
	"fmt"
	"log"
	"os"

	"github.com/VojtechVitek/go-trello"
	"github.com/klauern/trackello/rest"
	"github.com/spf13/viper"
)

// Trackello represents the connection to Trello for a specific user.
type Trackello struct {
	token  string
	appKey string
	client *trello.Client
}

// Card is both the Trello Card + other stats on the actions in it.
type Card struct {
	card  *trello.Card
	stats *statistics
}

// List is both the Trello List + other stats on the actions in it.
type List struct {
	name  string
	cards map[string]Card
	stats *statistics
}

// NewTrackello will create a `Trackello` type using your preferences application token and appkey.
func NewTrackello() (*Trackello, error) {
	token := viper.GetString("token")
	appKey := viper.GetString("appkey")

	// New Trello Client
	tr, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	return &Trackello{
		token:  token,
		appKey: appKey,
		client: tr,
	}, nil
}

// PrimaryBoard will return a board based on your settings for a primary board.
func (t *Trackello) PrimaryBoard() (trello.Board, error) {
	board, err := t.client.Board(viper.GetString("board"))
	if err != nil {
		log.Fatal(err)
		return *board, err
	}
	return *board, nil
}

// BoardWithId will return the Trello Board given it's ID string.
func (t *Trackello) BoardWithId(id string) (trello.Board, error) {
	board, err := t.client.Board(id)
	if err != nil {
		log.Fatal(err)
		return *board, err
	}
	return *board, nil
}

// Boards will list all of the boards for the authenticated user (i.e. 'me').
func (t *Trackello) Boards() ([]trello.Board, error) {
	member, err := t.client.Member("me")
	if err != nil {
		log.Fatalf("Error getting 'me' Member: %v", err)
		return make([]trello.Board, 0), err
	}
	boards, err := member.Boards()
	return boards, err
}

func (t *Trackello) getCardForAction(a trello.Action) (*trello.Card, error) {
	return t.client.Card(a.Data.Card.Id)
}

// MapBoardActions takes the slice of []trello.Action and maps it to a Card and it's associated List.
func (t *Trackello) MapBoardActions(actions []trello.Action) ([]List, error) {
	listCards := make(map[string]List)
	for _, action := range actions {
		if len(action.Data.Card.Id) > 0 {
			card, err := t.getCardForAction(action)
			if err != nil {
				return nil, err
			}
			list, err := t.client.List(card.IdList)
			if err != nil {
				return nil, err
			}
			lc, ok := listCards[list.Name]
			if !ok {
				cards := make(map[string]Card)
				cards[card.Name] = Card{card: card}
				listCards[list.Name] = List{
					name:  list.Name,
					cards: cards,
				}
				lc = listCards[list.Name]
			}
			if _, cok := lc.cards[card.Name]; !cok {
				newCard := Card{
					card: card,
				}
				lc.cards[card.Name] = newCard
			}
			c, _ := lc.cards[card.Name]
			c.AddCalculation(action)
			lc.cards[card.Name] = c
			listCards[list.Name] = lc
		}
	}
	list := make([]List, len(listCards))
	for _, v := range listCards {
		list = append(list, v)
	}
	return list, nil
}

// Print will print out a list and all of the cards to the command-line.
func (l *List) Print() {
	if len(l.name) > 0 {
		fmt.Printf("%s\n", l.name)
		for _, card := range l.cards {
			fmt.Printf("  * %s\n", card.String())
		}
	}
}

func (c *Card) String() string {
	return fmt.Sprintf("%s %s", c.card.Name, c.stats.PrintStatistics())
}

// Board will pull the Trello Board with an ID.  If id is "", it will pull it from the PrimaryBoard configuration setting.
func (t *Trackello) Board(id string) (trello.Board, error) {
	if id == "" {
		return t.PrimaryBoard()
	}
	return t.BoardWithId(id)
}

// BoardActions will retrieve a slice of trello.Action based on the Board ID.
func BoardActions(id string) ([]trello.Action, error) {
	t, err := NewTrackello()
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	board, err := t.Board(id)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	args := rest.CreateArgsForBoardActions()
	actions, err := board.Actions(args...)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	return actions, err
}
<commit_msg>move some things around<commit_after>package trackello

import (
	"fmt"
	"log"
	"os"

	"github.com/VojtechVitek/go-trello"
	"github.com/klauern/trackello/rest"
	"github.com/spf13/viper"
)

// Trackello represents the connection to Trello for a specific user.
type Trackello struct {
	token  string
	appKey string
	client *trello.Client
}

// Card is both the Trello Card + other stats on the actions in it.
type Card struct {
	card  *trello.Card
	stats *statistics
}

// List is both the Trello List + other stats on the actions in it.
type List struct {
	name  string
	cards map[string]Card
	stats *statistics
}

// NewTrackello will create a `Trackello` type using your preferences application token and appkey.
func NewTrackello() (*Trackello, error) {
	token := viper.GetString("token")
	appKey := viper.GetString("appkey")

	// New Trello Client
	tr, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	return &Trackello{
		token:  token,
		appKey: appKey,
		client: tr,
	}, nil
}

// PrimaryBoard will return a board based on your settings for a primary board.
func (t *Trackello) PrimaryBoard() (trello.Board, error) {
	board, err := t.client.Board(viper.GetString("board"))
	if err != nil {
		log.Fatal(err)
		return *board, err
	}
	return *board, nil
}

// BoardWithId will return the Trello Board given it's ID string.
func (t *Trackello) BoardWithId(id string) (trello.Board, error) {
	board, err := t.client.Board(id)
	if err != nil {
		log.Fatal(err)
		return *board, err
	}
	return *board, nil
}

// Boards will list all of the boards for the authenticated user (i.e. 'me').
func (t *Trackello) Boards() ([]trello.Board, error) {
	member, err := t.client.Member("me")
	if err != nil {
		log.Fatalf("Error getting 'me' Member: %v", err)
		return make([]trello.Board, 0), err
	}
	boards, err := member.Boards()
	return boards, err
}

func (t *Trackello) getCardForAction(a trello.Action) (*trello.Card, error) {
	return t.client.Card(a.Data.Card.Id)
}

// MapBoardActions takes the slice of []trello.Action and maps it to a Card and it's associated List.
func (t *Trackello) MapBoardActions(actions []trello.Action) ([]List, error) {
	listCards := make(map[string]List)
	for _, action := range actions {
		if len(action.Data.Card.Id) > 0 {
			card, err := t.getCardForAction(action)
			if err != nil {
				return nil, err
			}
			list, err := t.client.List(card.IdList)
			if err != nil {
				return nil, err
			}
			lc, ok := listCards[list.Name]
			if !ok {
				lc = newCardMap(card, listCards, list.Name)
				lc = listCards[list.Name]
			}
			if _, cok := lc.cards[card.Name]; !cok {
				newCard := Card{
					card: card,
				}
				lc.cards[card.Name] = newCard
			}
			c, _ := lc.cards[card.Name]
			c.AddCalculation(action)
			lc.cards[card.Name] = c
			listCards[list.Name] = lc
		}
	}
	return makeList(listCards), nil
}

func newCardMap(card trello.Card, listCards map[string]List, listName string) map[string]Card {
	cards := make(map[string]Card)
	cards[card.Name] = Card{card: card}
	listCards[listName] = List{
		name:  listName,
		cards: cards,
	}
	return cards
}

func makeList(listMap map[string]List) []List {
	list := make([]List, len(listMap))
	for _, v := range listMap {
		list = append(list, v)
	}
	return list
}

// Print will print out a list and all of the cards to the command-line.
func (l *List) Print() {
	if len(l.name) > 0 {
		fmt.Printf("%s\n", l.name)
		for _, card := range l.cards {
			fmt.Printf("  * %s\n", card.String())
		}
	}
}

func (c *Card) String() string {
	return fmt.Sprintf("%s %s", c.card.Name, c.stats.PrintStatistics())
}

// Board will pull the Trello Board with an ID.  If id is "", it will pull it from the PrimaryBoard configuration setting.
func (t *Trackello) Board(id string) (trello.Board, error) {
	if id == "" {
		return t.PrimaryBoard()
	}
	return t.BoardWithId(id)
}

// BoardActions will retrieve a slice of trello.Action based on the Board ID.
func BoardActions(id string) ([]trello.Action, error) {
	t, err := NewTrackello()
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	board, err := t.Board(id)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	args := rest.CreateArgsForBoardActions()
	actions, err := board.Actions(args...)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	return actions, err
}
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"github.com/Supernomad/quantum/common"
	"github.com/Supernomad/quantum/logger"
)

type GCM struct {
	log  *logger.Logger
	ecdh *ECDH
}

func RandomBytes(b []byte) ([]byte, error) {
	_, err := rand.Read(b)
	return b, err
}

func (gcm *GCM) Seal(payload *common.Payload) (*common.Payload, bool) {
	// Generate the shared key
	key := gcm.ecdh.GenerateSharedSecret(payload.PublicKey)

	// Grab a new random nonce
	_, err := RandomBytes(payload.Nonce)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating random nonce:", err)
		return payload, false
	}

	// Get the block ciper
	block, err := aes.NewCipher(key)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating ciper:", err)
		return payload, false
	}

	// Get the GCM block wrapper
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating gcm wrapper:", err)
		return payload, false
	}

	// Seal the packet and associated meta data
	aesgcm.Seal(payload.Packet[:0], payload.Nonce, payload.Packet, nil)

	copy(payload.Key, gcm.ecdh.PublicKey[:])
	return payload, true
}

func (gcm *GCM) Unseal(payload *common.Payload) (*common.Payload, bool) {
	// Generate the shared key
	key := gcm.ecdh.GenerateSharedSecret(payload.Key)

	// Get the block ciper
	block, err := aes.NewCipher(key)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating ciper:", err)
		return payload, false
	}

	// Get the GCM block wrapper
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating gcm wrapper:", err)
		return payload, false
	}

	_, err = aesgcm.Open(payload.Packet[:0], payload.Nonce, payload.Packet, nil)
	if err != nil {
		gcm.log.Error("[GCM]", "Error decrypting/authenticating packet:", err)
		return payload, false
	}

	return payload, true
}

func NewGCM(log *logger.Logger, ecdh *ECDH) *GCM {
	return &GCM{log: log, ecdh: ecdh}
}
<commit_msg>Missed minor change to have gcm include the senders public key in the verification process<commit_after>package crypto

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"github.com/Supernomad/quantum/common"
	"github.com/Supernomad/quantum/logger"
)

type GCM struct {
	log  *logger.Logger
	ecdh *ECDH
}

func RandomBytes(b []byte) ([]byte, error) {
	_, err := rand.Read(b)
	return b, err
}

func (gcm *GCM) Seal(payload *common.Payload) (*common.Payload, bool) {
	// Generate the shared key
	key := gcm.ecdh.GenerateSharedSecret(payload.PublicKey)

	// Grab a new random nonce
	_, err := RandomBytes(payload.Nonce)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating random nonce:", err)
		return payload, false
	}

	// Get the block ciper
	block, err := aes.NewCipher(key)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating ciper:", err)
		return payload, false
	}

	// Get the GCM block wrapper
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating gcm wrapper:", err)
		return payload, false
	}

	// Seal the packet and associated meta data
	aesgcm.Seal(payload.Packet[:0], payload.Nonce, payload.Packet, gcm.ecdh.PublicKey[:])

	copy(payload.Key, gcm.ecdh.PublicKey[:])
	return payload, true
}

func (gcm *GCM) Unseal(payload *common.Payload) (*common.Payload, bool) {
	// Generate the shared key
	key := gcm.ecdh.GenerateSharedSecret(payload.Key)

	// Get the block ciper
	block, err := aes.NewCipher(key)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating ciper:", err)
		return payload, false
	}

	// Get the GCM block wrapper
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		gcm.log.Error("[GCM]", "Error generating gcm wrapper:", err)
		return payload, false
	}

	_, err = aesgcm.Open(payload.Packet[:0], payload.Nonce, payload.Packet, payload.Key)
	if err != nil {
		gcm.log.Error("[GCM]", "Error decrypting/authenticating packet:", err)
		return payload, false
	}

	return payload, true
}

func NewGCM(log *logger.Logger, ecdh *ECDH) *GCM {
	return &GCM{log: log, ecdh: ecdh}
}
<|endoftext|>


<commit_before>//  Copyright 2015 Walter Schulze
//
//  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 jsonschema

import (
	"encoding/json"
	"fmt"
	"github.com/katydid/katydid/funcs"
	"github.com/katydid/katydid/relapse/ast"
	"github.com/katydid/katydid/relapse/combinator"
	"sort"
)

//TODO
func TranslateDraft4(jsonSchema []byte) (*relapse.Grammar, error) {
	schema := &Schema{}
	if err := json.Unmarshal(jsonSchema, schema); err != nil {
		panic(err)
	}
	p, err := translate(schema)
	if err != nil {
		return nil, err
	}
	return relapse.NewGrammar(relapse.RefLookup(map[string]*relapse.Pattern{"main": p})), nil
}

func translate(schema *Schema) (*relapse.Pattern, error) {
	if schema.Id != nil {
		return nil, fmt.Errorf("id not supported")
	}
	if schema.Default != nil {
		return nil, fmt.Errorf("default not supported")
	}
	if schema.HasNumericConstraints() {
		p, err := translateNumeric(schema)
		return p, err
	}
	if schema.HasStringConstraints() {
		p, err := translateString(schema)
		return p, err
	}
	if schema.HasArrayConstraints() {
		return nil, fmt.Errorf("array not supported")
	}
	if schema.HasObjectConstraints() {
		return nil, fmt.Errorf("object not supported")
	}
	if schema.HasInstanceConstraints() {
		return nil, fmt.Errorf("instance not supported")
	}

	if schema.Ref != nil {
		return nil, fmt.Errorf("ref not supported")
	}
	if schema.Format != nil {
		return nil, fmt.Errorf("format not supported")
	}
	return relapse.NewEmptySet(), nil
}

func translateObject(schema *Schema) (*relapse.Pattern, error) {
	if schema.MaxProperties != nil {
		return nil, fmt.Errorf("maxProperties not supported")
	}
	if schema.MinProperties > 0 {
		return nil, fmt.Errorf("minProperties not supported")
	}
	required := make(map[string]struct{})
	for _, req := range schema.Required {
		required[req] = struct{}{}
	}
	requiredIf := make(map[string][]string)
	moreProperties := make(map[string]*Schema)
	if schema.Dependencies != nil {
		deps := *schema.Dependencies
		for name, dep := range deps {
			if len(dep.RequiredProperty) > 0 {
				requiredIf[name] = deps[name].RequiredProperty
			} else {
				moreProperties[name] = deps[name].Schema
			}
		}
	}
	additional := relapse.NewZAny()
	if schema.AdditionalProperties != nil {
		if schema.AdditionalProperties.Bool != nil && !(*schema.AdditionalProperties.Bool) {
			additional = relapse.NewEmpty()
		} else if schema.AdditionalProperties.Type != TypeUnknown {
			var typ *relapse.Pattern
			switch schema.AdditionalProperties.Type {
			case TypeArray:
				return nil, fmt.Errorf("type array in additionalProperties not supported")
			case TypeBoolean:
				typ = combinator.Value(funcs.TypeBool(funcs.BoolVar()))
			case TypeInteger:
				typ = combinator.Value(funcs.TypeDouble(Integer()))
			case TypeNull:
				typ = relapse.NewEmpty()
			case TypeNumber:
				typ = combinator.Value(funcs.TypeDouble(Number()))
			case TypeObject:
				return nil, fmt.Errorf("type object in additionalProperties not supported")
			case TypeString:
				typ = combinator.Value(funcs.TypeString(funcs.StringVar()))
			default:
				panic(fmt.Sprintf("unknown simpletype in additional properties: %s",
					schema.AdditionalProperties.Type))
			}
			additional = relapse.NewZeroOrMore(
				relapse.NewTreeNode(relapse.NewAnyName(), typ),
			)
		}
	}
	names := []string{}
	for name, _ := range schema.Properties {
		names = append(names, name)
	}
	sort.Strings(names)
	patterns := make(map[string]*relapse.Pattern)
	for _, name := range names {
		child, err := translate(schema.Properties[name])
		if err != nil {
			return nil, err
		}
		patterns[name] = relapse.NewTreeNode(relapse.NewName(name), child)
	}
	_ = additional
	for _, name := range names {
		if requires, ok := requiredIf[name]; ok {
			_ = requires
		}
		if s, ok := moreProperties[name]; ok {
			_ = s
		}
		if _, ok := required[name]; !ok {
			//prop = optional(prop)
		}
	}
	if len(schema.PatternProperties) > 0 {
		return nil, fmt.Errorf("patternProperties not supported")
	}
	panic("todo")
}

func optional(p *relapse.Pattern) *relapse.Pattern {
	return relapse.NewOr(relapse.NewEmpty(), p)
}

func translateNumeric(schema *Schema) (*relapse.Pattern, error) {
	v := Number()
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with numeric constraints %#v", schema)
		}
		if schema.GetType()[0] == TypeInteger {
			v = Integer()
		} else if schema.GetType()[0] != TypeNumber {
			return nil, fmt.Errorf("%v not supported with numeric constraints", schema.GetType()[0])
		}
	}
	list := []funcs.Bool{}
	if schema.MultipleOf != nil {
		mult := MultipleOf(v, funcs.DoubleConst(*schema.MultipleOf))
		list = append(list, mult)
	}
	if schema.Maximum != nil {
		lt := funcs.DoubleLE(v, funcs.DoubleConst(*schema.Maximum))
		if schema.ExclusiveMaximum {
			lt = funcs.DoubleLt(v, funcs.DoubleConst(*schema.Maximum))
		}
		list = append(list, lt)
	}
	if schema.Minimum != nil {
		lt := funcs.DoubleGE(v, funcs.DoubleConst(*schema.Minimum))
		if schema.ExclusiveMinimum {
			lt = funcs.DoubleGt(v, funcs.DoubleConst(*schema.Minimum))
		}
		list = append(list, lt)
	}
	return combinator.Value(and(list)), nil
}

func and(list []funcs.Bool) funcs.Bool {
	if len(list) == 0 {
		panic("unreachable")
	}
	if len(list) == 1 {
		return list[0]
	}
	return funcs.And(list[0], and(list[1:]))
}

func translateString(schema *Schema) (*relapse.Pattern, error) {
	v := funcs.StringVar()
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with string constraints %#v", schema)
		}
		if schema.GetType()[0] != TypeString {
			return nil, fmt.Errorf("%v not supported with string constraints", schema.GetType()[0])
		}
	}
	list := []funcs.Bool{}
	if schema.MaxLength != nil {
		list = append(list, MaxLength(v, *schema.MaxLength))
	}
	if schema.MinLength > 0 {
		list = append(list, MinLength(v, schema.MinLength))
	}
	if schema.Pattern != nil {
		list = append(list, funcs.Regex(funcs.StringConst(*schema.Pattern), v))
	}
	return combinator.Value(and(list)), nil
}

func translateArray(schema *Schema) (*relapse.Pattern, error) {
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with array constraints %#v", schema)
		}
		if schema.GetType()[0] != TypeArray {
			return nil, fmt.Errorf("%v not supported with array constraints", schema.GetType()[0])
		}
	}
	if schema.UniqueItems {
		return nil, fmt.Errorf("uniqueItems are not supported")
	}
	if schema.MaxItems != nil {
		return nil, fmt.Errorf("maxItems are not supported")
	}
	if schema.MinItems > 0 {
		return nil, fmt.Errorf("minItems are not supported")
	}
	additionalItems := true
	if schema.AdditionalItems != nil {
		if schema.Items == nil {
			//any
		}
		if schema.AdditionalItems.Bool != nil {
			additionalItems = *schema.AdditionalItems.Bool
		}
		if !additionalItems && (schema.MaxLength != nil || schema.MinLength > 0) {
			return nil, fmt.Errorf("additionalItems: false and (maxItems|minItems) are not supported together")
		}
		return nil, fmt.Errorf("additionalItems are not supported")
	}
	if schema.Items != nil {
		if schema.Items.Object != nil {
			if schema.Items.Object.Type == nil {
				//any
			} else {
				typ := schema.Items.Object.GetType()[0]
				_ = typ
			}
			//TODO this specifies the type of every item in the list
		} else if schema.Items.Array != nil {
			if !additionalItems {
				//TODO this specifies the length of the list as well as each ordered element's type
				//  if no type is set then any type is accepted
				maxLength := len(schema.Items.Array)
				_ = maxLength
			} else {
				//TODO this specifies the types of the first few ordered items in the list
				//  if no type is set then any type is accepted
			}

		}
		return nil, fmt.Errorf("items are not supported")
	}
	return nil, nil
}
<commit_msg>some instance support<commit_after>//  Copyright 2015 Walter Schulze
//
//  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 jsonschema

import (
	"encoding/json"
	"fmt"
	"github.com/katydid/katydid/funcs"
	"github.com/katydid/katydid/relapse/ast"
	"github.com/katydid/katydid/relapse/combinator"
	"sort"
)

//TODO
func TranslateDraft4(jsonSchema []byte) (*relapse.Grammar, error) {
	schema := &Schema{}
	if err := json.Unmarshal(jsonSchema, schema); err != nil {
		panic(err)
	}
	p, err := translate(schema)
	if err != nil {
		return nil, err
	}
	return relapse.NewGrammar(relapse.RefLookup(map[string]*relapse.Pattern{"main": p})), nil
}

func translate(schema *Schema) (*relapse.Pattern, error) {
	if schema.Id != nil {
		return nil, fmt.Errorf("id not supported")
	}
	if schema.Default != nil {
		return nil, fmt.Errorf("default not supported")
	}
	if schema.HasNumericConstraints() {
		p, err := translateNumeric(schema)
		return p, err
	}
	if schema.HasStringConstraints() {
		p, err := translateString(schema)
		return p, err
	}
	if schema.HasArrayConstraints() {
		return nil, fmt.Errorf("array not supported")
	}
	if schema.HasObjectConstraints() {
		p, err := translateObject(schema)
		return p, err
	}
	if schema.HasInstanceConstraints() {
		p, err := translateInstance(schema)
		return p, err
	}

	if schema.Ref != nil {
		return nil, fmt.Errorf("ref not supported")
	}
	if schema.Format != nil {
		return nil, fmt.Errorf("format not supported")
	}
	return relapse.NewEmptySet(), nil
}

func translates(schemas []*Schema) ([]*relapse.Pattern, error) {
	ps := make([]*relapse.Pattern, len(schemas))
	for i := range schemas {
		var err error
		ps[i], err = translate(schemas[i])
		if err != nil {
			return nil, err
		}
	}
	return ps, nil
}

func rest(patterns []*relapse.Pattern, index int) []*relapse.Pattern {
	return append(patterns[:index], patterns[index+1:]...)
}

func translateInstance(schema *Schema) (*relapse.Pattern, error) {
	if len(schema.Definitions) > 0 {
		return nil, fmt.Errorf("definitions not supported")
	}
	if len(schema.Enum) > 0 {
		return nil, fmt.Errorf("enum not supported")
	}
	if schema.Type != nil {
		types := *schema.Type
		if len(types) == 1 {
			return translateType(types[0])
		}
		ps := make([]*relapse.Pattern, len(types))
		for i := range types {
			var err error
			ps[i], err = translateType(types[i])
			if err != nil {
				return nil, err
			}
		}
		return relapse.NewOr(ps...), nil
	}
	if len(schema.AllOf) > 0 {
		ps, err := translates(schema.AllOf)
		if err != nil {
			return nil, err
		}
		return relapse.NewAnd(ps...), nil
	}
	if len(schema.AnyOf) > 0 {
		ps, err := translates(schema.AnyOf)
		if err != nil {
			return nil, err
		}
		return relapse.NewOr(ps...), nil
	}
	if len(schema.OneOf) > 0 {
		ps, err := translates(schema.OneOf)
		if err != nil {
			return nil, err
		}
		if len(ps) == 0 {
			return nil, fmt.Errorf("oneof of zero schemas not supported")
		}
		if len(ps) == 1 {
			return ps[0], nil
		}
		orps := make([]*relapse.Pattern, len(ps))
		for i := range ps {
			other := rest(ps, i)
			orps[i] = relapse.NewAnd(
				ps[i],
				relapse.NewNot(
					relapse.NewOr(other...),
				),
			)
		}
		return relapse.NewOr(orps...), nil
	}
	if schema.Not != nil {
		p, err := translate(schema.Not)
		if err != nil {
			return nil, err
		}
		return relapse.NewNot(p), nil
	}
	panic("unreachable object")
}

func translateType(typ SimpleType) (*relapse.Pattern, error) {
	switch typ {
	case TypeArray:
		return nil, fmt.Errorf("type array not supported")
	case TypeBoolean:
		return combinator.Value(funcs.TypeBool(funcs.BoolVar())), nil
	case TypeInteger:
		return combinator.Value(funcs.TypeDouble(Integer())), nil
	case TypeNull:
		return relapse.NewEmpty(), nil
	case TypeNumber:
		return combinator.Value(funcs.TypeDouble(Number())), nil
	case TypeObject:
		return nil, fmt.Errorf("type object not supported")
	case TypeString:
		return combinator.Value(funcs.TypeString(funcs.StringVar())), nil
	}
	panic(fmt.Sprintf("unknown simpletype: %s", typ))
}

func translateObject(schema *Schema) (*relapse.Pattern, error) {
	if schema.MaxProperties != nil {
		return nil, fmt.Errorf("maxProperties not supported")
	}
	if schema.MinProperties > 0 {
		return nil, fmt.Errorf("minProperties not supported")
	}
	required := make(map[string]struct{})
	for _, req := range schema.Required {
		required[req] = struct{}{}
	}
	requiredIf := make(map[string][]string)
	moreProperties := make(map[string]*Schema)
	if schema.Dependencies != nil {
		deps := *schema.Dependencies
		for name, dep := range deps {
			if len(dep.RequiredProperty) > 0 {
				requiredIf[name] = deps[name].RequiredProperty
			} else {
				moreProperties[name] = deps[name].Schema
			}
		}
	}
	additional := relapse.NewZAny()
	if schema.AdditionalProperties != nil {
		if schema.AdditionalProperties.Bool != nil && !(*schema.AdditionalProperties.Bool) {
			additional = relapse.NewEmpty()
		} else if schema.AdditionalProperties.Type != TypeUnknown {
			typ, err := translateType(schema.AdditionalProperties.Type)
			if err != nil {
				return nil, err
			}
			additional = relapse.NewZeroOrMore(
				relapse.NewTreeNode(relapse.NewAnyName(), typ),
			)
		}
	}
	names := []string{}
	for name, _ := range schema.Properties {
		names = append(names, name)
	}
	sort.Strings(names)
	patterns := make(map[string]*relapse.Pattern)
	for _, name := range names {
		child, err := translate(schema.Properties[name])
		if err != nil {
			return nil, err
		}
		patterns[name] = relapse.NewTreeNode(relapse.NewName(name), child)
	}
	_ = additional
	for _, name := range names {
		if requires, ok := requiredIf[name]; ok {
			_ = requires
		}
		if s, ok := moreProperties[name]; ok {
			_ = s
		}
		if _, ok := required[name]; !ok {
			//prop = optional(prop)
		}
	}
	if len(schema.PatternProperties) > 0 {
		return nil, fmt.Errorf("patternProperties not supported")
	}
	return nil, fmt.Errorf("object not fully supported")
}

func optional(p *relapse.Pattern) *relapse.Pattern {
	return relapse.NewOr(relapse.NewEmpty(), p)
}

func translateNumeric(schema *Schema) (*relapse.Pattern, error) {
	v := Number()
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with numeric constraints %#v", schema)
		}
		if schema.GetType()[0] == TypeInteger {
			v = Integer()
		} else if schema.GetType()[0] != TypeNumber {
			return nil, fmt.Errorf("%v not supported with numeric constraints", schema.GetType()[0])
		}
	}
	list := []funcs.Bool{}
	if schema.MultipleOf != nil {
		mult := MultipleOf(v, funcs.DoubleConst(*schema.MultipleOf))
		list = append(list, mult)
	}
	if schema.Maximum != nil {
		lt := funcs.DoubleLE(v, funcs.DoubleConst(*schema.Maximum))
		if schema.ExclusiveMaximum {
			lt = funcs.DoubleLt(v, funcs.DoubleConst(*schema.Maximum))
		}
		list = append(list, lt)
	}
	if schema.Minimum != nil {
		lt := funcs.DoubleGE(v, funcs.DoubleConst(*schema.Minimum))
		if schema.ExclusiveMinimum {
			lt = funcs.DoubleGt(v, funcs.DoubleConst(*schema.Minimum))
		}
		list = append(list, lt)
	}
	return combinator.Value(and(list)), nil
}

func and(list []funcs.Bool) funcs.Bool {
	if len(list) == 0 {
		panic("unreachable")
	}
	if len(list) == 1 {
		return list[0]
	}
	return funcs.And(list[0], and(list[1:]))
}

func translateString(schema *Schema) (*relapse.Pattern, error) {
	v := funcs.StringVar()
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with string constraints %#v", schema)
		}
		if schema.GetType()[0] != TypeString {
			return nil, fmt.Errorf("%v not supported with string constraints", schema.GetType()[0])
		}
	}
	list := []funcs.Bool{}
	if schema.MaxLength != nil {
		list = append(list, MaxLength(v, *schema.MaxLength))
	}
	if schema.MinLength > 0 {
		list = append(list, MinLength(v, schema.MinLength))
	}
	if schema.Pattern != nil {
		list = append(list, funcs.Regex(funcs.StringConst(*schema.Pattern), v))
	}
	return combinator.Value(and(list)), nil
}

func translateArray(schema *Schema) (*relapse.Pattern, error) {
	if schema.Type != nil {
		if len(*schema.Type) > 1 {
			return nil, fmt.Errorf("list of types not supported with array constraints %#v", schema)
		}
		if schema.GetType()[0] != TypeArray {
			return nil, fmt.Errorf("%v not supported with array constraints", schema.GetType()[0])
		}
	}
	if schema.UniqueItems {
		return nil, fmt.Errorf("uniqueItems are not supported")
	}
	if schema.MaxItems != nil {
		return nil, fmt.Errorf("maxItems are not supported")
	}
	if schema.MinItems > 0 {
		return nil, fmt.Errorf("minItems are not supported")
	}
	additionalItems := true
	if schema.AdditionalItems != nil {
		if schema.Items == nil {
			//any
		}
		if schema.AdditionalItems.Bool != nil {
			additionalItems = *schema.AdditionalItems.Bool
		}
		if !additionalItems && (schema.MaxLength != nil || schema.MinLength > 0) {
			return nil, fmt.Errorf("additionalItems: false and (maxItems|minItems) are not supported together")
		}
		return nil, fmt.Errorf("additionalItems are not supported")
	}
	if schema.Items != nil {
		if schema.Items.Object != nil {
			if schema.Items.Object.Type == nil {
				//any
			} else {
				typ := schema.Items.Object.GetType()[0]
				_ = typ
			}
			//TODO this specifies the type of every item in the list
		} else if schema.Items.Array != nil {
			if !additionalItems {
				//TODO this specifies the length of the list as well as each ordered element's type
				//  if no type is set then any type is accepted
				maxLength := len(schema.Items.Array)
				_ = maxLength
			} else {
				//TODO this specifies the types of the first few ordered items in the list
				//  if no type is set then any type is accepted
			}

		}
		return nil, fmt.Errorf("items are not supported")
	}
	return nil, nil
}
<|endoftext|>


<commit_before>// +build windows

/*
Binary vnc_to_vm connects VNC to a remote VM via its socket file over SSH.
- connect to the local SSH agent in cygwin
- connect to the remote server with SSH
- relay the remote VNC socket file over SSH into a local listener
- start VNC viewer with it

Requirements:
- cygwin, with ssh and socat installed, ssh-agent running
- socat on the destination, TightVNC viewer locally

To avoid opening a command window:
  go build -ldflags -H=windowsgui vnc_to_vm.go
*/

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os/exec"
	"regexp"
	"strings"

	"github.com/StalkR/misc/windows/cygwin"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

var (
	flagHost   = flag.String("host", "", "Host to SSH to (user@host[:port]).")
	flagSocket = flag.String("socket", "", "Path to remote VNC socket.")
	flagViewer = flag.String("viewer", `C:\Program Files\TightVNC\tvnviewer.exe`, "Path to local TightVNC viewer.")
)

func main() {
	flag.Parse()
	if err := launch(); err != nil {
		log.Fatal(err)
	}
}

func launch() error {
	if *flagHost == "" || *flagSocket == "" {
		flag.PrintDefaults()
		return nil
	}
	userHost := regexp.MustCompile(`^([^@]*)@(.*)$`).FindStringSubmatch(*flagHost)
	if len(userHost) == 0 {
		return fmt.Errorf("invalid host")
	}
	user, host := userHost[1], userHost[2]
	if strings.Contains(*flagSocket, `"`) || strings.Contains(*flagSocket, `'`) {
		return fmt.Errorf("invalid socket path")
	}
	socket := *flagSocket

	vnc, err := vncViewer()
	if err != nil {
		return err
	}
	defer vnc.Close()
	return sshToVNC(user, host, socket, vnc)
}

func vncViewer() (io.ReadWriteCloser, error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, err
	}
	port := ln.Addr().(*net.TCPAddr).Port
	cmd := exec.Command(*flagViewer, fmt.Sprintf("localhost::%d", port))
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	conn, err := ln.Accept()
	if err != nil {
		cmd.Wait()
		return nil, err
	}
	if err := ln.Close(); err != nil {
		cmd.Wait()
		return nil, err
	}
	log.Printf("VNC viewer connected (localhost:%v)", port)
	return &vncConn{conn, cmd}, nil
}

// vncConn implements io.ReadWriteCloser.
type vncConn struct {
	conn io.ReadWriteCloser
	cmd  *exec.Cmd
}

func (s *vncConn) Read(p []byte) (int, error)  { return s.conn.Read(p) }
func (s *vncConn) Write(p []byte) (int, error) { return s.conn.Write(p) }
func (s *vncConn) Close() error {
	var errors []error
	if err := s.conn.Close(); err != nil {
		errors = append(errors, err)
	}
	if err := s.cmd.Wait(); err != nil {
		errors = append(errors, err)
	}
	if len(errors) > 0 {
		if len(errors) == 1 {
			return errors[0]
		}
		return fmt.Errorf("close: %v errors, first: %v", len(errors), errors[0])
	}
	return nil
}

func sshToVNC(user, host, socket string, vnc io.ReadWriter) error {
	client, err := connect(user, host)
	if err != nil {
		return err
	}
	defer client.Close()
	log.Print("Connected to SSH server")
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()
	session.Stdin = vnc
	session.Stdout = vnc
	if err := session.Start(fmt.Sprintf(`socat 'UNIX:"%s"' -`, socket)); err != nil {
		return err
	}
	log.Print("Connected to VNC via SSH")
	return session.Wait()
}

func connect(user, host string) (*ssh.Client, error) {
	ag, err := cygwin.SSHAgent()
	if err != nil {
		return nil, err
	}
	defer ag.Close()
	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeysCallback(agent.NewClient(ag).Signers),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	return ssh.Dial("tcp", host, config)
}
<commit_msg>kvm/vnc_to_vm: update cygwin library, moved to github.com/StalkR/winpulse<commit_after>// +build windows

/*
Binary vnc_to_vm connects VNC to a remote VM via its socket file over SSH.
- connect to the local SSH agent in cygwin
- connect to the remote server with SSH
- relay the remote VNC socket file over SSH into a local listener
- start VNC viewer with it

Requirements:
- cygwin, with ssh and socat installed, ssh-agent running
- socat on the destination, TightVNC viewer locally

To avoid opening a command window:
  go build -ldflags -H=windowsgui vnc_to_vm.go
*/

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os/exec"
	"regexp"
	"strings"

	"github.com/StalkR/winpulse/cygwin"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

var (
	flagHost   = flag.String("host", "", "Host to SSH to (user@host[:port]).")
	flagSocket = flag.String("socket", "", "Path to remote VNC socket.")
	flagViewer = flag.String("viewer", `C:\Program Files\TightVNC\tvnviewer.exe`, "Path to local TightVNC viewer.")
)

func main() {
	flag.Parse()
	if err := launch(); err != nil {
		log.Fatal(err)
	}
}

func launch() error {
	if *flagHost == "" || *flagSocket == "" {
		flag.PrintDefaults()
		return nil
	}
	userHost := regexp.MustCompile(`^([^@]*)@(.*)$`).FindStringSubmatch(*flagHost)
	if len(userHost) == 0 {
		return fmt.Errorf("invalid host")
	}
	user, host := userHost[1], userHost[2]
	if strings.Contains(*flagSocket, `"`) || strings.Contains(*flagSocket, `'`) {
		return fmt.Errorf("invalid socket path")
	}
	socket := *flagSocket

	vnc, err := vncViewer()
	if err != nil {
		return err
	}
	defer vnc.Close()
	return sshToVNC(user, host, socket, vnc)
}

func vncViewer() (io.ReadWriteCloser, error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, err
	}
	port := ln.Addr().(*net.TCPAddr).Port
	cmd := exec.Command(*flagViewer, fmt.Sprintf("localhost::%d", port))
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	conn, err := ln.Accept()
	if err != nil {
		cmd.Wait()
		return nil, err
	}
	if err := ln.Close(); err != nil {
		cmd.Wait()
		return nil, err
	}
	log.Printf("VNC viewer connected (localhost:%v)", port)
	return &vncConn{conn, cmd}, nil
}

// vncConn implements io.ReadWriteCloser.
type vncConn struct {
	conn io.ReadWriteCloser
	cmd  *exec.Cmd
}

func (s *vncConn) Read(p []byte) (int, error)  { return s.conn.Read(p) }
func (s *vncConn) Write(p []byte) (int, error) { return s.conn.Write(p) }
func (s *vncConn) Close() error {
	var errors []error
	if err := s.conn.Close(); err != nil {
		errors = append(errors, err)
	}
	if err := s.cmd.Wait(); err != nil {
		errors = append(errors, err)
	}
	if len(errors) > 0 {
		if len(errors) == 1 {
			return errors[0]
		}
		return fmt.Errorf("close: %v errors, first: %v", len(errors), errors[0])
	}
	return nil
}

func sshToVNC(user, host, socket string, vnc io.ReadWriter) error {
	client, err := connect(user, host)
	if err != nil {
		return err
	}
	defer client.Close()
	log.Print("Connected to SSH server")
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()
	session.Stdin = vnc
	session.Stdout = vnc
	if err := session.Start(fmt.Sprintf(`socat 'UNIX:"%s"' -`, socket)); err != nil {
		return err
	}
	log.Print("Connected to VNC via SSH")
	return session.Wait()
}

func connect(user, host string) (*ssh.Client, error) {
	ag, err := cygwin.SSHAgent()
	if err != nil {
		return nil, err
	}
	defer ag.Close()
	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeysCallback(agent.NewClient(ag).Signers),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	return ssh.Dial("tcp", host, config)
}
<|endoftext|>