<commit_before>package consultant_test

import (
	"testing"

	"fmt"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/myENA/consultant"
)

const (
	quorumWaitDuration = 10 * time.Second

	siblingLocatorClusterCount = 3
	siblingLocatorServiceName  = "locator-test-service"
	siblingLocatorServiceAddr  = "127.0.0.1"
)

// TODO: Move cluster, service, and locator creation to discrete function

func TestSiblingLocator_Current(t *testing.T) {
	localCluster := makeCluster(t, siblingLocatorClusterCount)
	defer localCluster.shutdown()

	locators := make([]*consultant.SiblingLocator, siblingLocatorClusterCount)
	results := make([][]consultant.Siblings, siblingLocatorClusterCount)

	t.Run("register services", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := localCluster.client(i).Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name:    siblingLocatorServiceName,
				Address: siblingLocatorServiceAddr,
				Port:    randomPort(),
			})

			if nil != err {
				t.Logf("Unable to register service \"%d\": %v", i, err)
				t.FailNow()
			}
		}
	})

	// TODO: Don't use a sleep...?
	time.Sleep(quorumWaitDuration)

	t.Run("create locators", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			svcs, _, err := localCluster.client(i).Catalog().Service(siblingLocatorServiceName, "", nil)
			if nil != err {
				t.Logf("Unable to locate services in node \"%d\": %v", i, err)
				t.FailNow()
			}

			if siblingLocatorClusterCount != len(svcs) {
				t.Logf("Expected to see \"%d\" services, saw \"%d\"", siblingLocatorClusterCount, len(svcs))
				t.FailNow()
			}

			for _, svc := range svcs {
				if svc.Node == localCluster.server(i).Config.NodeName && svc.ServiceName == siblingLocatorServiceName {
					var err error
					locators[i], err = consultant.NewSiblingLocatorWithCatalogService(localCluster.client(i), svc)
					if nil != err {
						t.Logf("Unable to create locator for node \"%d\": %v", i, err)
						t.FailNow()
					}

					results[i] = make([]consultant.Siblings, 0)

					// :|
					func(i int, locator *consultant.SiblingLocator) {
						locator.AddCallback("", func(_ uint64, siblings consultant.Siblings) {
							results[i] = append(results[i], siblings)
						})
					}(i, locators[i])
				}
			}

			if nil == locators[i] {
				t.Logf("Unable to locate service for node \"%s\"", localCluster.server(i).Config.NodeName)
				t.FailNow()
			}
		}
	})

	t.Run("fetch current", func(t *testing.T) {
		// spread the word
		for i := 0; i < siblingLocatorClusterCount; i++ {
			locators[i].Current(false, true, nil)
		}

		// wait for callback routines to finish...
		time.Sleep(1 * time.Second)

		// Should only have 1 entry
		for i := 0; i < siblingLocatorClusterCount; i++ {
			if 1 != len(results[i]) {
				t.Logf("Expected \"results[%d]\" to be length 1, saw \"%d\"", i, len(results[i]))
				t.FailNow()
			}
		}

		for i := 0; i < siblingLocatorClusterCount; i++ {
			if siblingLocatorClusterCount-1 != len(results[i][0]) {
				t.Logf(
					"Expected node \"%d\" result \"0\" length \"%d\", saw \"%d\"",
					i,
					siblingLocatorClusterCount-1,
					len(results[i][0]))
				t.FailNow()
			}
		}
	})
}

func TestSiblingLocator_Watchers(t *testing.T) {
	localCluster := makeCluster(t, siblingLocatorClusterCount)

	locators := make([]*consultant.SiblingLocator, siblingLocatorClusterCount)
	results := make([][]consultant.Siblings, siblingLocatorClusterCount)

	defer func() {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			locators[i].StopWatcher()
		}
		localCluster.shutdown()
	}()

	t.Run("register services", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := localCluster.client(i).Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name:    siblingLocatorServiceName,
				Address: siblingLocatorServiceAddr,
				Port:    randomPort(),
			})

			if nil != err {
				t.Logf("Unable to register service \"%d\": %v", i, err)
				t.FailNow()
			}
		}
	})

	// TODO: Don't use a sleep...?
	time.Sleep(quorumWaitDuration)

	t.Run("create locators", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			svcs, _, err := localCluster.client(i).Catalog().Service(siblingLocatorServiceName, "", nil)
			if nil != err {
				t.Logf("Unable to locate services in node \"%d\": %v", i, err)
				t.FailNow()
			}

			if siblingLocatorClusterCount != len(svcs) {
				t.Logf("Expected to see \"%d\" services, saw \"%d\"", siblingLocatorClusterCount, len(svcs))
				t.FailNow()
			}

			for _, svc := range svcs {
				if svc.Node == localCluster.server(i).Config.NodeName && svc.ServiceName == siblingLocatorServiceName {
					var err error
					locators[i], err = consultant.NewSiblingLocatorWithCatalogService(localCluster.client(i), svc)
					if nil != err {
						t.Logf("Unable to create locator for node \"%d\": %v", i, err)
						t.FailNow()
					}

					results[i] = make([]consultant.Siblings, 0)

					// :|
					func(i int, locator *consultant.SiblingLocator) {
						locator.AddCallback("", func(_ uint64, siblings consultant.Siblings) {
							results[i] = append(results[i], siblings)
						})
					}(i, locators[i])

				}
			}

			if nil == locators[i] {
				t.Logf("Unable to locate service for node \"%s\"", localCluster.server(i).Config.NodeName)
				t.FailNow()
			}
		}
	})

	t.Run("start watchers", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := locators[i].StartWatcher(
				false,
				fmt.Sprintf("%s:%d",
					localCluster.server(i).Config.Addresses.HTTP,
					localCluster.server(i).Config.Ports.HTTP))
			if nil != err {
				t.Logf("Failed to start node \"%d\" watcher; %v", i, err)
				t.FailNow()
			}
		}
	})

	// TODO: Don't use a sleep...?
	time.Sleep(quorumWaitDuration)

	t.Run("change services", func(t *testing.T) {
		locators[0].StopWatcher()
		err := localCluster.client(0).Agent().ServiceDeregister(siblingLocatorServiceName)
		if nil != err {
			t.Logf("Unable to deregister service on node \"1\": %v", err)
			t.FailNow()
		}

		// wait for callback routines to finish
		time.Sleep(5 * time.Second)

		if len(results[1]) != len(results[2]) {
			t.Logf(
				"Expected node 1 and 2 to have same result length, saw: \"%d\" \"%d\"",
				len(results[0]),
				len(results[2]))
			t.FailNow()
		}

		if len(results[0]) != len(results[1])-1 {
			t.Logf(
				"Expected node 0 results to be 1 less than node 1, saw: \"%d\" \"%d\"",
				len(results[0]),
				len(results[1]))
			t.FailNow()
		}
	})
}
<commit_msg>Slight modification to test case.<commit_after>package consultant_test

import (
	"testing"

	"fmt"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/myENA/consultant"
)

const (
	quorumWaitDuration = 10 * time.Second

	siblingLocatorClusterCount = 3
	siblingLocatorServiceName  = "locator-test-service"
	siblingLocatorServiceAddr  = "127.0.0.1"
)

// TODO: Move cluster, service, and locator creation to discrete function

func TestSiblingLocator_Current(t *testing.T) {
	localCluster := makeCluster(t, siblingLocatorClusterCount)
	defer localCluster.shutdown()

	locators := make([]*consultant.SiblingLocator, siblingLocatorClusterCount)
	results := make([][]consultant.Siblings, siblingLocatorClusterCount)

	t.Run("register services", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := localCluster.client(i).Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name:    siblingLocatorServiceName,
				Address: siblingLocatorServiceAddr,
				Port:    randomPort(),
			})

			if nil != err {
				t.Logf("Unable to register service \"%d\": %v", i, err)
				t.FailNow()
			}
		}
	})

	// TODO: Don't use a sleep...?
	time.Sleep(quorumWaitDuration)

	t.Run("create locators", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			svcs, _, err := localCluster.client(i).Catalog().Service(siblingLocatorServiceName, "", nil)
			if nil != err {
				t.Logf("Unable to locate services in node \"%d\": %v", i, err)
				t.FailNow()
			}

			if siblingLocatorClusterCount != len(svcs) {
				t.Logf("Expected to see \"%d\" services, saw \"%d\"", siblingLocatorClusterCount, len(svcs))
				t.FailNow()
			}

			for _, svc := range svcs {
				if svc.Node == localCluster.server(i).Config.NodeName && svc.ServiceName == siblingLocatorServiceName {
					var err error
					locators[i], err = consultant.NewSiblingLocatorWithCatalogService(localCluster.client(i), svc)
					if nil != err {
						t.Logf("Unable to create locator for node \"%d\": %v", i, err)
						t.FailNow()
					}

					results[i] = make([]consultant.Siblings, 0)

					// :|
					func(i int, locator *consultant.SiblingLocator) {
						locator.AddCallback("", func(_ uint64, siblings consultant.Siblings) {
							results[i] = append(results[i], siblings)
						})
					}(i, locators[i])
				}
			}

			if nil == locators[i] {
				t.Logf("Unable to locate service for node \"%s\"", localCluster.server(i).Config.NodeName)
				t.FailNow()
			}
		}
	})

	t.Run("fetch current", func(t *testing.T) {
		// spread the word
		for i := 0; i < siblingLocatorClusterCount; i++ {
			locators[i].Current(false, true, nil)
		}

		// wait for callback routines to finish...
		time.Sleep(1 * time.Second)

		// Should only have 1 entry
		for i := 0; i < siblingLocatorClusterCount; i++ {
			if 1 != len(results[i]) {
				t.Logf("Expected \"results[%d]\" to be length 1, saw \"%d\"", i, len(results[i]))
				t.FailNow()
			}
		}

		for i := 0; i < siblingLocatorClusterCount; i++ {
			if siblingLocatorClusterCount-1 != len(results[i][0]) {
				t.Logf(
					"Expected node \"%d\" result \"0\" length \"%d\", saw \"%d\"",
					i,
					siblingLocatorClusterCount-1,
					len(results[i][0]))
				t.FailNow()
			}
		}
	})
}

func TestSiblingLocator_Watchers(t *testing.T) {
	localCluster := makeCluster(t, siblingLocatorClusterCount)

	locators := make([]*consultant.SiblingLocator, siblingLocatorClusterCount)
	results := make([][]consultant.Siblings, siblingLocatorClusterCount)

	defer func() {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			locators[i].StopWatcher()
		}
		localCluster.shutdown()
	}()

	t.Run("register services", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := localCluster.client(i).Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name:    siblingLocatorServiceName,
				Address: siblingLocatorServiceAddr,
				Port:    randomPort(),
			})

			if nil != err {
				t.Logf("Unable to register service \"%d\": %v", i, err)
				t.FailNow()
			}
		}
	})

	// TODO: Don't use a sleep...?
	time.Sleep(quorumWaitDuration)

	t.Run("create locators", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			svcs, _, err := localCluster.client(i).Catalog().Service(siblingLocatorServiceName, "", nil)
			if nil != err {
				t.Logf("Unable to locate services in node \"%d\": %v", i, err)
				t.FailNow()
			}

			if siblingLocatorClusterCount != len(svcs) {
				t.Logf("Expected to see \"%d\" services, saw \"%d\"", siblingLocatorClusterCount, len(svcs))
				t.FailNow()
			}

			for _, svc := range svcs {
				if svc.Node == localCluster.server(i).Config.NodeName && svc.ServiceName == siblingLocatorServiceName {
					var err error
					locators[i], err = consultant.NewSiblingLocatorWithCatalogService(localCluster.client(i), svc)
					if nil != err {
						t.Logf("Unable to create locator for node \"%d\": %v", i, err)
						t.FailNow()
					}

					results[i] = make([]consultant.Siblings, 0)

					// :|
					func(i int, locator *consultant.SiblingLocator) {
						locator.AddCallback("", func(_ uint64, siblings consultant.Siblings) {
							results[i] = append(results[i], siblings)
						})
					}(i, locators[i])

				}
			}

			if nil == locators[i] {
				t.Logf("Unable to locate service for node \"%s\"", localCluster.server(i).Config.NodeName)
				t.FailNow()
			}
		}
	})

	t.Run("start watchers", func(t *testing.T) {
		for i := 0; i < siblingLocatorClusterCount; i++ {
			err := locators[i].StartWatcher(
				false,
				fmt.Sprintf("%s:%d",
					localCluster.server(i).Config.Addresses.HTTP,
					localCluster.server(i).Config.Ports.HTTP))
			if nil != err {
				t.Logf("Failed to start node \"%d\" watcher; %v", i, err)
				t.FailNow()
			}
		}
	})

	// wait for watcher routines to start up
	time.Sleep(quorumWaitDuration)

	t.Run("change services", func(t *testing.T) {
		locators[0].StopWatcher()
		err := localCluster.client(0).Agent().ServiceDeregister(siblingLocatorServiceName)
		if nil != err {
			t.Logf("Unable to deregister service on node \"1\": %v", err)
			t.FailNow()
		}

		// wait for quorum service deregistration and watcher callbacks to finish
		time.Sleep(quorumWaitDuration)

		if len(results[1]) != len(results[2]) {
			t.Logf(
				"Expected node 1 and 2 to have same result length, saw: \"%d\" \"%d\"",
				len(results[0]),
				len(results[2]))
			t.FailNow()
		}

		if len(results[0]) != len(results[1])-1 {
			t.Logf(
				"Expected node 0 results to be 1 less than node 1, saw: \"%d\" \"%d\"",
				len(results[0]),
				len(results[1]))
			t.FailNow()
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Song struct {
	Id      int    `json:"id"`
	Videoid string `json:"videoid"`
	Name    string `json:"name"`
	Length  int    `json:"length"`
	Seek    int    `json:"seek"`
}

type Playlist []Song

func (s *Song) init(id int, videoid string, name string, length int, seek int) Song {
	return Song{
		Id:      id,
		Videoid: videoid,
		Name:    name,
		Length:  length,
		Seek:    seek,
	}
}

func createSong(videoid string, name string) Song {
	return Song{
		Id:      -1,
		Videoid: videoid,
		Name:    name,
		Length:  getDuration(videoid),
		Seek:    -5,
	}
}

func Truncate() {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("TRUNCATE playlist")
	CheckError(err)
	_, err = stmt.Exec()
	CheckError(err)
}

func cleanup(results []Song) []Song {
	var cleanedResults []Song
	for i := range results {
		if results[i].Length != -1 {
			cleanedResults = append(cleanedResults, results[i])
		}
	}
	return cleanedResults
}

func Seed() {
	/*
		seedQuery := "tum se hi"
		searchResults := cleanup(Search(seedQuery))
		seedSong := searchResults[0]
		Truncate()
		enqueue(seedSong)
	*/
	enqueue(getLastSong())
}

func GetPlaylist() Playlist {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	playlist := []Song{}
	db := GetDbHandle()
	defer db.Close()
	rows, err := db.Query("SELECT  * from  playlist order by id")
	CheckError(err)
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&id, &videoid, &name, &length, &seek)
		var s = Song{}
		s = s.init(id, videoid, name, length, seek)
		CheckError(err)
		playlist = append(playlist, s)
	}
	return playlist
}

func CurrentlyPlaying() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id ASC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	s = s.init(id, videoid, name, length, seek)
	return s
}

func updateSeek(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("UPDATE playlist SET seek = seek + 1 WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func getLastSong() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id DESC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	lastSong := s.init(id, videoid, name, length, seek)
	return lastSong
}

func enqueue(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("INSERT INTO playlist (videoid, name, length, seek) VALUES (?, ?, ?, ?)")
	CheckError(err)
	_, err = stmt.Exec(s.Videoid, s.Name, s.Length, s.Seek)
	CheckError(err)
}

func remove(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("DELETE FROM playlist WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func Size() int {
	var size int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT count(*) FROM playlist").Scan(&size)
	CheckError(err)
	return size
}

func Add(query string) {
	searchResults := cleanup(Search(query))
	enqueue(searchResults[0])
}

func autoAdd() {
	ticker := time.NewTicker(time.Second * 5)
	for _ = range ticker.C {
		if Size() == 1 {
			newSong := recommend(getLastSong())
			enqueue(newSong)
		}
	}
}

func recommend(s Song) Song {
	recommendations := cleanup(Recommend(s.Videoid))
	songindex := rand.Intn(len(recommendations)-3) + 3
	return recommendations[songindex]
}

func Refresh() {
	s := CurrentlyPlaying()
	if s.Seek < s.Length {
		updateSeek(s)
		fmt.Println(s.Videoid, "   ", s.Seek, "          ", GetPlaylist())
	} else {
		remove(s)
		go PostToSlack(CurrentlyPlaying().Name)
		Refresh()
	}
}

func Skip() {
	s := CurrentlyPlaying()
	PostToSlack("skipped " + CurrentlyPlaying().Name + ". Don't do this :angry")
	remove(s)
	go PostToSlack(CurrentlyPlaying().Name)
	Refresh()
}
<commit_msg>handling cases when recommendations are too low<commit_after>package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Song struct {
	Id      int    `json:"id"`
	Videoid string `json:"videoid"`
	Name    string `json:"name"`
	Length  int    `json:"length"`
	Seek    int    `json:"seek"`
}

type Playlist []Song

func (s *Song) init(id int, videoid string, name string, length int, seek int) Song {
	return Song{
		Id:      id,
		Videoid: videoid,
		Name:    name,
		Length:  length,
		Seek:    seek,
	}
}

func createSong(videoid string, name string) Song {
	return Song{
		Id:      -1,
		Videoid: videoid,
		Name:    name,
		Length:  getDuration(videoid),
		Seek:    -5,
	}
}

func Truncate() {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("TRUNCATE playlist")
	CheckError(err)
	_, err = stmt.Exec()
	CheckError(err)
}

func cleanup(results []Song) []Song {
	var cleanedResults []Song
	for i := range results {
		if results[i].Length != -1 {
			cleanedResults = append(cleanedResults, results[i])
		}
	}
	return cleanedResults
}

func Seed() {
	seedQuery := "tum se hi"
	searchResults := cleanup(Search(seedQuery))
	seedSong := searchResults[0]
	Truncate()
	enqueue(seedSong)
}

func GetPlaylist() Playlist {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	playlist := []Song{}
	db := GetDbHandle()
	defer db.Close()
	rows, err := db.Query("SELECT  * from  playlist order by id")
	CheckError(err)
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&id, &videoid, &name, &length, &seek)
		var s = Song{}
		s = s.init(id, videoid, name, length, seek)
		CheckError(err)
		playlist = append(playlist, s)
	}
	return playlist
}

func CurrentlyPlaying() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id ASC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	s = s.init(id, videoid, name, length, seek)
	return s
}

func updateSeek(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("UPDATE playlist SET seek = seek + 1 WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func getLastSong() Song {
	var id int
	var videoid string
	var name string
	var length int
	var seek int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT * FROM playlist ORDER BY id DESC LIMIT 1").Scan(&id, &videoid, &name, &length, &seek)
	CheckError(err)
	var s = Song{}
	lastSong := s.init(id, videoid, name, length, seek)
	return lastSong
}

func enqueue(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("INSERT INTO playlist (videoid, name, length, seek) VALUES (?, ?, ?, ?)")
	CheckError(err)
	_, err = stmt.Exec(s.Videoid, s.Name, s.Length, s.Seek)
	CheckError(err)
}

func remove(s Song) {
	db := GetDbHandle()
	defer db.Close()
	stmt, err := db.Prepare("DELETE FROM playlist WHERE id = ?")
	CheckError(err)
	_, err = stmt.Exec(s.Id)
	CheckError(err)
}

func Size() int {
	var size int
	db := GetDbHandle()
	defer db.Close()
	err := db.QueryRow("SELECT count(*) FROM playlist").Scan(&size)
	CheckError(err)
	return size
}

func Add(query string) {
	searchResults := cleanup(Search(query))
	enqueue(searchResults[0])
}

func autoAdd() {
	ticker := time.NewTicker(time.Second * 5)
	for _ = range ticker.C {
		if Size() == 1 {
			newSong := recommend(getLastSong())
			enqueue(newSong)
		}
	}
}

func recommend(s Song) Song {
	var recommendedSong Song
	recommendations := cleanup(Recommend(s.Videoid))
	if len(recommendations) < 6 {
		seedQuery := "tum se hi"
		searchResults := cleanup(Search(seedQuery))
		recommendedSong = searchResults[0]
	} else {
		songindex := rand.Intn(len(recommendations)-3) + 3
		recommendedSong = recommendations[songindex]
	}
	return recommendedSong
}

func Refresh() {
	s := CurrentlyPlaying()
	if s.Seek < s.Length {
		updateSeek(s)
		fmt.Println(s.Videoid, "   ", s.Seek, "          ", GetPlaylist())
	} else {
		remove(s)
		go PostToSlack(CurrentlyPlaying().Name)
		Refresh()
	}
}

func Skip() {
	s := CurrentlyPlaying()
	PostToSlack("skipped " + CurrentlyPlaying().Name + ". Don't do this :angry")
	remove(s)
	go PostToSlack(CurrentlyPlaying().Name)
	Refresh()
}
<|endoftext|>


<commit_before>package rpc

import (
	"fmt"
	"testing"
	"time"
)

var rpc RPCProto

const (
	serverUri = "http://localhost:6800/jsonrpc"
)

func init() {
	rpc = New(serverUri)
}

// func Test0(t *testing.T) {
// 	var reply interface{}
// 	err := Call(serverUri, addUri, []interface{}{[]string{"http://cran.rstudio.com/bin/macosx/R-3.0.1.pkg"}}, &reply)
// 	fmt.Printf("%s %v\n", reply, err)
// }

func TestA(t *testing.T) {
	fmt.Println(rpc.GetVersion())
	g, err := rpc.AddUri("http://cran.rstudio.com/bin/macosx/R-3.0.1.pkg")
	if err != nil {
		t.Error(err.Error())
	}
	fmt.Println(g)
	fmt.Println(rpc.TellActive())
	// fmt.Println(rpc.ForcePause(g))
	// fmt.Println(rpc.Unpause(g))
	time.Sleep(1 * time.Second)
	fmt.Println(rpc.TellStatus(g))
	// time.Sleep(1 * time.Second)
	// fmt.Println(rpc.Pause(g))
	time.Sleep(1 * time.Second)
	fmt.Println(rpc.PauseAll())
	fmt.Println(rpc.Remove(g))
	fmt.Println(rpc.TellActive())
}
<commit_msg>update tests<commit_after>package rpc

import (
	"fmt"
	"os/exec"
	"testing"
	"time"
)

var rpc RPCProto

const (
	serverUri = "http://localhost:6800/jsonrpc"
)

func init() {
	rpc = New(serverUri)
	if err := launchAria2cDaemon(); err != nil {
		panic(err)
	}
}

// func Test0(t *testing.T) {
// 	var reply interface{}
// 	err := Call(serverUri, addUri, []interface{}{[]string{"http://cran.rstudio.com/bin/macosx/R-3.0.1.pkg"}}, &reply)
// 	fmt.Printf("%s %v\n", reply, err)
// }

func TestAll(t *testing.T) {
	defer fmt.Println(rpc.ForceShutdown())
	v, err := rpc.GetVersion()
	if err != nil {
		t.Error(err)
	}
	fmt.Println(v["version"])
	g, err := rpc.AddUri("http://cran.rstudio.com/bin/macosx/R-3.0.1.pkg")
	if err != nil {
		t.Error(err)
	}
	fmt.Println(g)
	if _, err = rpc.TellActive(); err != nil {
		t.Error(err)
	}
	time.Sleep(1 * time.Second)
	if _, err = rpc.TellStatus(g); err != nil {
		t.Error(err)
	}
	time.Sleep(1 * time.Second)
	if _, err = rpc.PauseAll(); err != nil {
		t.Error(err)
	}
	if _, err = rpc.Remove(g); err != nil {
		t.Error(err)
	}
	if _, err = rpc.TellActive(); err != nil {
		t.Error(err)
	}
}

func launchAria2cDaemon() (err error) {
	if _, err = rpc.GetVersion(); err == nil {
		return
	}
	cmd := exec.Command("aria2c", "--enable-rpc", "--rpc-listen-all")
	if err = cmd.Start(); err != nil {
		return err
	}
	cmd.Process.Release()
	time.Sleep(1. * time.Second)
	fmt.Println("aria2c started!")
	return nil
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"github.com/golang/glog"
	. "github.com/infradash/dash/pkg/dash"
	"github.com/qorio/maestro/pkg/docker"
	"strings"
)

func (this *Agent) DiscoverSelfInDocker() error {

	docker_name := this.Name

	if docker_name == "" {
		return ErrNoDockerName
	}

	glog.Infoln("Discovering container in Docker with name=", docker_name)

	dc, err := this.docker.FindContainersByName(docker_name)
	if err != nil {
		return err
	}

	if len(dc) > 1 {
		// This is unlikely, since Docker itself will prevent containers with
		// the same assigned name from starting.  However, it's possible that
		// the name provided isn't a complete name, so we have partial match.
		// In this case, it's programming error.
		return ErrMoreThanOneAgent
	}

	if len(dc) == 1 {
		this.self_container = dc[0]

		this.self_container.Inspect()
		glog.Infoln("Found self as container:", this.self_container)

		if len(dc[0].Ports) > 0 {
			this.Port = dc[0].Ports[0]
		}
	}

	return nil
}

type ContainerMatchRule struct {
	MatchContainerRule

	Domain      string
	Service     ServiceKey
	PortMatched bool
}

func (this *MatchContainerRule) GetMatchContainerPort() int {
	if this.MatchContainerPort != nil {
		return *this.MatchContainerPort
	} else {
		return 0
	}
}

func (this *ContainerMatchRule) GetMatchContainerPort() int {
	return this.MatchContainerRule.GetMatchContainerPort()
}

func (this *ContainerMatchRule) match_by_running_port(c *docker.Container) bool {
	if this.MatchContainerPort == nil {
		return false // Don't care
	}
	port := *this.MatchContainerPort
	if port > 0 {
		for _, p := range c.Ports {
			if port == int(p.ContainerPort) {
				this.PortMatched = true
				return true
			}
		}
	}
	return false
}

func (this *ContainerMatchRule) match(c *docker.Container) bool {
	if c.DockerData == nil {
		return false
	}

	// We first try by matching image. Then by name, environment variable.
	if !ImageMatch(c.Image, &this.Image) {
		return false
	}

	// When the container isn't running, the port information is erased.
	// So we count on other conditions to match -- we at least eliminate the negative case
	// where it's running and the port doesn't match
	if c.DockerData.State.Running && this.MatchContainerPort != nil && !this.match_by_running_port(c) {
		return false
	}

	// If we have no other criteria by now, just match
	if len(this.MatchAll) == 0 && len(this.MatchFirst) == 0 {
		return true
	}

	match := true
	for _, r := range this.MatchAll {
		match = match && r.Match(c)
	}
	if !match {
		return false
	}

	match = false // reset
	for _, r := range this.MatchFirst {
		if r.Match(c) {
			match = true
			break
		}
	}
	return match
}

type CheckContainer func(*docker.Container) map[ServiceKey]*ContainerMatchRule
type OnMatch func(*docker.Container, *ContainerMatchRule)

func (this *Agent) DiscoverRunningContainers(check CheckContainer, do OnMatch) error {

	glog.Infoln("Querying docker for all containers")
	all_containers, err := this.docker.FindContainers(nil) //map[string][]string{"status": []string{"running"}})
	if err != nil {
		return err
	}
	glog.Infoln("Found", len(all_containers), "containers")

	for _, container := range all_containers {
		glog.Infoln("Checking", "Name=", container.Name, "Image=", container.Image, "Id=", container.Id[0:12])
		match_rules := check(container)
		for serviceKey, match_rule := range match_rules {
			glog.Infoln("==========================>>>>  Matched",
				"Service=", serviceKey, "Name=", container.Name,
				"Id=", container.Id[0:12],
				"Image=", container.Image, "Service=", match_rule.Service, "Rule=", match_rule)
			do(container, match_rule)
		}
	}
	return nil
}

type DiscoveryContainerMatcher struct {
	rulesByDomainService map[string]map[ServiceKey]ContainerMatchRule
}

func (this *DiscoveryContainerMatcher) Init() *DiscoveryContainerMatcher {
	this.rulesByDomainService = make(map[string]map[ServiceKey]ContainerMatchRule)
	return this
}

func (this *DiscoveryContainerMatcher) C(domain string, service ServiceKey, spec *MatchContainerRule) *DiscoveryContainerMatcher {
	match_rule := ContainerMatchRule{
		MatchContainerRule: *spec,
		Domain:             domain,
		Service:            service,
	}
	if _, has := this.rulesByDomainService[domain]; !has {
		this.rulesByDomainService[domain] = map[ServiceKey]ContainerMatchRule{service: match_rule}
	} else {
		this.rulesByDomainService[domain][service] = match_rule
	}
	return this
}

// This is critical for the discovery to know how to locate the rule for matching.  This is because rules are
// organized by domain and service.  We look for hints in the container's metadata to determine which domain
// this container may belong to.
func findContainerDomain(c *docker.Container) *string {
	if c.DockerData == nil || c.DockerData.Config == nil {
		return nil
	}
	v := ""
	// Find by label or environment variables
	if c.DockerData != nil && c.DockerData.Config != nil {
		if len(c.DockerData.Config.Labels) > 0 {
			v = find_container_domain_by_label(c)
		}
		if v == "" && len(c.DockerData.Config.Env) > 0 {
			v = find_container_domain_by_env(c)
		}
	}
	return &v
}

func find_container_domain_by_label(c *docker.Container) string {
	if l, exists := c.DockerData.Config.Labels[EnvDomain]; exists {
		return l
	}
	return ""
}

func find_container_domain_by_env(c *docker.Container) string {
	search := fmt.Sprintf("%s=", EnvDomain)
	for _, env := range c.DockerData.Config.Env {
		index := strings.Index(env, search)
		if index == 0 {
			return env[len(search):]
		}
	}
	return ""
}

func (this *DiscoveryContainerMatcher) MatcherForDomain(domain string, service ServiceKey) func(docker.Action, *docker.Container) bool {
	// get the rule
	if service_rule_map, has_domain := this.rulesByDomainService[domain]; has_domain {
		if rule, has_map := service_rule_map[service]; has_map {
			return func(a docker.Action, c *docker.Container) bool {
				if a == docker.Remove {
					return ImageMatch(c.Image, &rule.Image)
				}

				if env := findContainerDomain(c); env != nil {
					return *env == domain && rule.match(c)
				} else {
					return rule.match(c)
				}
			}
		}
	}
	glog.Warningln("No matcher for Domain=", domain, "Service=", service)
	return func(docker.Action, *docker.Container) bool {
		return false
	}
}

func (this *DiscoveryContainerMatcher) match(domain *string, c *docker.Container) map[ServiceKey]*ContainerMatchRule {
	if domain != nil {
		matches := map[ServiceKey]*ContainerMatchRule{}
		// Now we have matched by the domain of the container.  Let's see if it's running an image we care about:
		for key, match_rule := range this.rulesByDomainService[*domain] {
			if env := findContainerDomain(c); env != nil {
				if *env == *domain && match_rule.match(c) {
					matches[key] = &match_rule
				}
			} else if match_rule.match(c) {
				matches[key] = &match_rule
			}
		}
		return matches
	} else {
		matches := map[ServiceKey]*ContainerMatchRule{}
		// if we don't know the domain, then search through all the images...
		for _, rules := range this.rulesByDomainService {
			for key, match_rule := range rules {
				if match_rule.match(c) {
					matches[key] = &match_rule
				}
			}
		}
		return matches
	}

}

func (this *DiscoveryContainerMatcher) Match(c *docker.Container) map[ServiceKey]*ContainerMatchRule {
	return this.match(findContainerDomain(c), c)
}

func ImageMatch(image string, spec *docker.Image) bool {
	glog.V(100).Infoln("Matching image", image, "vs", spec)
	if spec.Tag != "" {
		return image == fmt.Sprintf("%s:%s", spec.Repository, spec.Tag)
	} else {
		return strings.Index(image, spec.Repository) == 0
	}
}

func BuildRegistryEntry(container *docker.Container, match_port int) (*RegistryContainerEntry, error) {
	_, version, _, err := ParseVersion(container.Image)
	if err != nil {
		return nil, err
	}

	entry := &RegistryContainerEntry{
		RegistryReleaseEntry: RegistryReleaseEntry{
			RegistryEntryBase: RegistryEntryBase{
				Version: version,
			},
			Image: container.Image,
		},
		ContainerId: container.Id,
	}

	// Interactive sessions don't have ports... So we add an entry only if we don't care
	// to match ports (match_container_port == 0)
	for _, p := range container.Ports {
		if match_port == int(p.ContainerPort) || match_port == 0 {
			entry.Port = p
			return entry, nil
		}
	}

	if match_port == 0 {
		return entry, nil
	} else {
		return nil, nil
	}
}
<commit_msg>Discover multiple services - fix<commit_after>package agent

import (
	"fmt"
	"github.com/golang/glog"
	. "github.com/infradash/dash/pkg/dash"
	"github.com/qorio/maestro/pkg/docker"
	"strings"
)

func (this *Agent) DiscoverSelfInDocker() error {

	docker_name := this.Name

	if docker_name == "" {
		return ErrNoDockerName
	}

	glog.Infoln("Discovering container in Docker with name=", docker_name)

	dc, err := this.docker.FindContainersByName(docker_name)
	if err != nil {
		return err
	}

	if len(dc) > 1 {
		// This is unlikely, since Docker itself will prevent containers with
		// the same assigned name from starting.  However, it's possible that
		// the name provided isn't a complete name, so we have partial match.
		// In this case, it's programming error.
		return ErrMoreThanOneAgent
	}

	if len(dc) == 1 {
		this.self_container = dc[0]

		this.self_container.Inspect()
		glog.Infoln("Found self as container:", this.self_container)

		if len(dc[0].Ports) > 0 {
			this.Port = dc[0].Ports[0]
		}
	}

	return nil
}

type ContainerMatchRule struct {
	MatchContainerRule

	Domain      string
	Service     ServiceKey
	PortMatched bool
}

func (this *MatchContainerRule) GetMatchContainerPort() int {
	if this.MatchContainerPort != nil {
		return *this.MatchContainerPort
	} else {
		return 0
	}
}

func (this *ContainerMatchRule) GetMatchContainerPort() int {
	return this.MatchContainerRule.GetMatchContainerPort()
}

func (this *ContainerMatchRule) match_by_running_port(c *docker.Container) bool {
	if this.MatchContainerPort == nil {
		return false // Don't care
	}
	port := *this.MatchContainerPort
	if port > 0 {
		for _, p := range c.Ports {
			if port == int(p.ContainerPort) {
				this.PortMatched = true
				return true
			}
		}
	}
	return false
}

func (this *ContainerMatchRule) match(c *docker.Container) bool {
	if c.DockerData == nil {
		return false
	}

	// We first try by matching image. Then by name, environment variable.
	if !ImageMatch(c.Image, &this.Image) {
		return false
	}

	// When the container isn't running, the port information is erased.
	// So we count on other conditions to match -- we at least eliminate the negative case
	// where it's running and the port doesn't match
	if c.DockerData.State.Running && this.MatchContainerPort != nil && !this.match_by_running_port(c) {
		return false
	}

	// If we have no other criteria by now, just match
	if len(this.MatchAll) == 0 && len(this.MatchFirst) == 0 {
		return true
	}

	match := true
	for _, r := range this.MatchAll {
		match = match && r.Match(c)
	}
	if !match {
		return false
	}

	match = false // reset
	for _, r := range this.MatchFirst {
		if r.Match(c) {
			match = true
			break
		}
	}
	return match
}

type CheckContainer func(*docker.Container) map[ServiceKey]*ContainerMatchRule
type OnMatch func(*docker.Container, *ContainerMatchRule)

func (this *Agent) DiscoverRunningContainers(check CheckContainer, do OnMatch) error {

	glog.Infoln("Querying docker for all containers")
	all_containers, err := this.docker.FindContainers(nil) //map[string][]string{"status": []string{"running"}})
	if err != nil {
		return err
	}
	glog.Infoln("Found", len(all_containers), "containers")

	for _, container := range all_containers {
		glog.Infoln("Checking", "Name=", container.Name, "Image=", container.Image, "Id=", container.Id[0:12])
		match_rules := check(container)
		for serviceKey, match_rule := range match_rules {
			glog.Infoln("==========================>>>>  Matched",
				"Service=", serviceKey, "Name=", container.Name,
				"Id=", container.Id[0:12],
				"Image=", container.Image, "Service=", match_rule.Service, "Rule=", match_rule)
			do(container, match_rule)
		}
	}
	return nil
}

type DiscoveryContainerMatcher struct {
	rulesByDomainService map[string]map[ServiceKey]ContainerMatchRule
}

func (this *DiscoveryContainerMatcher) Init() *DiscoveryContainerMatcher {
	this.rulesByDomainService = make(map[string]map[ServiceKey]ContainerMatchRule)
	return this
}

func (this *DiscoveryContainerMatcher) C(domain string, service ServiceKey, spec *MatchContainerRule) *DiscoveryContainerMatcher {
	match_rule := ContainerMatchRule{
		MatchContainerRule: *spec,
		Domain:             domain,
		Service:            service,
	}
	if _, has := this.rulesByDomainService[domain]; !has {
		this.rulesByDomainService[domain] = map[ServiceKey]ContainerMatchRule{}
	}

	this.rulesByDomainService[domain][service] = match_rule
	return this
}

// This is critical for the discovery to know how to locate the rule for matching.  This is because rules are
// organized by domain and service.  We look for hints in the container's metadata to determine which domain
// this container may belong to.
func findContainerDomain(c *docker.Container) *string {
	if c.DockerData == nil || c.DockerData.Config == nil {
		return nil
	}
	v := ""
	// Find by label or environment variables
	if c.DockerData != nil && c.DockerData.Config != nil {
		if len(c.DockerData.Config.Labels) > 0 {
			v = find_container_domain_by_label(c)
		}
		if v == "" && len(c.DockerData.Config.Env) > 0 {
			v = find_container_domain_by_env(c)
		}
	}
	return &v
}

func find_container_domain_by_label(c *docker.Container) string {
	if l, exists := c.DockerData.Config.Labels[EnvDomain]; exists {
		return l
	}
	return ""
}

func find_container_domain_by_env(c *docker.Container) string {
	search := fmt.Sprintf("%s=", EnvDomain)
	for _, env := range c.DockerData.Config.Env {
		index := strings.Index(env, search)
		if index == 0 {
			return env[len(search):]
		}
	}
	return ""
}

func (this *DiscoveryContainerMatcher) MatcherForDomain(domain string, service ServiceKey) func(docker.Action, *docker.Container) bool {
	// get the rule
	if service_rule_map, has_domain := this.rulesByDomainService[domain]; has_domain {
		if rule, has_map := service_rule_map[service]; has_map {
			return func(a docker.Action, c *docker.Container) bool {
				if a == docker.Remove {
					return ImageMatch(c.Image, &rule.Image)
				}

				if env := findContainerDomain(c); env != nil {
					return *env == domain && rule.match(c)
				} else {
					return rule.match(c)
				}
			}
		}
	}
	glog.Warningln("No matcher for Domain=", domain, "Service=", service)
	return func(docker.Action, *docker.Container) bool {
		return false
	}
}

func (this *DiscoveryContainerMatcher) match(domain *string, c *docker.Container) map[ServiceKey]*ContainerMatchRule {
	if domain != nil {
		matches := map[ServiceKey]*ContainerMatchRule{}
		// Now we have matched by the domain of the container.  Let's see if it's running an image we care about:
		for serviceKey, match_rule := range this.rulesByDomainService[*domain] {
			glog.Infoln("Checking domain=", domain, "service=", serviceKey, "rule=", match_rule)

			matched := false
			if env := findContainerDomain(c); env != nil {
				matched = *env == *domain && match_rule.match(c)
			} else {
				matched = match_rule.match(c)
			}

			glog.Infoln(">>>>>> matched=", matched)
			if matched {
				matches[serviceKey] = &match_rule
			}
		}
		return matches
	} else {
		matches := map[ServiceKey]*ContainerMatchRule{}
		// if we don't know the domain, then search through all the images...
		for _, rules := range this.rulesByDomainService {
			for serviceKey, match_rule := range rules {
				if match_rule.match(c) {
					glog.Infoln("Matched service=", serviceKey, "rule=", match_rule)
					matches[serviceKey] = &match_rule
				}
			}
		}
		return matches
	}

}

func (this *DiscoveryContainerMatcher) Match(c *docker.Container) map[ServiceKey]*ContainerMatchRule {
	return this.match(findContainerDomain(c), c)
}

func ImageMatch(image string, spec *docker.Image) bool {
	glog.V(100).Infoln("Matching image", image, "vs", spec)
	if spec.Tag != "" {
		return image == fmt.Sprintf("%s:%s", spec.Repository, spec.Tag)
	} else {
		return strings.Index(image, spec.Repository) == 0
	}
}

func BuildRegistryEntry(container *docker.Container, match_port int) (*RegistryContainerEntry, error) {
	_, version, _, err := ParseVersion(container.Image)
	if err != nil {
		return nil, err
	}

	entry := &RegistryContainerEntry{
		RegistryReleaseEntry: RegistryReleaseEntry{
			RegistryEntryBase: RegistryEntryBase{
				Version: version,
			},
			Image: container.Image,
		},
		ContainerId: container.Id,
	}

	// Interactive sessions don't have ports... So we add an entry only if we don't care
	// to match ports (match_container_port == 0)
	for _, p := range container.Ports {
		if match_port == int(p.ContainerPort) || match_port == 0 {
			entry.Port = p
			return entry, nil
		}
	}

	if match_port == 0 {
		return entry, nil
	} else {
		return nil, nil
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 api

import (
	"fmt"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

var (
	supportedManifestVersions util.StringSet = util.NewStringSet("v1beta1", "v1beta2")
)

func errInvalid(field string, value interface{}) error {
	return fmt.Errorf("%s is invalid: '%v'", field, value)
}

func errNotSupported(field string, value interface{}) error {
	return fmt.Errorf("%s is not supported: '%v'", field, value)
}

func errNotUnique(field string, value interface{}) error {
	return fmt.Errorf("%s is not unique: '%v'", field, value)
}

func validateVolumes(volumes []Volume) (util.StringSet, error) {
	allNames := util.StringSet{}
	for i := range volumes {
		vol := &volumes[i] // so we can set default values
		if len(vol.Name) > 63 || !util.IsDNSLabel(vol.Name) {
			return util.StringSet{}, errInvalid("Volume.Name", vol.Name)
		}
		if allNames.Has(vol.Name) {
			return util.StringSet{}, errNotUnique("Volume.Name", vol.Name)
		}
		allNames.Insert(vol.Name)
	}
	return allNames, nil
}

func validateEnv(vars []EnvVar) error {
	for i := range vars {
		ev := &vars[i] // so we can set default values
		if len(ev.Name) == 0 {
			// Backwards compat.
			if len(ev.Key) == 0 {
				return errInvalid("EnvVar.Name", ev.Name)
			}
			glog.Warning("DEPRECATED: EnvVar.Key has been replaced by EnvVar.Name")
			ev.Name = ev.Key
			ev.Key = ""
		}
		if !util.IsCIdentifier(ev.Name) {
			return errInvalid("EnvVar.Name", ev.Name)
		}
	}
	return nil
}

func validateContainers(containers []Container, volumes util.StringSet) error {
	allNames := util.StringSet{}
	for i := range containers {
		ctr := &containers[i] // so we can set default values
		if len(ctr.Name) > 63 || !util.IsDNSLabel(ctr.Name) {
			return errInvalid("Container.Name", ctr.Name)
		}
		if allNames.Has(ctr.Name) {
			return errNotUnique("Container.Name", ctr.Name)
		}
		allNames.Insert(ctr.Name)
		if len(ctr.Image) == 0 {
			return errInvalid("Container.Image", ctr.Name)
		}
		if err := validateEnv(ctr.Env); err != nil {
			return err
		}

		// TODO(thockin): finish validation.
	}
	return nil
}

// ValidateManifest tests that the specified ContainerManifest has valid data.
// This includes checking formatting and uniqueness.  It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
// TODO(thockin): We should probably collect all errors rather than aborting the validation.
func ValidateManifest(manifest *ContainerManifest) error {
	if len(manifest.Version) == 0 {
		return errInvalid("ContainerManifest.Version", manifest.Version)
	}
	if !supportedManifestVersions.Has(manifest.Version) {
		return errNotSupported("ContainerManifest.Version", manifest.Version)
	}
	if len(manifest.ID) > 255 || !util.IsDNSSubdomain(manifest.ID) {
		return errInvalid("ContainerManifest.ID", manifest.ID)
	}
	allVolumes, err := validateVolumes(manifest.Volumes)
	if err != nil {
		return err
	}
	if err := validateContainers(manifest.Containers, allVolumes); err != nil {
		return err
	}
	return nil
}
<commit_msg>Use structured errors during validation<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 api

import (
	"fmt"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

var (
	supportedManifestVersions util.StringSet = util.NewStringSet("v1beta1", "v1beta2")
)

// Validation errors.
type ValidationErrorEnum string

const (
	ErrTypeInvalid      ValidationErrorEnum = "invalid value"
	ErrTypeNotSupported ValidationErrorEnum = "unsupported value"
	ErrTypeDuplicate    ValidationErrorEnum = "duplicate value"
)

// Implements the 'error' interface.
type ValidationError struct {
	ErrorType  ValidationErrorEnum
	ErrorField string
	BadValue   interface{}
}

func (v ValidationError) Error() string {
	return fmt.Sprintf("%s: %v '%v'", v.ErrorField, v.ErrorType, v.BadValue)
}

// Factory functions for errors.
func makeInvalidError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeInvalid, field, value}
}

func makeNotSupportedError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeNotSupported, field, value}
}

func makeDuplicateError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeDuplicate, field, value}
}

func validateVolumes(volumes []Volume) (util.StringSet, error) {
	allNames := util.StringSet{}
	for i := range volumes {
		vol := &volumes[i] // so we can set default values
		if len(vol.Name) > 63 || !util.IsDNSLabel(vol.Name) {
			return util.StringSet{}, makeInvalidError("Volume.Name", vol.Name)
		}
		if allNames.Has(vol.Name) {
			return util.StringSet{}, makeDuplicateError("Volume.Name", vol.Name)
		}
		allNames.Insert(vol.Name)
	}
	return allNames, nil
}

func validateEnv(vars []EnvVar) error {
	for i := range vars {
		ev := &vars[i] // so we can set default values
		if len(ev.Name) == 0 {
			// Backwards compat.
			if len(ev.Key) == 0 {
				return makeInvalidError("EnvVar.Name", ev.Name)
			}
			glog.Warning("DEPRECATED: EnvVar.Key has been replaced by EnvVar.Name")
			ev.Name = ev.Key
			ev.Key = ""
		}
		if !util.IsCIdentifier(ev.Name) {
			return makeInvalidError("EnvVar.Name", ev.Name)
		}
	}
	return nil
}

func validateContainers(containers []Container, volumes util.StringSet) error {
	allNames := util.StringSet{}
	for i := range containers {
		ctr := &containers[i] // so we can set default values
		if len(ctr.Name) > 63 || !util.IsDNSLabel(ctr.Name) {
			return makeInvalidError("Container.Name", ctr.Name)
		}
		if allNames.Has(ctr.Name) {
			return makeDuplicateError("Container.Name", ctr.Name)
		}
		allNames.Insert(ctr.Name)
		if len(ctr.Image) == 0 {
			return makeInvalidError("Container.Image", ctr.Name)
		}
		if err := validateEnv(ctr.Env); err != nil {
			return err
		}

		// TODO(thockin): finish validation.
	}
	return nil
}

// ValidateManifest tests that the specified ContainerManifest has valid data.
// This includes checking formatting and uniqueness.  It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
// TODO(thockin): We should probably collect all errors rather than aborting the validation.
func ValidateManifest(manifest *ContainerManifest) error {
	if len(manifest.Version) == 0 {
		return makeInvalidError("ContainerManifest.Version", manifest.Version)
	}
	if !supportedManifestVersions.Has(manifest.Version) {
		return makeNotSupportedError("ContainerManifest.Version", manifest.Version)
	}
	if len(manifest.ID) > 255 || !util.IsDNSSubdomain(manifest.ID) {
		return makeInvalidError("ContainerManifest.ID", manifest.ID)
	}
	allVolumes, err := validateVolumes(manifest.Volumes)
	if err != nil {
		return err
	}
	if err := validateContainers(manifest.Containers, allVolumes); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package runtime

import (
	"fmt"
	"io"
	"net/http"
	"net/textproto"

	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime/internal"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/status"
)

// ForwardResponseStream forwards the stream from gRPC server to REST client.
func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
	f, ok := w.(http.Flusher)
	if !ok {
		grpclog.Printf("Flush not supported in %T", w)
		http.Error(w, "unexpected type of web server", http.StatusInternalServerError)
		return
	}

	md, ok := ServerMetadataFromContext(ctx)
	if !ok {
		grpclog.Printf("Failed to extract ServerMetadata from context")
		http.Error(w, "unexpected error", http.StatusInternalServerError)
		return
	}
	handleForwardResponseServerMetadata(w, mux, md)

	w.Header().Set("Transfer-Encoding", "chunked")
	w.Header().Set("Content-Type", marshaler.ContentType())
	if err := handleForwardResponseOptions(ctx, w, nil, opts); err != nil {
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	var delimiter []byte
	if d, ok := marshaler.(Delimited); ok {
		delimiter = d.Delimiter()
	}

	var wroteHeader bool
	for {
		resp, err := recv()
		if err == io.EOF {
			return
		}
		if err != nil {
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}
		if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}

		buf, err := marshaler.Marshal(streamChunk(resp, nil))
		if err != nil {
			grpclog.Printf("Failed to marshal response chunk: %v", err)
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}
		if _, err = w.Write(buf); err != nil {
			grpclog.Printf("Failed to send response chunk: %v", err)
			return
		}
		wroteHeader = true
		if _, err = w.Write(delimiter); err != nil {
			grpclog.Printf("Failed to send delimiter chunk: %v", err)
			return
		}
		f.Flush()
	}
}

func handleForwardResponseServerMetadata(w http.ResponseWriter, mux *ServeMux, md ServerMetadata) {
	for k, vs := range md.HeaderMD {
		if h, ok := mux.outgoingHeaderMatcher(k); ok {
			for _, v := range vs {
				w.Header().Add(h, v)
			}
		}
	}
}

func handleForwardResponseTrailerHeader(w http.ResponseWriter, md ServerMetadata) {
	for k := range md.TrailerMD {
		tKey := textproto.CanonicalMIMEHeaderKey(fmt.Sprintf("%s%s", MetadataTrailerPrefix, k))
		w.Header().Add("Trailer", tKey)
	}
}

func handleForwardResponseTrailer(w http.ResponseWriter, md ServerMetadata) {
	for k, vs := range md.TrailerMD {
		tKey := fmt.Sprintf("%s%s", MetadataTrailerPrefix, k)
		for _, v := range vs {
			w.Header().Add(tKey, v)
		}
	}
}

// ForwardResponseMessage forwards the message "resp" from gRPC server to REST client.
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
	md, ok := ServerMetadataFromContext(ctx)
	if !ok {
		grpclog.Printf("Failed to extract ServerMetadata from context")
	}

	handleForwardResponseServerMetadata(w, mux, md)
	handleForwardResponseTrailerHeader(w, md)
	w.Header().Set("Content-Type", marshaler.ContentType())
	if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	buf, err := marshaler.Marshal(resp)
	if err != nil {
		grpclog.Printf("Marshal error: %v", err)
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	if _, err = w.Write(buf); err != nil {
		grpclog.Printf("Failed to write response: %v", err)
	}

	handleForwardResponseTrailer(w, md)
}

func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, resp proto.Message, opts []func(context.Context, http.ResponseWriter, proto.Message) error) error {
	if len(opts) == 0 {
		return nil
	}
	for _, opt := range opts {
		if err := opt(ctx, w, resp); err != nil {
			grpclog.Printf("Error handling ForwardResponseOptions: %v", err)
			return err
		}
	}
	return nil
}

func handleForwardResponseStreamError(wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, err error) {
	buf, merr := marshaler.Marshal(streamChunk(nil, err))
	if merr != nil {
		grpclog.Printf("Failed to marshal an error: %v", merr)
		return
	}
	if !wroteHeader {
		s, ok := status.FromError(err)
		if !ok {
			s = status.New(codes.Unknown, err.Error())
		}
		w.WriteHeader(HTTPStatusFromCode(s.Code()))
	}
	if _, werr := w.Write(buf); werr != nil {
		grpclog.Printf("Failed to notify error to client: %v", werr)
		return
	}
}

func streamChunk(result proto.Message, err error) map[string]proto.Message {
	if err != nil {
		grpcCode := codes.Unknown
		if s, ok := status.FromError(err); ok {
			grpcCode = s.Code()
		}
		httpCode := HTTPStatusFromCode(grpcCode)
		return map[string]proto.Message{
			"error": &internal.StreamError{
				GrpcCode:   int32(grpcCode),
				HttpCode:   int32(httpCode),
				Message:    err.Error(),
				HttpStatus: http.StatusText(httpCode),
			},
		}
	}
	if result == nil {
		return streamChunk(nil, fmt.Errorf("empty response"))
	}
	return map[string]proto.Message{"result": result}
}
<commit_msg>Maintain default delimiter of newline<commit_after>package runtime

import (
	"fmt"
	"io"
	"net/http"
	"net/textproto"

	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime/internal"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/status"
)

// ForwardResponseStream forwards the stream from gRPC server to REST client.
func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
	f, ok := w.(http.Flusher)
	if !ok {
		grpclog.Printf("Flush not supported in %T", w)
		http.Error(w, "unexpected type of web server", http.StatusInternalServerError)
		return
	}

	md, ok := ServerMetadataFromContext(ctx)
	if !ok {
		grpclog.Printf("Failed to extract ServerMetadata from context")
		http.Error(w, "unexpected error", http.StatusInternalServerError)
		return
	}
	handleForwardResponseServerMetadata(w, mux, md)

	w.Header().Set("Transfer-Encoding", "chunked")
	w.Header().Set("Content-Type", marshaler.ContentType())
	if err := handleForwardResponseOptions(ctx, w, nil, opts); err != nil {
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	var delimiter []byte
	if d, ok := marshaler.(Delimited); ok {
		delimiter = d.Delimiter()
	} else {
	    delimiter = []byte("\n")
	}

	var wroteHeader bool
	for {
		resp, err := recv()
		if err == io.EOF {
			return
		}
		if err != nil {
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}
		if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}

		buf, err := marshaler.Marshal(streamChunk(resp, nil))
		if err != nil {
			grpclog.Printf("Failed to marshal response chunk: %v", err)
			handleForwardResponseStreamError(wroteHeader, marshaler, w, err)
			return
		}
		if _, err = w.Write(buf); err != nil {
			grpclog.Printf("Failed to send response chunk: %v", err)
			return
		}
		wroteHeader = true
		if _, err = w.Write(delimiter); err != nil {
			grpclog.Printf("Failed to send delimiter chunk: %v", err)
			return
		}
		f.Flush()
	}
}

func handleForwardResponseServerMetadata(w http.ResponseWriter, mux *ServeMux, md ServerMetadata) {
	for k, vs := range md.HeaderMD {
		if h, ok := mux.outgoingHeaderMatcher(k); ok {
			for _, v := range vs {
				w.Header().Add(h, v)
			}
		}
	}
}

func handleForwardResponseTrailerHeader(w http.ResponseWriter, md ServerMetadata) {
	for k := range md.TrailerMD {
		tKey := textproto.CanonicalMIMEHeaderKey(fmt.Sprintf("%s%s", MetadataTrailerPrefix, k))
		w.Header().Add("Trailer", tKey)
	}
}

func handleForwardResponseTrailer(w http.ResponseWriter, md ServerMetadata) {
	for k, vs := range md.TrailerMD {
		tKey := fmt.Sprintf("%s%s", MetadataTrailerPrefix, k)
		for _, v := range vs {
			w.Header().Add(tKey, v)
		}
	}
}

// ForwardResponseMessage forwards the message "resp" from gRPC server to REST client.
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
	md, ok := ServerMetadataFromContext(ctx)
	if !ok {
		grpclog.Printf("Failed to extract ServerMetadata from context")
	}

	handleForwardResponseServerMetadata(w, mux, md)
	handleForwardResponseTrailerHeader(w, md)
	w.Header().Set("Content-Type", marshaler.ContentType())
	if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	buf, err := marshaler.Marshal(resp)
	if err != nil {
		grpclog.Printf("Marshal error: %v", err)
		HTTPError(ctx, mux, marshaler, w, req, err)
		return
	}

	if _, err = w.Write(buf); err != nil {
		grpclog.Printf("Failed to write response: %v", err)
	}

	handleForwardResponseTrailer(w, md)
}

func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, resp proto.Message, opts []func(context.Context, http.ResponseWriter, proto.Message) error) error {
	if len(opts) == 0 {
		return nil
	}
	for _, opt := range opts {
		if err := opt(ctx, w, resp); err != nil {
			grpclog.Printf("Error handling ForwardResponseOptions: %v", err)
			return err
		}
	}
	return nil
}

func handleForwardResponseStreamError(wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, err error) {
	buf, merr := marshaler.Marshal(streamChunk(nil, err))
	if merr != nil {
		grpclog.Printf("Failed to marshal an error: %v", merr)
		return
	}
	if !wroteHeader {
		s, ok := status.FromError(err)
		if !ok {
			s = status.New(codes.Unknown, err.Error())
		}
		w.WriteHeader(HTTPStatusFromCode(s.Code()))
	}
	if _, werr := w.Write(buf); werr != nil {
		grpclog.Printf("Failed to notify error to client: %v", werr)
		return
	}
}

func streamChunk(result proto.Message, err error) map[string]proto.Message {
	if err != nil {
		grpcCode := codes.Unknown
		if s, ok := status.FromError(err); ok {
			grpcCode = s.Code()
		}
		httpCode := HTTPStatusFromCode(grpcCode)
		return map[string]proto.Message{
			"error": &internal.StreamError{
				GrpcCode:   int32(grpcCode),
				HttpCode:   int32(httpCode),
				Message:    err.Error(),
				HttpStatus: http.StatusText(httpCode),
			},
		}
	}
	if result == nil {
		return streamChunk(nil, fmt.Errorf("empty response"))
	}
	return map[string]proto.Message{"result": result}
}
<|endoftext|>


<commit_before>package tfdownload

import (
    "net/http"
    "io"
    "os"
    "runtime"
    "fmt"
    "regexp"

    "github.com/fatih/color"
)

var (
    url_tf    string
    version   string
    denied    []string
    fatal     *color.Color
    good      *color.Color
)

func init()  {
    fatal = color.New(color.FgRed, color.Bold)
    good = color.New(color.FgGreen, color.Bold)
}

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

func Run(version string) bool {
    match, err := regexp.MatchString("[0-9]+\\.[0-6]+\\.[0-9]+(-(rc|beta)[0-9]+)?", version)
    check(err)

    if !match {
        // Formulation URL Terraform Website
        fmt.Printf("Attempting to download version: %s\n", version)
        url_tf = "https://releases.hashicorp.com/terraform/" + version + "/terraform_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH + ".zip"
        resp, err := http.Get(url_tf)
        check(err)
        defer resp.Body.Close()

        // Verify code equal 200
        if (err == nil) && (resp.StatusCode == 200) {
            good.Printf("Start download ...\n")
            out, err := os.Create("/tmp/terraform-" + version + ".zip")
            check(err)
            defer out.Close()

            _, err = io.Copy(out, resp.Body)
            check(err)

            return true
        } else {
            fatal.Printf("[FATAL] Download impossible, this version doesn't exist !")
            return false
        }
    } else {
        fatal.Printf("[ERROR] This version (%s) is not supported !", version)
        return false
    }

    return false
}
<commit_msg>~ Fix error download SSL<commit_after>package tfdownload

import (
    "net/http"
    "io"
    "os"
    "runtime"
    "fmt"
    "regexp"
    "crypto/tls"

    "github.com/fatih/color"
)

var (
    url_tf    string
    version   string
    denied    []string
    fatal     *color.Color
    good      *color.Color
)

func init()  {
    fatal = color.New(color.FgRed, color.Bold)
    good = color.New(color.FgGreen, color.Bold)
}

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

func Run(version string) bool {
    match, err := regexp.MatchString("[0-9]+\\.[0-6]+\\.[0-9]+(-(rc|beta)[0-9]+)?", version)
    check(err)

    if !match {
        // Formulation URL Terraform Website
        fmt.Printf("Attempting to download version: %s\n", version)
        url_tf = "https://releases.hashicorp.com/terraform/" + version + "/terraform_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH + ".zip"

        tr := &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        }
        client := &http.Client{Transport: tr}
        resp, err := client.Get(url_tf)
        check(err)
        defer resp.Body.Close()

        // Verify code equal 200
        if (err == nil) && (resp.StatusCode == 200) {
            good.Printf("Start download ...\n")
            out, err := os.Create("/tmp/terraform-" + version + ".zip")
            check(err)
            defer out.Close()

            _, err = io.Copy(out, resp.Body)
            check(err)

            return true
        } else {
            fatal.Printf("[FATAL] Download impossible, this version doesn't exist !")
            return false
        }
    } else {
        fatal.Printf("[ERROR] This version (%s) is not supported !", version)
        return false
    }

    return false
}
<|endoftext|>


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

// Package os provides a platform-independent interface to operating system
// functionality. The design is Unix-like, although the error handling is
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as Open or Stat, the error
// will include the failing file name when printed and will be of type
// *PathError, which may be unpacked for more information.
//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
//
// Here is a simple example, opening a file and reading some of it.
//
//	file, err := os.Open("file.go") // For read access.
//	if err != nil {
//		log.Fatal(err)
//	}
//
// If the open fails, the error string will be self-explanatory, like
//
//	open file.go: no such file or directory
//
// The file's data can then be read into a slice of bytes. Read and
// Write take their byte counts from the length of the argument slice.
//
//	data := make([]byte, 100)
//	count, err := file.Read(data)
//	if err != nil {
//		log.Fatal(err)
//	}
//	fmt.Printf("read %d bytes: %q\n", count, data[:count])
//
package os

import (
	"io"
	"syscall"
)

// Name returns the name of the file as presented to Open.
func (f *File) Name() string { return f.name }

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
//
// Note that the Go runtime writes to standard error for panics and crashes;
// closing Stderr may cause those messages to go elsewhere, perhaps
// to a file opened later.
var (
	Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // if possible, truncate file when opened.
)

// Seek whence values.
//
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
	SEEK_SET int = 0 // seek relative to the origin of the file
	SEEK_CUR int = 1 // seek relative to the current offset
	SEEK_END int = 2 // seek relative to the end
)

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
// EOF is signaled by a zero count with err set to io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	if n == 0 && len(b) > 0 && e == nil {
		return 0, io.EOF
	}
	if e != nil {
		err = &PathError{"read", f.name, e}
	}
	return n, err
}

// ReadAt reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	for len(b) > 0 {
		m, e := f.pread(b, off)
		if m == 0 && e == nil {
			return n, io.EOF
		}
		if e != nil {
			err = &PathError{"read", f.name, e}
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	n, e := f.write(b)
	if n < 0 {
		n = 0
	}
	if n != len(b) {
		err = io.ErrShortWrite
	}

	epipecheck(f, e)

	if e != nil {
		err = &PathError{"write", f.name, e}
	}
	return n, err
}

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	for len(b) > 0 {
		m, e := f.pwrite(b, off)
		if e != nil {
			err = &PathError{"write", f.name, e}
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
	if err := f.checkValid("seek"); err != nil {
		return 0, err
	}
	r, e := f.seek(offset, whence)
	if e == nil && f.dirinfo != nil && r != 0 {
		e = syscall.EISDIR
	}
	if e != nil {
		return 0, &PathError{"seek", f.name, e}
	}
	return r, nil
}

// WriteString is like Write, but writes the contents of string s rather than
// a slice of bytes.
func (f *File) WriteString(s string) (n int, err error) {
	return f.Write([]byte(s))
}

// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
	e := syscall.Mkdir(name, syscallMode(perm))

	if e != nil {
		return &PathError{"mkdir", name, e}
	}

	// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
	if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
		Chmod(name, perm)
	}

	return nil
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	if e := syscall.Chdir(dir); e != nil {
		return &PathError{"chdir", dir, e}
	}
	return nil
}

// Chdir changes the current working directory to the file,
// which must be a directory.
// If there is an error, it will be of type *PathError.
func (f *File) Chdir() error {
	if err := f.checkValid("chdir"); err != nil {
		return err
	}
	if e := syscall.Fchdir(f.fd); e != nil {
		return &PathError{"chdir", f.name, e}
	}
	return nil
}

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

// Create creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

// lstat is overridden in tests.
var lstat = Lstat

// Rename renames (moves) oldpath to newpath.
// If newpath already exists, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
	return rename(oldpath, newpath)
}

// Many functions in package syscall return a count of -1 instead of 0.
// Using fixCount(call()) instead of call() corrects the count.
func fixCount(n int, err error) (int, error) {
	if n < 0 {
		n = 0
	}
	return n, err
}

// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
	if f == nil {
		return ErrInvalid
	}
	if f.fd == badFd {
		return &PathError{op, f.name, ErrClosed}
	}
	return nil
}
<commit_msg>os: adjust (*File).Read comment<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package os provides a platform-independent interface to operating system
// functionality. The design is Unix-like, although the error handling is
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as Open or Stat, the error
// will include the failing file name when printed and will be of type
// *PathError, which may be unpacked for more information.
//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
//
// Here is a simple example, opening a file and reading some of it.
//
//	file, err := os.Open("file.go") // For read access.
//	if err != nil {
//		log.Fatal(err)
//	}
//
// If the open fails, the error string will be self-explanatory, like
//
//	open file.go: no such file or directory
//
// The file's data can then be read into a slice of bytes. Read and
// Write take their byte counts from the length of the argument slice.
//
//	data := make([]byte, 100)
//	count, err := file.Read(data)
//	if err != nil {
//		log.Fatal(err)
//	}
//	fmt.Printf("read %d bytes: %q\n", count, data[:count])
//
package os

import (
	"io"
	"syscall"
)

// Name returns the name of the file as presented to Open.
func (f *File) Name() string { return f.name }

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
//
// Note that the Go runtime writes to standard error for panics and crashes;
// closing Stderr may cause those messages to go elsewhere, perhaps
// to a file opened later.
var (
	Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // if possible, truncate file when opened.
)

// Seek whence values.
//
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
	SEEK_SET int = 0 // seek relative to the origin of the file
	SEEK_CUR int = 1 // seek relative to the current offset
	SEEK_END int = 2 // seek relative to the end
)

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	if n == 0 && len(b) > 0 && e == nil {
		return 0, io.EOF
	}
	if e != nil {
		err = &PathError{"read", f.name, e}
	}
	return n, err
}

// ReadAt reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	for len(b) > 0 {
		m, e := f.pread(b, off)
		if m == 0 && e == nil {
			return n, io.EOF
		}
		if e != nil {
			err = &PathError{"read", f.name, e}
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	n, e := f.write(b)
	if n < 0 {
		n = 0
	}
	if n != len(b) {
		err = io.ErrShortWrite
	}

	epipecheck(f, e)

	if e != nil {
		err = &PathError{"write", f.name, e}
	}
	return n, err
}

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	for len(b) > 0 {
		m, e := f.pwrite(b, off)
		if e != nil {
			err = &PathError{"write", f.name, e}
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
	if err := f.checkValid("seek"); err != nil {
		return 0, err
	}
	r, e := f.seek(offset, whence)
	if e == nil && f.dirinfo != nil && r != 0 {
		e = syscall.EISDIR
	}
	if e != nil {
		return 0, &PathError{"seek", f.name, e}
	}
	return r, nil
}

// WriteString is like Write, but writes the contents of string s rather than
// a slice of bytes.
func (f *File) WriteString(s string) (n int, err error) {
	return f.Write([]byte(s))
}

// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
	e := syscall.Mkdir(name, syscallMode(perm))

	if e != nil {
		return &PathError{"mkdir", name, e}
	}

	// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
	if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
		Chmod(name, perm)
	}

	return nil
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	if e := syscall.Chdir(dir); e != nil {
		return &PathError{"chdir", dir, e}
	}
	return nil
}

// Chdir changes the current working directory to the file,
// which must be a directory.
// If there is an error, it will be of type *PathError.
func (f *File) Chdir() error {
	if err := f.checkValid("chdir"); err != nil {
		return err
	}
	if e := syscall.Fchdir(f.fd); e != nil {
		return &PathError{"chdir", f.name, e}
	}
	return nil
}

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

// Create creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

// lstat is overridden in tests.
var lstat = Lstat

// Rename renames (moves) oldpath to newpath.
// If newpath already exists, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
	return rename(oldpath, newpath)
}

// Many functions in package syscall return a count of -1 instead of 0.
// Using fixCount(call()) instead of call() corrects the count.
func fixCount(n int, err error) (int, error) {
	if n < 0 {
		n = 0
	}
	return n, err
}

// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
	if f == nil {
		return ErrInvalid
	}
	if f.fd == badFd {
		return &PathError{op, f.name, ErrClosed}
	}
	return nil
}
<|endoftext|>


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

package main

import (
	"bufio";
	"flag";
	"fmt";
	"http";
	"io";
	"net";
	"os";
)

// hello world, the web server
func HelloServer(c *http.Conn, req *http.Request) {
	io.WriteString(c, "hello, world!\n");
}

// simple counter server
type Counter struct {
	n int;
}

func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
	fmt.Fprintf(c, "counter = %d\n", ctr.n);
	ctr.n++;
}

// simple file server
var webroot = flag.String("root", "/home/rsc", "web root directory")
func FileServer(c *http.Conn, req *http.Request) {
	c.SetHeader("content-type", "text/plain; charset=utf-8");
	path := *webroot + req.Url.Path;	// TODO: insecure: use os.CleanName
	fd, err := os.Open(path, os.O_RDONLY, 0);
	if err != nil {
		c.WriteHeader(http.StatusNotFound);
		fmt.Fprintf(c, "open %s: %v\n", path, err);
		return;
	}
	n, err1 := io.Copy(fd, c);
	fmt.Fprintf(c, "[%d bytes]\n", n);
}

func main() {
	flag.Parse();
	http.Handle("/counter", new(Counter));
	http.Handle("/go/", http.HandlerFunc(FileServer));
	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
	err := http.ListenAndServe(":12345", nil);
	if err != nil {
		panic("ListenAndServe: ", err.String())
	}
}

<commit_msg>further pedagogy: a channel that satisfies the HTTP server interface<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio";
	"flag";
	"fmt";
	"http";
	"io";
	"net";
	"os";
)

// hello world, the web server
func HelloServer(c *http.Conn, req *http.Request) {
	io.WriteString(c, "hello, world!\n");
}

// simple counter server
type Counter struct {
	n int;
}

func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
	fmt.Fprintf(c, "counter = %d\n", ctr.n);
	ctr.n++;
}

// simple file server
var webroot = flag.String("root", "/home/rsc", "web root directory")
func FileServer(c *http.Conn, req *http.Request) {
	c.SetHeader("content-type", "text/plain; charset=utf-8");
	path := *webroot + req.Url.Path;	// TODO: insecure: use os.CleanName
	fd, err := os.Open(path, os.O_RDONLY, 0);
	if err != nil {
		c.WriteHeader(http.StatusNotFound);
		fmt.Fprintf(c, "open %s: %v\n", path, err);
		return;
	}
	n, err1 := io.Copy(fd, c);
	fmt.Fprintf(c, "[%d bytes]\n", n);
}

// a channel (just for the fun of it)
type Chan chan int

func ChanCreate() Chan {
	c := make(Chan);
	go func(c Chan) {
		for x := 0;; x++ {
			c <- x
		}
	}(c);
	return c;
}

func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
	io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch));
}

func main() {
	flag.Parse();
	http.Handle("/counter", new(Counter));
	http.Handle("/go/", http.HandlerFunc(FileServer));
	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
	http.Handle("/chan", ChanCreate());
	err := http.ListenAndServe(":12345", nil);
	if err != nil {
		panic("ListenAndServe: ", err.String())
	}
}

<|endoftext|>