<commit_before>package semver

// compare is used to in dependency resolution to compare two versions of
// software. The return value is an integer, either -1, 0, or 1. The result may
// be compared against '0' with standard operators to determine greater than,
// less than, etc.
func (v1 *SemVer) compare(v2 *SemVer) int {
	if v1.String() == v2.String() {
		return 0
	}
	return vcomp(v1.parts(), v2.parts())
}

// vcomp is a recursive function which will compare two slices of version
// components and return an integer representing which is greater. The result
// is intended to be compared against integer 0 using standard operators.
func vcomp(v1, v2 []string) int {
	switch {
	case len(v1) == 0 && len(v2) == 0:
		return 0
	case v1[0] > v2[0]:
		return 1
	case v1[0] < v2[0]:
		return -1
	default:
		return vcomp(v1[1:], v2[1:])
	}
}

// greaterThan determines if one version is larger than another
func (v1 *SemVer) greaterThan(v2 *SemVer) bool {
	return v1.compare(v2) > 0
}

// lessThan is the inverse of the Greater than function
func (v1 *SemVer) lessThan(v2 *SemVer) bool {
	return v1.compare(v2) < 0
}

// equalTo determines if two versions are equivalent.
func (v1 *SemVer) equalTo(v2 *SemVer) bool {
	return v1.compare(v2) == 0
}

// twiddleCompare will perform a pessemistic version comparison (~> comparator)
// to allow implicit semantic versioning trust between versioned artifacts.
func (v1 *SemVer) twiddleCompare(v2 *SemVer) bool {
	if v1.equalTo(v2) {
		return true
	}

	partsA := v1.parts()
	partsB := v2.parts()

	last := len(partsB)
	for i := last - 1; i >= 0; i-- {
		if partsB[i] == "" || partsB[i] == "0" {
			last = i
		}
		if partsB[i] == "" {
			continue
		}
		break
	}

	if vcomp(partsA[:last-1], partsB[:last-1]) != 0 {
		return false
	}
	if vcomp(partsA[last-1:], partsB[last-1:]) < 0 {
		return false
	}
	return true
}

// Compare will take a human-comprehensible string comparator and execute
// the proper comparison function agains v2.
func (v1 *SemVer) Compare(comparator string, v2 *SemVer) bool {
	switch comparator {
	case ">":
		return v1.greaterThan(v2)
	case "<":
		return v1.lessThan(v2)
	case "=":
		return v1.equalTo(v2)
	case ">=":
		return v1.greaterThan(v2) || v1.equalTo(v2)
	case "<=":
		return v1.lessThan(v2) || v1.equalTo(v2)
	case "~>":
		return v1.twiddleCompare(v2)
	default:
		return false
	}
}
<commit_msg>Ignore build metadata in build comparison per SemVer.<commit_after>package semver

// compare is used to in dependency resolution to compare two versions of
// software. The return value is an integer, either -1, 0, or 1. The result may
// be compared against '0' with standard operators to determine greater than,
// less than, etc.
func (v1 *SemVer) compare(v2 *SemVer) int {
	if v1.String() == v2.String() {
		return 0
	}
	partsA := v1.parts()
	partsB := v2.parts()
	return vcomp(partsA[:len(partsA)-1], partsB[:len(partsB)-1])
}

// vcomp is a recursive function which will compare two slices of version
// components and return an integer representing which is greater. The result
// is intended to be compared against integer 0 using standard operators.
func vcomp(v1, v2 []string) int {
	switch {
	case len(v1) == 0 && len(v2) == 0:
		return 0
	case v1[0] > v2[0]:
		return 1
	case v1[0] < v2[0]:
		return -1
	default:
		return vcomp(v1[1:], v2[1:])
	}
}

// greaterThan determines if one version is larger than another
func (v1 *SemVer) greaterThan(v2 *SemVer) bool {
	return v1.compare(v2) > 0
}

// lessThan is the inverse of the Greater than function
func (v1 *SemVer) lessThan(v2 *SemVer) bool {
	return v1.compare(v2) < 0
}

// equalTo determines if two versions are equivalent.
func (v1 *SemVer) equalTo(v2 *SemVer) bool {
	return v1.compare(v2) == 0
}

// twiddleCompare will perform a pessemistic version comparison (~> comparator)
// to allow implicit semantic versioning trust between versioned artifacts.
func (v1 *SemVer) twiddleCompare(v2 *SemVer) bool {
	if v1.equalTo(v2) {
		return true
	}

	partsA := v1.parts()
	partsB := v2.parts()

	last := len(partsB)
	for i := last - 1; i >= 0; i-- {
		if partsB[i] == "" || partsB[i] == "0" {
			last = i
		}
		if partsB[i] == "" {
			continue
		}
		break
	}

	if vcomp(partsA[:last-1], partsB[:last-1]) != 0 {
		return false
	}
	if vcomp(partsA[last-1:], partsB[last-1:]) < 0 {
		return false
	}
	return true
}

// Compare will take a human-comprehensible string comparator and execute
// the proper comparison function agains v2.
func (v1 *SemVer) Compare(comparator string, v2 *SemVer) bool {
	switch comparator {
	case ">":
		return v1.greaterThan(v2)
	case "<":
		return v1.lessThan(v2)
	case "=":
		return v1.equalTo(v2)
	case ">=":
		return v1.greaterThan(v2) || v1.equalTo(v2)
	case "<=":
		return v1.lessThan(v2) || v1.equalTo(v2)
	case "~>":
		return v1.twiddleCompare(v2)
	default:
		return false
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package main

import (
	"fmt"
	"github.com/maruel/wi/wi-plugin"
	"io/ioutil"
	"log"
	"net/rpc"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

// TODO(maruel): Implement the RPC to make plugins work.

// loadPlugin starts a plugin and returns the process.
func loadPlugin(server *rpc.Server, f string) *os.Process {
	cmd := exec.Command(f)
	cmd.Env = append(os.Environ(), "WI=plugin")

	stdin, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}
	if err := cmd.Start(); err != nil {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal(err)
	}

	// Fail on any write to Stderr.
	go func() {
		buf := make([]byte, 2048)
		n, _ := stderr.Read(buf)
		if n != 0 {
			panic(fmt.Sprintf("Plugin %s failed: %s", f, buf))
		}
	}()

	// Before starting the RPC, ensures the version matches.
	expectedVersion := wi.CalculateVersion()
	b := make([]byte, 40)
	n, err := stdout.Read(b)
	if err != nil {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal(err)
	}
	if n != 40 {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal("Unexpected size")
	}
	actualVersion := string(b)
	if expectedVersion != actualVersion {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatalf("For %s; expected %s, got %s", f, expectedVersion, actualVersion)
	}

	// Start the RPC server for this plugin.
	go func() {
		server.ServeConn(wi.MakeReadWriteCloser(stdout, stdin))
	}()

	return cmd.Process
}

// loadPlugins loads all the plugins and returns the process handles.
func loadPlugins(e wi.Editor) []*os.Process {
	// TODO(maruel): Get the path of the executable. It's a bit involved since
	// very OS specific but it's doable. Then all plugins in the same directory
	// are access.
	searchDir := "."
	files, err := ioutil.ReadDir(searchDir)
	if err != nil {
		return nil
	}
	if len(files) == 0 {
		// Save registering RPC stuff when unnecessary.
		return nil
	}

	var wg sync.WaitGroup
	c := make(chan *os.Process)
	server := rpc.NewServer()
	// TODO(maruel): http://golang.org/pkg/net/rpc/#Server.RegisterName
	// It should be an interface with methods of style DoStuff(Foo, Bar) Baz
	//server.RegisterName("Editor", e)
	for _, f := range files {
		name := f.Name()
		if !strings.HasPrefix(name, "wi-plugin-") {
			continue
		}
		// Crude check for executable test.
		if runtime.GOOS == "windows" {
			if !strings.HasSuffix(name, ".exe") {
				continue
			}
		} else {
			if f.Mode()&0111 == 0 {
				continue
			}
		}
		wg.Add(1)
		go func(s *rpc.Server, n string) {
			c <- loadPlugin(s, n)
			wg.Done()
		}(server, name)
	}

	var wg2 sync.WaitGroup
	out := []*os.Process{}
	wg2.Add(1)
	go func() {
		for i := range c {
			out = append(out, i)
		}
		wg2.Done()
	}()

	// Wait for all the plugins to be loaded.
	wg.Wait()

	// Convert to a slice.
	close(c)
	wg2.Wait()
	return out
}
<commit_msg>Now doesn't crash, just hangs<commit_after>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package main

import (
	"fmt"
	"github.com/maruel/wi/wi-plugin"
	"io/ioutil"
	"log"
	"net/rpc"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

// TODO(maruel): Implement the RPC to make plugins work.

// loadPlugin starts a plugin and returns the process.
func loadPlugin(server *rpc.Server, f string) *os.Process {
	cmd := exec.Command(f)
	cmd.Env = append(os.Environ(), "WI=plugin")

	stdin, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}
	if err := cmd.Start(); err != nil {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal(err)
	}

	// Fail on any write to Stderr.
	go func() {
		buf := make([]byte, 2048)
		n, _ := stderr.Read(buf)
		if n != 0 {
			panic(fmt.Sprintf("Plugin %s failed: %s", f, buf))
		}
	}()

	// Before starting the RPC, ensures the version matches.
	expectedVersion := wi.CalculateVersion()
	b := make([]byte, 40)
	n, err := stdout.Read(b)
	if err != nil {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal(err)
	}
	if n != 40 {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatal("Unexpected size")
	}
	actualVersion := string(b)
	if expectedVersion != actualVersion {
		// Surface the error as an "alert", since it's not a fatal error.
		log.Fatalf("For %s; expected %s, got %s", f, expectedVersion, actualVersion)
	}

	// Start the RPC server for this plugin.
	go func() {
		server.ServeConn(wi.MakeReadWriteCloser(stdout, stdin))
	}()

	return cmd.Process
}

// loadPlugins loads all the plugins and returns the process handles.
func loadPlugins(e wi.Editor) []*os.Process {
	// TODO(maruel): Get the path of the executable. It's a bit involved since
	// very OS specific but it's doable. Then all plugins in the same directory
	// are access.
	searchDir := "."
	files, err := ioutil.ReadDir(searchDir)
	if err != nil {
		return nil
	}
	if len(files) == 0 {
		// Save registering RPC stuff when unnecessary.
		return nil
	}

	var wg sync.WaitGroup
	c := make(chan *os.Process)
	server := rpc.NewServer()
	// TODO(maruel): http://golang.org/pkg/net/rpc/#Server.RegisterName
	// It should be an interface with methods of style DoStuff(Foo, Bar) Baz
	//server.RegisterName("Editor", e)
	for _, f := range files {
		if f.IsDir() {
			continue
		}
		name := f.Name()
		if !strings.HasPrefix(name, "wi-plugin-") {
			continue
		}
		// Crude check for executable test.
		if runtime.GOOS == "windows" {
			if !strings.HasSuffix(name, ".exe") {
				continue
			}
		} else {
			if f.Mode()&0111 == 0 {
				continue
			}
		}
		wg.Add(1)
		go func(s *rpc.Server, n string) {
			c <- loadPlugin(s, n)
			wg.Done()
		}(server, name)
	}

	var wg2 sync.WaitGroup
	out := []*os.Process{}
	wg2.Add(1)
	go func() {
		for i := range c {
			out = append(out, i)
		}
		wg2.Done()
	}()

	// Wait for all the plugins to be loaded.
	wg.Wait()

	// Convert to a slice.
	close(c)
	wg2.Wait()
	return out
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

const debug = true
const maxSizeHealthStatusQueue = 60 // Magnificent Server Log Store Size, displays the last 60 seconds in the plot

type Msg struct {
	MessageId string
	Content   string
	TimeStamp int64
}

// Client connection consists of the websocket and the client ip
type Client struct {
	websocket *websocket.Conn
	clientIP  string
}

type ServiceMonitor struct {
	errChan          chan error // unbuffered channel
	errChanWebsock   chan error // unbuffered channel
	activeClients    map[string]Client
	healthStatusChan chan bool
	alertChan        chan string
	newClientChan    chan Client
	alertQueue        []Msg
	healthStatusQueue []Msg
}

func NewServiceMonitor() *ServiceMonitor {
	m := ServiceMonitor{}
	m.activeClients = make(map[string]Client)
	m.errChan = make(chan error)
	m.healthStatusChan = make(chan bool, 10)
	m.alertChan = make(chan string, 10)
	m.newClientChan = make(chan Client, 10)
	m.alertQueue = make([]Msg, 0)
	m.healthStatusQueue = make([]Msg, 0)
	return &m
}

func BoolToString(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func (m *ServiceMonitor) sendClientMsg(msg *Msg, ip string) {
	if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to:", ip, err.Error())
		log.Println("Client disconnected:", ip)
		delete(m.activeClients, ip)
	}
}

func (m *ServiceMonitor) sendBroadcastMsg(msg *Msg) {
	for ip, _ := range m.activeClients {
		if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
			// we could not send the message to a peer
			log.Println("Could not send message to:", ip, err.Error())
			log.Println("Client disconnected:", ip)
			delete(m.activeClients, ip)
		}
	}
}

func (m *ServiceMonitor) sendQueueData(ip string) {
	for _, msg := range m.healthStatusQueue {
		m.sendClientMsg(&msg, ip)
	}

	for _, msg := range m.alertQueue {
		m.sendClientMsg(&msg, ip)
	}
}

func (m *ServiceMonitor) appendhealthStatus(msg Msg) {
	// add msg to HealthStatusQueue
	if len(m.healthStatusQueue) < maxSizeHealthStatusQueue {
		m.healthStatusQueue = append(m.healthStatusQueue, msg)
	} else {
		m.healthStatusQueue = m.healthStatusQueue[1:]
		m.healthStatusQueue = append(m.healthStatusQueue, msg)
	}
}

func (m *ServiceMonitor) appendAlert(msg Msg) {
	// add msg to alertQueue
	if len(m.alertQueue) < maxSizeHealthStatusQueue {
		m.alertQueue = append(m.alertQueue, msg)
	} else {
		m.alertQueue = m.alertQueue[1:]
		m.alertQueue = append(m.alertQueue, msg)
	}
}

// this routine handles all outgoing websocket messages
func (m *ServiceMonitor) pushDataToClients() {
	for {
		select {
		// a new Client is connecting
		case newClient := <-m.newClientChan:
			// send current Queue data to the new connecting client
			m.activeClients[newClient.clientIP] = newClient
			m.sendQueueData(newClient.clientIP)

		// broadcast a new health status message to all clients
		// newHealthStatus == 1 ... deamon failure
		// newHealthStatus == 0 ... deamon ok
		case newHealthStatus := <-m.healthStatusChan:
			msg := Msg{"Plot", BoolToString(newHealthStatus), time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			m.appendhealthStatus(msg)

		// broadcast an alert message to all clients
		case newAlert := <-m.alertChan:
			msg := Msg{"Alert", newAlert, time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			m.appendAlert(msg)
		}
	}
}

// WebSocket handler to handle clients
func (m *ServiceMonitor) wsHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()

	client := conn.RemoteAddr().String()
	if debug {
		log.Println("New client connected:", client)
	}

	m.newClientChan <- Client{conn, client}

	// wait for errChan, so the websocket stays open otherwise it'll close
	err = <-m.errChanWebsock
}

// handler for the main page
func HomeHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-type", "text/html")
	webpage, err := ioutil.ReadFile("home.html")

	if err != nil {
		http.Error(response, fmt.Sprintf("home.html file error %v", err), 500)
	}

	fmt.Fprint(response, string(webpage))
}

func (m *ServiceMonitor) parseResponse(resp string) {
	if !strings.Contains(resp, "Magnificent!") {
		m.healthStatusChan <- false
	} else {
		m.healthStatusChan <- true
		m.alertChan <- "Server has failed"
	}
}

func (m *ServiceMonitor) requestDaemonStatus(url string) {
	response, err := http.Get(url)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	resp := string(contents)
	m.parseResponse(resp)
}

func (m *ServiceMonitor) monitorDaemon(url string, time_interval time.Duration) {
	for {
		m.requestDaemonStatus(url)
		time.Sleep(time_interval * time.Second)
	}
}

func (m *ServiceMonitor) startHTTPServer() {
	http.Handle("/", http.HandlerFunc(HomeHandler))
	http.HandleFunc("/sock", m.wsHandler)

	err := http.ListenAndServe(":8080", nil)
	m.errChanWebsock <- err
	m.errChan <- err
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	m := NewServiceMonitor()

	go m.startHTTPServer()
	go m.pushDataToClients()
	go m.monitorDaemon("http://localhost:12345/", 1)

	err := <-m.errChan
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}
<commit_msg>simplified code<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

const debug = true
const maxSizeHealthStatusQueue = 60 // Magnificent Server Log Store Size, displays the last 60 seconds in the plot

type Msg struct {
	MessageId string
	Content   string
	TimeStamp int64
}

// Client connection consists of the websocket and the client ip
type Client struct {
	websocket *websocket.Conn
	clientIP  string
}

type ServiceMonitor struct {
	errChan          chan error // unbuffered channel
	errChanWebsock   chan error // unbuffered channel
	activeClients    map[string]Client
	healthStatusChan chan bool
	alertChan        chan string
	newClientChan    chan Client
	alertQueue        []Msg
	healthStatusQueue []Msg
}

func NewServiceMonitor() *ServiceMonitor {
	m := ServiceMonitor{}
	m.activeClients = make(map[string]Client)
	m.errChan = make(chan error)
	m.healthStatusChan = make(chan bool, 10)
	m.alertChan = make(chan string, 10)
	m.newClientChan = make(chan Client, 10)
	m.alertQueue = make([]Msg, 0)
	m.healthStatusQueue = make([]Msg, 0)
	return &m
}

func BoolToString(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func (m *ServiceMonitor) sendClientMsg(msg *Msg, ip string) {
	if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to:", ip, err.Error())
		log.Println("Client disconnected:", ip)
		delete(m.activeClients, ip)
	}
}

func (m *ServiceMonitor) sendBroadcastMsg(msg *Msg) {
	for ip, con := range m.activeClients {
		if err := con.websocket.WriteJSON(msg); err != nil {
			// we could not send the message to a peer
			log.Println("Could not send message to:", ip, err.Error())
			log.Println("Client disconnected:", ip)
			delete(m.activeClients, ip)
		}
	}
}

func (m *ServiceMonitor) sendQueueData(ip string) {
	for _, msg := range m.healthStatusQueue {
		m.sendClientMsg(&msg, ip)
	}

	for _, msg := range m.alertQueue {
		m.sendClientMsg(&msg, ip)
	}
}

func (m *ServiceMonitor) appendhealthStatus(msg Msg) {
	// add msg to HealthStatusQueue
	if len(m.healthStatusQueue) < maxSizeHealthStatusQueue {
		m.healthStatusQueue = append(m.healthStatusQueue, msg)
	} else {
		m.healthStatusQueue = m.healthStatusQueue[1:]
		m.healthStatusQueue = append(m.healthStatusQueue, msg)
	}
}

func (m *ServiceMonitor) appendAlert(msg Msg) {
	// add msg to alertQueue
	if len(m.alertQueue) < maxSizeHealthStatusQueue {
		m.alertQueue = append(m.alertQueue, msg)
	} else {
		m.alertQueue = m.alertQueue[1:]
		m.alertQueue = append(m.alertQueue, msg)
	}
}

// this routine handles all outgoing websocket messages
func (m *ServiceMonitor) pushDataToClients() {
	for {
		select {
		// a new Client is connecting
		case newClient := <-m.newClientChan:
			// send current Queue data to the new connecting client
			m.activeClients[newClient.clientIP] = newClient
			m.sendQueueData(newClient.clientIP)

		// broadcast a new health status message to all clients
		// newHealthStatus == 1 ... deamon failure
		// newHealthStatus == 0 ... deamon ok
		case newHealthStatus := <-m.healthStatusChan:
			msg := Msg{"Plot", BoolToString(newHealthStatus), time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			m.appendhealthStatus(msg)

		// broadcast an alert message to all clients
		case newAlert := <-m.alertChan:
			msg := Msg{"Alert", newAlert, time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			m.appendAlert(msg)
		}
	}
}

// WebSocket handler to handle clients
func (m *ServiceMonitor) wsHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()

	client := conn.RemoteAddr().String()
	if debug {
		log.Println("New client connected:", client)
	}

	m.newClientChan <- Client{conn, client}

	// wait for errChan, so the websocket stays open otherwise it'll close
	err = <-m.errChanWebsock
}

// handler for the main page
func HomeHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-type", "text/html")
	webpage, err := ioutil.ReadFile("home.html")

	if err != nil {
		http.Error(response, fmt.Sprintf("home.html file error %v", err), 500)
	}

	fmt.Fprint(response, string(webpage))
}

func (m *ServiceMonitor) parseResponse(resp string) {
	if !strings.Contains(resp, "Magnificent!") {
		m.healthStatusChan <- false
	} else {
		m.healthStatusChan <- true
		m.alertChan <- "Server has failed"
	}
}

func (m *ServiceMonitor) requestDaemonStatus(url string) {
	response, err := http.Get(url)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	resp := string(contents)
	m.parseResponse(resp)
}

func (m *ServiceMonitor) monitorDaemon(url string, time_interval time.Duration) {
	for {
		m.requestDaemonStatus(url)
		time.Sleep(time_interval * time.Second)
	}
}

func (m *ServiceMonitor) startHTTPServer() {
	http.Handle("/", http.HandlerFunc(HomeHandler))
	http.HandleFunc("/sock", m.wsHandler)

	err := http.ListenAndServe(":8080", nil)
	m.errChanWebsock <- err
	m.errChan <- err
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	m := NewServiceMonitor()

	go m.startHTTPServer()
	go m.pushDataToClients()
	go m.monitorDaemon("http://localhost:12345/", 1)

	err := <-m.errChan
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"fmt"
	"github.com/abh/go-metrics"
	"html/template"
	"io"
	"log"
	"math"
	"net/http"
	"os"
	"runtime"
	"sort"
	"time"
)

// Initial status message on websocket
type statusStreamMsgStart struct {
	Hostname string   `json:"h,omitemty"`
	Version  string   `json:"v"`
	Id       string   `json:"id"`
	Ip       string   `json:"ip"`
	Uptime   int      `json:"up"`
	Started  int      `json:"started"`
	Groups   []string `json:"groups"`
}

// Update message on websocket
type statusStreamMsgUpdate struct {
	Uptime     int     `json:"up"`
	QueryCount int64   `json:"qs"`
	Qps        int64   `json:"qps"`
	Qps1m      float64 `json:"qps1m,omitempty"`
}

type wsConnection struct {
	// The websocket connection.
	ws *websocket.Conn

	// Buffered channel of outbound messages.
	send chan string
}

type monitorHub struct {
	connections map[*wsConnection]bool
	broadcast   chan string
	register    chan *wsConnection
	unregister  chan *wsConnection
}

var hub = monitorHub{
	broadcast:   make(chan string),
	register:    make(chan *wsConnection, 10),
	unregister:  make(chan *wsConnection, 10),
	connections: make(map[*wsConnection]bool),
}

func (h *monitorHub) run() {
	for {
		select {
		case c := <-h.register:
			h.connections[c] = true
			log.Println("Queuing initial status")
			c.send <- initialStatus()
		case c := <-h.unregister:
			log.Println("Unregistering connection")
			delete(h.connections, c)
		case m := <-h.broadcast:
			for c := range h.connections {
				if len(c.send)+5 > cap(c.send) {
					log.Println("WS connection too close to cap")
					c.send <- `{"error": "too slow"}`
					close(c.send)
					go c.ws.Close()
					h.unregister <- c
					continue
				}
				select {
				case c.send <- m:
				default:
					close(c.send)
					delete(h.connections, c)
					log.Println("Closing channel when sending")
					go c.ws.Close()
				}
			}
		}
	}
}

func (c *wsConnection) reader() {
	for {
		var message string
		err := websocket.Message.Receive(c.ws, &message)
		if err != nil {
			if err == io.EOF {
				log.Println("WS connection closed")
			} else {
				log.Println("WS read error:", err)
			}
			break
		}
		log.Println("WS message", message)
		// TODO(ask) take configuration options etc
		//h.broadcast <- message
	}
	c.ws.Close()
}

func (c *wsConnection) writer() {
	for message := range c.send {
		err := websocket.Message.Send(c.ws, message)
		if err != nil {
			log.Println("WS write error:", err)
			break
		}
	}
	c.ws.Close()
}

func wsHandler(ws *websocket.Conn) {
	log.Println("Starting new WS connection")
	c := &wsConnection{send: make(chan string, 180), ws: ws}
	hub.register <- c
	defer func() {
		log.Println("sending unregister message")
		hub.unregister <- c
	}()
	go c.writer()
	c.reader()
}

func initialStatus() string {
	status := new(statusStreamMsgStart)
	status.Version = VERSION
	status.Id = serverId
	status.Ip = serverIP
	if len(serverGroups) > 0 {
		status.Groups = serverGroups
	}
	hostname, err := os.Hostname()
	if err == nil {
		status.Hostname = hostname
	}

	status.Uptime = int(time.Since(timeStarted).Seconds())
	status.Started = int(timeStarted.Unix())

	message, err := json.Marshal(status)
	return string(message)
}

func logStatus() {
	log.Println(initialStatus())
	// Does not impact performance too much
	lastQueryCount := qCounter.Count()

	for {
		current := qCounter.Count()
		newQueries := current - lastQueryCount
		lastQueryCount = current

		log.Println("goroutines", runtime.NumGoroutine(), "queries", newQueries)

		time.Sleep(60 * time.Second)
	}
}

func monitor(zones Zones) {
	go logStatus()

	if len(*flaghttp) == 0 {
		return
	}
	go hub.run()
	go httpHandler(zones)

	lastQueryCount := qCounter.Count()

	status := new(statusStreamMsgUpdate)
	var lastQps1m float64

	for {
		current := qCounter.Count()
		newQueries := current - lastQueryCount
		lastQueryCount = current

		status.Uptime = int(time.Since(timeStarted).Seconds())
		status.QueryCount = qCounter.Count()
		status.Qps = newQueries

		// go-metrics only updates the rate every 5 seconds, so don't pretend otherwise
		newQps1m := qCounter.Rate1()
		if newQps1m != lastQps1m {
			status.Qps1m = newQps1m
			lastQps1m = newQps1m
		} else {
			status.Qps1m = 0
		}

		message, err := json.Marshal(status)

		if err == nil {
			hub.broadcast <- string(message)
		}
		time.Sleep(1 * time.Second)
	}
}

func MainServer(w http.ResponseWriter, req *http.Request) {
	if req.RequestURI != "/version" {
		http.NotFound(w, req)
		return
	}
	io.WriteString(w, `<html><head><title>GeoDNS `+
		VERSION+`</title><body>`+
		initialStatus()+
		`</body></html>`)
}

type rate struct {
	Name    string
	Count   int64
	Metrics ZoneMetrics
}
type Rates []*rate

func (s Rates) Len() int      { return len(s) }
func (s Rates) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type RatesByCount struct{ Rates }

func (s RatesByCount) Less(i, j int) bool {
	ic := s.Rates[i].Count
	jc := s.Rates[j].Count
	if ic == jc {
		return s.Rates[i].Name < s.Rates[j].Name
	}
	return ic > jc
}

func round(val float64, prec int) float64 {

	var rounder float64
	intermed := val * math.Pow(10, float64(prec))

	if val >= 0.5 {
		rounder = math.Ceil(intermed)
	} else {
		rounder = math.Floor(intermed)
	}

	return rounder / math.Pow(10, float64(prec))

}

type histogramData struct {
	Max    int64
	Min    int64
	Mean   float64
	Pct90  float64
	Pct99  float64
	Pct999 float64
	StdDev float64
}

func setupHistogramData(met *metrics.StandardHistogram, dat *histogramData) {
	dat.Max = met.Max()
	dat.Min = met.Min()
	dat.Mean = met.Mean()
	dat.StdDev = met.StdDev()
	percentiles := met.Percentiles([]float64{0.90, 0.99, 0.999})
	dat.Pct90 = percentiles[0]
	dat.Pct99 = percentiles[1]
	dat.Pct999 = percentiles[2]
}

func StatusServer(zones Zones) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, req *http.Request) {

		tmpl := template.New("status_html")
		tmpl, err := tmpl.Parse(string(status_html()))

		if err != nil {
			str := fmt.Sprintf("Could not parse template: %s", err)
			io.WriteString(w, str)
			return
		}

		tmpl.Funcs(map[string]interface{}{
			"round": round,
		})

		rates := make(Rates, 0)

		for name, zone := range zones {
			count := zone.Metrics.Queries.Count()
			rates = append(rates, &rate{Name: name, Count: count, Metrics: zone.Metrics})
		}

		sort.Sort(RatesByCount{rates})

		type statusData struct {
			Version  string
			Zones    Rates
			Uptime   DayDuration
			Platform string
			Global   struct {
				Queries         *metrics.StandardMeter
				Histogram       histogramData
				HistogramRecent histogramData
			}
		}

		uptime := DayDuration{time.Since(timeStarted)}

		status := statusData{
			Version:  VERSION,
			Zones:    rates,
			Uptime:   uptime,
			Platform: runtime.GOARCH + "-" + runtime.GOOS,
		}

		status.Global.Queries = metrics.Get("queries").(*metrics.StandardMeter)

		setupHistogramData(metrics.Get("queries-histogram").(*metrics.StandardHistogram), &status.Global.Histogram)
		setupHistogramData(metrics.Get("queries-histogram-recent").(*metrics.StandardHistogram), &status.Global.HistogramRecent)

		err = tmpl.Execute(w, status)
		if err != nil {
			log.Println("Status template error", err)
		}
	}
}

func httpHandler(zones Zones) {
	http.Handle("/monitor", websocket.Handler(wsHandler))
	http.HandleFunc("/status", StatusServer(zones))
	http.HandleFunc("/", MainServer)

	log.Println("Starting HTTP interface on", *flaghttp)

	log.Fatal(http.ListenAndServe(*flaghttp, nil))
}
<commit_msg>Remove unused code<commit_after>package main

import (
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"fmt"
	"github.com/abh/go-metrics"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"runtime"
	"sort"
	"time"
)

// Initial status message on websocket
type statusStreamMsgStart struct {
	Hostname string   `json:"h,omitemty"`
	Version  string   `json:"v"`
	Id       string   `json:"id"`
	Ip       string   `json:"ip"`
	Uptime   int      `json:"up"`
	Started  int      `json:"started"`
	Groups   []string `json:"groups"`
}

// Update message on websocket
type statusStreamMsgUpdate struct {
	Uptime     int     `json:"up"`
	QueryCount int64   `json:"qs"`
	Qps        int64   `json:"qps"`
	Qps1m      float64 `json:"qps1m,omitempty"`
}

type wsConnection struct {
	// The websocket connection.
	ws *websocket.Conn

	// Buffered channel of outbound messages.
	send chan string
}

type monitorHub struct {
	connections map[*wsConnection]bool
	broadcast   chan string
	register    chan *wsConnection
	unregister  chan *wsConnection
}

var hub = monitorHub{
	broadcast:   make(chan string),
	register:    make(chan *wsConnection, 10),
	unregister:  make(chan *wsConnection, 10),
	connections: make(map[*wsConnection]bool),
}

func (h *monitorHub) run() {
	for {
		select {
		case c := <-h.register:
			h.connections[c] = true
			log.Println("Queuing initial status")
			c.send <- initialStatus()
		case c := <-h.unregister:
			log.Println("Unregistering connection")
			delete(h.connections, c)
		case m := <-h.broadcast:
			for c := range h.connections {
				if len(c.send)+5 > cap(c.send) {
					log.Println("WS connection too close to cap")
					c.send <- `{"error": "too slow"}`
					close(c.send)
					go c.ws.Close()
					h.unregister <- c
					continue
				}
				select {
				case c.send <- m:
				default:
					close(c.send)
					delete(h.connections, c)
					log.Println("Closing channel when sending")
					go c.ws.Close()
				}
			}
		}
	}
}

func (c *wsConnection) reader() {
	for {
		var message string
		err := websocket.Message.Receive(c.ws, &message)
		if err != nil {
			if err == io.EOF {
				log.Println("WS connection closed")
			} else {
				log.Println("WS read error:", err)
			}
			break
		}
		log.Println("WS message", message)
		// TODO(ask) take configuration options etc
		//h.broadcast <- message
	}
	c.ws.Close()
}

func (c *wsConnection) writer() {
	for message := range c.send {
		err := websocket.Message.Send(c.ws, message)
		if err != nil {
			log.Println("WS write error:", err)
			break
		}
	}
	c.ws.Close()
}

func wsHandler(ws *websocket.Conn) {
	log.Println("Starting new WS connection")
	c := &wsConnection{send: make(chan string, 180), ws: ws}
	hub.register <- c
	defer func() {
		log.Println("sending unregister message")
		hub.unregister <- c
	}()
	go c.writer()
	c.reader()
}

func initialStatus() string {
	status := new(statusStreamMsgStart)
	status.Version = VERSION
	status.Id = serverId
	status.Ip = serverIP
	if len(serverGroups) > 0 {
		status.Groups = serverGroups
	}
	hostname, err := os.Hostname()
	if err == nil {
		status.Hostname = hostname
	}

	status.Uptime = int(time.Since(timeStarted).Seconds())
	status.Started = int(timeStarted.Unix())

	message, err := json.Marshal(status)
	return string(message)
}

func logStatus() {
	log.Println(initialStatus())
	// Does not impact performance too much
	lastQueryCount := qCounter.Count()

	for {
		current := qCounter.Count()
		newQueries := current - lastQueryCount
		lastQueryCount = current

		log.Println("goroutines", runtime.NumGoroutine(), "queries", newQueries)

		time.Sleep(60 * time.Second)
	}
}

func monitor(zones Zones) {
	go logStatus()

	if len(*flaghttp) == 0 {
		return
	}
	go hub.run()
	go httpHandler(zones)

	lastQueryCount := qCounter.Count()

	status := new(statusStreamMsgUpdate)
	var lastQps1m float64

	for {
		current := qCounter.Count()
		newQueries := current - lastQueryCount
		lastQueryCount = current

		status.Uptime = int(time.Since(timeStarted).Seconds())
		status.QueryCount = qCounter.Count()
		status.Qps = newQueries

		// go-metrics only updates the rate every 5 seconds, so don't pretend otherwise
		newQps1m := qCounter.Rate1()
		if newQps1m != lastQps1m {
			status.Qps1m = newQps1m
			lastQps1m = newQps1m
		} else {
			status.Qps1m = 0
		}

		message, err := json.Marshal(status)

		if err == nil {
			hub.broadcast <- string(message)
		}
		time.Sleep(1 * time.Second)
	}
}

func MainServer(w http.ResponseWriter, req *http.Request) {
	if req.RequestURI != "/version" {
		http.NotFound(w, req)
		return
	}
	io.WriteString(w, `<html><head><title>GeoDNS `+
		VERSION+`</title><body>`+
		initialStatus()+
		`</body></html>`)
}

type rate struct {
	Name    string
	Count   int64
	Metrics ZoneMetrics
}
type Rates []*rate

func (s Rates) Len() int      { return len(s) }
func (s Rates) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type RatesByCount struct{ Rates }

func (s RatesByCount) Less(i, j int) bool {
	ic := s.Rates[i].Count
	jc := s.Rates[j].Count
	if ic == jc {
		return s.Rates[i].Name < s.Rates[j].Name
	}
	return ic > jc
}

type histogramData struct {
	Max    int64
	Min    int64
	Mean   float64
	Pct90  float64
	Pct99  float64
	Pct999 float64
	StdDev float64
}

func setupHistogramData(met *metrics.StandardHistogram, dat *histogramData) {
	dat.Max = met.Max()
	dat.Min = met.Min()
	dat.Mean = met.Mean()
	dat.StdDev = met.StdDev()
	percentiles := met.Percentiles([]float64{0.90, 0.99, 0.999})
	dat.Pct90 = percentiles[0]
	dat.Pct99 = percentiles[1]
	dat.Pct999 = percentiles[2]
}

func StatusServer(zones Zones) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, req *http.Request) {

		tmpl := template.New("status_html")
		tmpl, err := tmpl.Parse(string(status_html()))

		if err != nil {
			str := fmt.Sprintf("Could not parse template: %s", err)
			io.WriteString(w, str)
			return
		}

		rates := make(Rates, 0)

		for name, zone := range zones {
			count := zone.Metrics.Queries.Count()
			rates = append(rates, &rate{Name: name, Count: count, Metrics: zone.Metrics})
		}

		sort.Sort(RatesByCount{rates})

		type statusData struct {
			Version  string
			Zones    Rates
			Uptime   DayDuration
			Platform string
			Global   struct {
				Queries         *metrics.StandardMeter
				Histogram       histogramData
				HistogramRecent histogramData
			}
		}

		uptime := DayDuration{time.Since(timeStarted)}

		status := statusData{
			Version:  VERSION,
			Zones:    rates,
			Uptime:   uptime,
			Platform: runtime.GOARCH + "-" + runtime.GOOS,
		}

		status.Global.Queries = metrics.Get("queries").(*metrics.StandardMeter)

		setupHistogramData(metrics.Get("queries-histogram").(*metrics.StandardHistogram), &status.Global.Histogram)
		setupHistogramData(metrics.Get("queries-histogram-recent").(*metrics.StandardHistogram), &status.Global.HistogramRecent)

		err = tmpl.Execute(w, status)
		if err != nil {
			log.Println("Status template error", err)
		}
	}
}

func httpHandler(zones Zones) {
	http.Handle("/monitor", websocket.Handler(wsHandler))
	http.HandleFunc("/status", StatusServer(zones))
	http.HandleFunc("/", MainServer)

	log.Println("Starting HTTP interface on", *flaghttp)

	log.Fatal(http.ListenAndServe(*flaghttp, nil))
}
<|endoftext|>


<commit_before>package youtube

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/net/proxy"
)

//SetLogOutput :Set logger writer
func SetLogOutput(w io.Writer) {
	log.SetOutput(w)
}

//NewYoutube :Initialize youtube package object
func NewYoutube(debug bool) *Youtube {
	return &Youtube{DebugMode: debug, DownloadPercent: make(chan int64, 100)}
}

func NewYoutubeWithSocks5Proxy(debug bool, socks5Proxy string) *Youtube {
	return &Youtube{DebugMode: debug, DownloadPercent: make(chan int64, 100), Socks5Proxy: socks5Proxy}
}

type stream map[string]string

type Youtube struct {
	DebugMode         bool
	StreamList        []stream
	VideoID           string
	videoInfo         string
	DownloadPercent   chan int64
	Socks5Proxy       string
	contentLength     float64
	totalWrittenBytes float64
	downloadLevel     float64
}

//DecodeURL : Decode youtube URL to retrieval video information.
func (y *Youtube) DecodeURL(url string) error {
	err := y.findVideoID(url)
	if err != nil {
		return fmt.Errorf("findVideoID error=%s", err)
	}

	err = y.getVideoInfo()
	if err != nil {
		return fmt.Errorf("getVideoInfo error=%s", err)
	}

	err = y.parseVideoInfo()
	if err != nil {
		return fmt.Errorf("parse video info failed, err=%s", err)
	}

	return nil
}

//StartDownload : Starting download video to specific address.
func (y *Youtube) StartDownload(destFile string) error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		url := v["url"]
		y.log(fmt.Sprintln("Download url=", url))

		y.log(fmt.Sprintln("Download to file=", destFile))
		err = y.videoDLWorker(destFile, url)
		if err == nil {
			break
		}
	}
	return err
}

//StartDownloadWithQuality : Starting download video with specific quality.
func (y *Youtube) StartDownloadWithQuality(destFile string, quality string) error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		if strings.Compare(v["quality"], quality) == 0 {
			url := v["url"]
			y.log(fmt.Sprintln("Download url=", url))
			y.log(fmt.Sprintln("Download to file=", destFile))
			err = y.videoDLWorker(destFile, url)
			if err == nil {
				break
			}
		}
	}

	if err != nil {
		return y.StartDownload(destFile)
	}
	return err
}

//StartDownloadFile : Starting download video on my download.
func (y *Youtube) StartDownloadFile() error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		url := v["url"]
		y.log(fmt.Sprintln("Download url=", url))

		usr, _ := user.Current()
		fileName := fmt.Sprintf("%s.mov", v["title"])
		destFile := filepath.Join(filepath.Join(usr.HomeDir, "Movies", "youtubedr"), fileName)
		y.log(fmt.Sprintln("Download to file=", destFile))

		err = y.videoDLWorker(destFile, url)
		if err == nil {
			break
		}
	}
	return err
}

func (y *Youtube) parseVideoInfo() error {
	answer, err := url.ParseQuery(y.videoInfo)
	if err != nil {
		return err
	}

	status, ok := answer["status"]
	if !ok {
		err = fmt.Errorf("no response status found in the server's answer")
		return err
	}
	if status[0] == "fail" {
		reason, ok := answer["reason"]
		if ok {
			err = fmt.Errorf("'fail' response status found in the server's answer, reason: '%s'", reason[0])
		} else {
			err = errors.New(fmt.Sprint("'fail' response status found in the server's answer, no reason given"))
		}
		return err
	}
	if status[0] != "ok" {
		err = fmt.Errorf("non-success response status found in the server's answer (status: '%s')", status)
		return err
	}
	// Parse answer
	// for k, v := range answer {
	// 	fmt.Println(" Key=", k, " Val=", v)
	// }

	// read the streams map
	streamMap, ok := answer["player_response"]
	if !ok {
		err = errors.New(fmt.Sprint("no stream map found in the server's answer."))
		return err
	}

	// Get video title and author.
	title, author := getVideoTitleAuthor(answer)

	var prData PlayerResponseData
	if err := json.Unmarshal([]byte(streamMap[0]), &prData); err != nil {
		fmt.Println(err)
		panic("Player response json data has changed.")
	}

	// Get video download link
	if prData.PlayabilityStatus.Status == "UNPLAYABLE" {
		//Cannot playback on embedded video screen, could not download.
		return errors.New(fmt.Sprint("Cannot playback and download, reason:", prData.PlayabilityStatus.Reason))
	}

	fmt.Println(">>>>>", len(prData.StreamingData.Formats), len(prData.StreamingData.AdaptiveFormats))
	fmt.Println("<<<<<", status[0])
	var streams []stream
	for streamPos, streamRaw := range prData.StreamingData.Formats {
		if streamRaw.MimeType == "" {
			y.log(fmt.Sprintf("An error occured while decoding one of the video's stream's information: stream %d.\n", streamPos))
			continue
		}
		streamUrl := streamRaw.URL
		if streamUrl == "" {
			cipher := streamRaw.Cipher
			decipheredUrl, err := y.decipher(cipher)
			if err != nil {
				return err
			}
			streamUrl = decipheredUrl
		}

		streams = append(streams, stream{
			"quality": streamRaw.Quality,
			"type":    streamRaw.MimeType,
			"url":     streamUrl,

			"title":  title,
			"author": author,
		})
		y.log(fmt.Sprintf("Title: %s Author: %s Stream found: quality '%s', format '%s'", title, author, streamRaw.Quality, streamRaw.MimeType))
	}

	y.StreamList = streams
	if len(y.StreamList) == 0 {
		return errors.New(fmt.Sprint("no stream list found in the server's answer"))
	}
	return nil
}

func (y *Youtube) getHTTPClient() (*http.Client, error) {
	// setup a http client
	httpTransport := &http.Transport{}
	httpClient := &http.Client{Transport: httpTransport}

	if len(y.Socks5Proxy) == 0 {
		y.log("Using http without proxy.")
		return httpClient, nil
	}

	dialer, err := proxy.SOCKS5("tcp", y.Socks5Proxy, nil, proxy.Direct)
	if err != nil {
		fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err)
		return nil, err
	}
	// set our socks5 as the dialer
	httpTransport.Dial = dialer.Dial

	y.log(fmt.Sprintf("Using http with proxy %s.", y.Socks5Proxy))

	return httpClient, nil
}

func (y *Youtube) getVideoInfo() error {
	eurl := "https://youtube.googleapis.com/v/" + y.VideoID
	url := "https://youtube.com/get_video_info?video_id=" + y.VideoID + "&eurl=" + eurl
	y.log(fmt.Sprintf("url: %s", url))

	httpClient, err := y.getHTTPClient()
	if err != nil {
		return err
	}

	resp, err := httpClient.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	y.videoInfo = string(body)
	return nil
}

func (y *Youtube) findVideoID(url string) error {
	videoID := url
	if strings.Contains(videoID, "youtu") || strings.ContainsAny(videoID, "\"?&/<%=") {
		reList := []*regexp.Regexp{
			regexp.MustCompile(`(?:v|embed|watch\?v)(?:=|/)([^"&?/=%]{11})`),
			regexp.MustCompile(`(?:=|/)([^"&?/=%]{11})`),
			regexp.MustCompile(`([^"&?/=%]{11})`),
		}
		for _, re := range reList {
			if isMatch := re.MatchString(videoID); isMatch {
				subs := re.FindStringSubmatch(videoID)
				videoID = subs[1]
			}
		}
	}
	y.log(fmt.Sprintf("Found video id: '%s'", videoID))
	y.VideoID = videoID
	if strings.ContainsAny(videoID, "?&/<%=") {
		return errors.New("invalid characters in video id")
	}
	if len(videoID) < 10 {
		return errors.New("the video id must be at least 10 characters long")
	}
	return nil
}

func (y *Youtube) Write(p []byte) (n int, err error) {
	n = len(p)
	y.totalWrittenBytes = y.totalWrittenBytes + float64(n)
	currentPercent := ((y.totalWrittenBytes / y.contentLength) * 100)
	if (y.downloadLevel <= currentPercent) && (y.downloadLevel < 100) {
		y.downloadLevel++
		y.DownloadPercent <- int64(y.downloadLevel)
	}
	return
}
func (y *Youtube) videoDLWorker(destFile string, target string) error {

	httpClient, err := y.getHTTPClient()
	if err != nil {
		return err
	}

	resp, err := httpClient.Get(target)
	if err != nil {
		y.log(fmt.Sprintf("Http.Get\nerror: %s\ntarget: %s\n", err, target))
		return err
	}
	defer resp.Body.Close()
	y.contentLength = float64(resp.ContentLength)

	if resp.StatusCode != 200 {
		y.log(fmt.Sprintf("reading answer: non 200[code=%v] status code received: '%v'", resp.StatusCode, err))
		return errors.New("non 200 status code received")
	}
	err = os.MkdirAll(filepath.Dir(destFile), 0755)
	if err != nil {
		return err
	}
	out, err := os.Create(destFile)
	if err != nil {
		return err
	}
	mw := io.MultiWriter(out, y)
	_, err = io.Copy(mw, resp.Body)
	if err != nil {
		y.log(fmt.Sprintln("download video err=", err))
		return err
	}
	return nil
}

func (y *Youtube) log(logText string) {
	if y.DebugMode {
		log.Println(logText)
	}
}

func getVideoTitleAuthor(in url.Values) (string, string) {
	playResponse, ok := in["player_response"]
	if !ok {
		return "", ""
	}
	personMap := make(map[string]interface{})

	if err := json.Unmarshal([]byte(playResponse[0]), &personMap); err != nil {
		panic(err)
	}

	s := personMap["videoDetails"]
	myMap := s.(map[string]interface{})
	// fmt.Println("-->", myMap["title"], "oooo:", myMap["author"])
	if title, ok := myMap["title"]; ok {
		if author, ok := myMap["author"]; ok {
			return title.(string), author.(string)
		}
	}

	return "", ""
}
<commit_msg>remove comment code<commit_after>package youtube

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/net/proxy"
)

//SetLogOutput :Set logger writer
func SetLogOutput(w io.Writer) {
	log.SetOutput(w)
}

//NewYoutube :Initialize youtube package object
func NewYoutube(debug bool) *Youtube {
	return &Youtube{DebugMode: debug, DownloadPercent: make(chan int64, 100)}
}

func NewYoutubeWithSocks5Proxy(debug bool, socks5Proxy string) *Youtube {
	return &Youtube{DebugMode: debug, DownloadPercent: make(chan int64, 100), Socks5Proxy: socks5Proxy}
}

type stream map[string]string

type Youtube struct {
	DebugMode         bool
	StreamList        []stream
	VideoID           string
	videoInfo         string
	DownloadPercent   chan int64
	Socks5Proxy       string
	contentLength     float64
	totalWrittenBytes float64
	downloadLevel     float64
}

//DecodeURL : Decode youtube URL to retrieval video information.
func (y *Youtube) DecodeURL(url string) error {
	err := y.findVideoID(url)
	if err != nil {
		return fmt.Errorf("findVideoID error=%s", err)
	}

	err = y.getVideoInfo()
	if err != nil {
		return fmt.Errorf("getVideoInfo error=%s", err)
	}

	err = y.parseVideoInfo()
	if err != nil {
		return fmt.Errorf("parse video info failed, err=%s", err)
	}

	return nil
}

//StartDownload : Starting download video to specific address.
func (y *Youtube) StartDownload(destFile string) error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		url := v["url"]
		y.log(fmt.Sprintln("Download url=", url))

		y.log(fmt.Sprintln("Download to file=", destFile))
		err = y.videoDLWorker(destFile, url)
		if err == nil {
			break
		}
	}
	return err
}

//StartDownloadWithQuality : Starting download video with specific quality.
func (y *Youtube) StartDownloadWithQuality(destFile string, quality string) error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		if strings.Compare(v["quality"], quality) == 0 {
			url := v["url"]
			y.log(fmt.Sprintln("Download url=", url))
			y.log(fmt.Sprintln("Download to file=", destFile))
			err = y.videoDLWorker(destFile, url)
			if err == nil {
				break
			}
		}
	}

	if err != nil {
		return y.StartDownload(destFile)
	}
	return err
}

//StartDownloadFile : Starting download video on my download.
func (y *Youtube) StartDownloadFile() error {
	//download highest resolution on [0]
	err := errors.New("Empty stream list")
	for _, v := range y.StreamList {
		url := v["url"]
		y.log(fmt.Sprintln("Download url=", url))

		usr, _ := user.Current()
		fileName := fmt.Sprintf("%s.mov", v["title"])
		destFile := filepath.Join(filepath.Join(usr.HomeDir, "Movies", "youtubedr"), fileName)
		y.log(fmt.Sprintln("Download to file=", destFile))

		err = y.videoDLWorker(destFile, url)
		if err == nil {
			break
		}
	}
	return err
}

func (y *Youtube) parseVideoInfo() error {
	answer, err := url.ParseQuery(y.videoInfo)
	if err != nil {
		return err
	}

	status, ok := answer["status"]
	if !ok {
		err = fmt.Errorf("no response status found in the server's answer")
		return err
	}
	if status[0] == "fail" {
		reason, ok := answer["reason"]
		if ok {
			err = fmt.Errorf("'fail' response status found in the server's answer, reason: '%s'", reason[0])
		} else {
			err = errors.New(fmt.Sprint("'fail' response status found in the server's answer, no reason given"))
		}
		return err
	}
	if status[0] != "ok" {
		err = fmt.Errorf("non-success response status found in the server's answer (status: '%s')", status)
		return err
	}

	// read the streams map
	streamMap, ok := answer["player_response"]
	if !ok {
		err = errors.New(fmt.Sprint("no stream map found in the server's answer."))
		return err
	}

	// Get video title and author.
	title, author := getVideoTitleAuthor(answer)

	var prData PlayerResponseData
	if err := json.Unmarshal([]byte(streamMap[0]), &prData); err != nil {
		fmt.Println(err)
		panic("Player response json data has changed.")
	}

	// Get video download link
	if prData.PlayabilityStatus.Status == "UNPLAYABLE" {
		//Cannot playback on embedded video screen, could not download.
		return errors.New(fmt.Sprint("Cannot playback and download, reason:", prData.PlayabilityStatus.Reason))
	}

	fmt.Println(">>>>>", len(prData.StreamingData.Formats), len(prData.StreamingData.AdaptiveFormats))
	fmt.Println("<<<<<", status[0])
	var streams []stream
	for streamPos, streamRaw := range prData.StreamingData.Formats {
		if streamRaw.MimeType == "" {
			y.log(fmt.Sprintf("An error occured while decoding one of the video's stream's information: stream %d.\n", streamPos))
			continue
		}
		streamUrl := streamRaw.URL
		if streamUrl == "" {
			cipher := streamRaw.Cipher
			decipheredUrl, err := y.decipher(cipher)
			if err != nil {
				return err
			}
			streamUrl = decipheredUrl
		}

		streams = append(streams, stream{
			"quality": streamRaw.Quality,
			"type":    streamRaw.MimeType,
			"url":     streamUrl,

			"title":  title,
			"author": author,
		})
		y.log(fmt.Sprintf("Title: %s Author: %s Stream found: quality '%s', format '%s'", title, author, streamRaw.Quality, streamRaw.MimeType))
	}

	y.StreamList = streams
	if len(y.StreamList) == 0 {
		return errors.New(fmt.Sprint("no stream list found in the server's answer"))
	}
	return nil
}

func (y *Youtube) getHTTPClient() (*http.Client, error) {
	// setup a http client
	httpTransport := &http.Transport{}
	httpClient := &http.Client{Transport: httpTransport}

	if len(y.Socks5Proxy) == 0 {
		y.log("Using http without proxy.")
		return httpClient, nil
	}

	dialer, err := proxy.SOCKS5("tcp", y.Socks5Proxy, nil, proxy.Direct)
	if err != nil {
		fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err)
		return nil, err
	}
	// set our socks5 as the dialer
	httpTransport.Dial = dialer.Dial

	y.log(fmt.Sprintf("Using http with proxy %s.", y.Socks5Proxy))

	return httpClient, nil
}

func (y *Youtube) getVideoInfo() error {
	eurl := "https://youtube.googleapis.com/v/" + y.VideoID
	url := "https://youtube.com/get_video_info?video_id=" + y.VideoID + "&eurl=" + eurl
	y.log(fmt.Sprintf("url: %s", url))

	httpClient, err := y.getHTTPClient()
	if err != nil {
		return err
	}

	resp, err := httpClient.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	y.videoInfo = string(body)
	return nil
}

func (y *Youtube) findVideoID(url string) error {
	videoID := url
	if strings.Contains(videoID, "youtu") || strings.ContainsAny(videoID, "\"?&/<%=") {
		reList := []*regexp.Regexp{
			regexp.MustCompile(`(?:v|embed|watch\?v)(?:=|/)([^"&?/=%]{11})`),
			regexp.MustCompile(`(?:=|/)([^"&?/=%]{11})`),
			regexp.MustCompile(`([^"&?/=%]{11})`),
		}
		for _, re := range reList {
			if isMatch := re.MatchString(videoID); isMatch {
				subs := re.FindStringSubmatch(videoID)
				videoID = subs[1]
			}
		}
	}
	y.log(fmt.Sprintf("Found video id: '%s'", videoID))
	y.VideoID = videoID
	if strings.ContainsAny(videoID, "?&/<%=") {
		return errors.New("invalid characters in video id")
	}
	if len(videoID) < 10 {
		return errors.New("the video id must be at least 10 characters long")
	}
	return nil
}

func (y *Youtube) Write(p []byte) (n int, err error) {
	n = len(p)
	y.totalWrittenBytes = y.totalWrittenBytes + float64(n)
	currentPercent := ((y.totalWrittenBytes / y.contentLength) * 100)
	if (y.downloadLevel <= currentPercent) && (y.downloadLevel < 100) {
		y.downloadLevel++
		y.DownloadPercent <- int64(y.downloadLevel)
	}
	return
}
func (y *Youtube) videoDLWorker(destFile string, target string) error {

	httpClient, err := y.getHTTPClient()
	if err != nil {
		return err
	}

	resp, err := httpClient.Get(target)
	if err != nil {
		y.log(fmt.Sprintf("Http.Get\nerror: %s\ntarget: %s\n", err, target))
		return err
	}
	defer resp.Body.Close()
	y.contentLength = float64(resp.ContentLength)

	if resp.StatusCode != 200 {
		y.log(fmt.Sprintf("reading answer: non 200[code=%v] status code received: '%v'", resp.StatusCode, err))
		return errors.New("non 200 status code received")
	}
	err = os.MkdirAll(filepath.Dir(destFile), 0755)
	if err != nil {
		return err
	}
	out, err := os.Create(destFile)
	if err != nil {
		return err
	}
	mw := io.MultiWriter(out, y)
	_, err = io.Copy(mw, resp.Body)
	if err != nil {
		y.log(fmt.Sprintln("download video err=", err))
		return err
	}
	return nil
}

func (y *Youtube) log(logText string) {
	if y.DebugMode {
		log.Println(logText)
	}
}

func getVideoTitleAuthor(in url.Values) (string, string) {
	playResponse, ok := in["player_response"]
	if !ok {
		return "", ""
	}
	personMap := make(map[string]interface{})

	if err := json.Unmarshal([]byte(playResponse[0]), &personMap); err != nil {
		panic(err)
	}

	s := personMap["videoDetails"]
	myMap := s.(map[string]interface{})
	// fmt.Println("-->", myMap["title"], "oooo:", myMap["author"])
	if title, ok := myMap["title"]; ok {
		if author, ok := myMap["author"]; ok {
			return title.(string), author.(string)
		}
	}

	return "", ""
}
<|endoftext|>


<commit_before>package murmur3

import "hash"

type Hash128 interface {
	hash.Hash
	Sum128() (uint64, uint64)
}

const (
	c1_32_128 = uint32(0x239b961b)
	c2_32_128 = uint32(0xab0e9789)
	c3_32_128 = uint32(0x38b34ae5)
	c4_32_128 = uint32(0xa1e38b93)

	c1_64_128 = uint64(0x87c37b91114253d5)
	c2_64_128 = uint64(0x4cf5ad432745937f)
)

type sum32_128 struct {
	h1     uint32
	h2     uint32
	h3     uint32
	h4     uint32
	k1     uint32
	k2     uint32
	k3     uint32
	k4     uint32
	length uint32
	offset uint8
}

type sum64_128 struct {
	h1     uint64
	h2     uint64
	k1     uint64
	k2     uint64
	length uint64
	offset uint8
}

func New32_128() Hash128 { return &sum32_128{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }
func New64_128() Hash128 { return &sum64_128{0, 0, 0, 0, 0, 0} }

func (s *sum32_128) Reset() {
	s.h1, s.h2, s.h3, s.h4 = 0, 0, 0, 0
	s.k1, s.k2, s.k3, s.k4 = 0, 0, 0, 0
	s.length, s.offset = 0, 0
}

func (s *sum64_128) Reset() {
	s.h1, s.h2, s.k1, s.k2, s.length, s.offset = 0, 0, 0, 0, 0, 0
}

func (s *sum32_128) Sum128() (uint64, uint64) {
	var h1, h2, h3, h4 = s.h1, s.h2, s.h3, s.h4

	//tail
	switch {
	case s.offset > 12:
		s.k4 *= c4_32_128
		s.k4 = (s.k4 << 18) | (s.k4 >> (32 - 18))
		s.k4 *= c1_32_128
		h4 ^= s.k4
		fallthrough

	case s.offset > 8:
		s.k3 *= c3_32_128
		s.k3 = (s.k3 << 17) | (s.k3 >> (32 - 17))
		s.k3 *= c4_32_128
		h3 ^= s.k3
		fallthrough

	case s.offset > 4:
		s.k2 *= c2_32_128
		s.k2 = (s.k2 << 16) | (s.k3 >> (32 - 16))
		s.k2 *= c3_32_128
		h2 ^= s.k2
		fallthrough

	case s.offset > 0:
		s.k1 *= c1_32_128
		s.k1 = (s.k1 << 15) | (s.k1 >> (32 - 15))
		s.k1 *= c2_32_128
		h1 ^= s.k1
	}

	//finalization
	h1 ^= s.length
	h2 ^= s.length
	h3 ^= s.length
	h4 ^= s.length

	h1 += h2
	h1 += h3
	h1 += h4

	h2 += h1
	h3 += h1
	h4 += h1

	h1 ^= h1 >> 16
	h1 *= 0x85ebca6b
	h1 ^= h1 >> 13
	h1 *= 0xc2b2ae35
	h1 ^= h1 >> 16

	h2 ^= h2 >> 16
	h2 *= 0x85ebca6b
	h2 ^= h2 >> 13
	h2 *= 0xc2b2ae35
	h2 ^= h2 >> 16

	h3 ^= h3 >> 16
	h3 *= 0x85ebca6b
	h3 ^= h3 >> 13
	h3 *= 0xc2b2ae35
	h3 ^= h3 >> 16

	h4 ^= h4 >> 16
	h4 *= 0x85ebca6b
	h4 ^= h4 >> 13
	h4 *= 0xc2b2ae35
	h4 ^= h4 >> 16

	h1 += h2
	h1 += h3
	h1 += h4

	h2 += h1
	h3 += h1
	h4 += h1

	return uint64((uint64(h1) << 32) | uint64(h2)),
		uint64((uint64(h3) << 32) | uint64(h4))
}

func (s *sum64_128) Sum128() (uint64, uint64) {
	var h1, h2 uint64 = s.h1, s.h2

	//tail
	switch {
	case s.offset > 8:
		s.k2 *= c2_64_128
		s.k2 = (s.k2 << 33) | (s.k2 >> (64 - 33))
		s.k2 *= c1_64_128
		h2 ^= s.k2
		fallthrough

	case s.offset > 0:
		s.k1 *= c1_64_128
		s.k1 = (s.k1 << 31) | (s.k1 >> (64 - 31))
		s.k1 *= c2_64_128
		h1 ^= s.k1
	}

	//finalization
	h1 ^= s.length
	h2 ^= s.length

	h1 += h2
	h2 += h1

	h1 ^= h1 >> 33
	h1 *= 0xff51afd7ed558ccd
	h1 ^= h1 >> 33
	h1 *= 0xc4ceb9fe1a85ec53
	h1 ^= h1 >> 33

	h2 ^= h2 >> 33
	h2 *= 0xff51afd7ed558ccd
	h2 ^= h2 >> 33
	h2 *= 0xc4ceb9fe1a85ec53
	h2 ^= h2 >> 33

	h1 += h2
	h2 += h1

	return h1, h2
}

func (s *sum32_128) Sum(in []byte) []byte {
	h1, h2 := s.Sum128()
	return append(in, byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), byte(h2>>56),
		byte(h2>>48), byte(h2>>32), byte(h2>>24), byte(h2>>16),
		byte(h2>>8), byte(h2))
}

func (s *sum64_128) Sum(in []byte) []byte {
	h1, h2 := s.Sum128()
	return append(in, byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), byte(h2>>56),
		byte(h2>>48), byte(h2>>32), byte(h2>>24), byte(h2>>16),
		byte(h2>>8), byte(h2))
}

func (s *sum32_128) Write(data []byte) (int, error) {
	length := len(data)
	if length == 0 {
		return 0, nil
	}
	s.length += uint32(length)

	for _, c := range data {

		// TODO: Might want to check this for endianness for consistency
		// across systems.
		if s.offset < 4 {
			s.k1 |= uint32(uint32(c) << uint32(s.offset*8))
		} else if s.offset >= 4 && s.offset < 8 {
			s.k2 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		} else if s.offset >= 8 && s.offset < 12 {
			s.k3 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		} else if s.offset >= 12 && s.offset < 16 {
			s.k4 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		} else {
			//something wrong
		}
		s.offset++

		if s.offset == 16 {
			s.k1 *= c1_32_128
			s.k1 = (s.k1 << 15) | (s.k1 >> (32 - 15))
			s.k1 *= c2_32_128
			s.h1 ^= s.k1

			s.h1 = (s.h1 << 19) | (s.h1 >> (32 - 19))
			s.h1 += s.h2
			s.h1 = s.h1*5 + 0x561ccd1b

			s.k2 *= c2_32_128
			s.k2 = (s.k2 << 16) | (s.k2 >> (32 - 16))
			s.k2 *= c3_32_128
			s.h2 ^= s.k2

			s.h2 = (s.h2 << 17) | (s.h2 >> (32 - 17))
			s.h2 += s.h3
			s.h2 = s.h2*5 + 0x0bcaa747

			s.k3 *= c3_32_128
			s.k3 = (s.k3 << 17) | (s.k3 >> (32 - 17))
			s.k3 *= c4_32_128
			s.h3 ^= s.k3

			s.h3 = (s.h3 << 15) | (s.h3 >> (32 - 15))
			s.h3 += s.h4
			s.h3 = s.h3*5 + 0x96cd1c35

			s.k4 *= c4_32_128
			s.k4 = (s.k4 << 18) | (s.k4 >> (32 - 18))
			s.k4 *= c1_32_128
			s.h4 ^= s.k4

			s.h4 = (s.h4 << 13) | (s.h4 >> (32 - 13))
			s.h4 += s.h1
			s.h4 = s.h4*5 + 0x32ac3b17

			s.offset = 0
		}
	}
	return length, nil
}

func (s *sum64_128) Write(data []byte) (int, error) {
	length := len(data)
	if length == 0 {
		return 0, nil
	}
	s.length += uint64(length)

	for _, c := range data {

		// TODO: Might want to check this for endianness for consistency
		// across systems.
		if s.offset < 8 {
			s.k1 |= uint64(uint64(c) << uint64(s.offset*8))
			s.offset++
		} else if s.offset >= 8 && s.offset < 16 {
			s.k2 |= uint64(uint64(c) << uint64((s.offset%8)*8))
			s.offset++
		} else {
			//something wrong
		}

		if s.offset == 16 {
			s.k1 *= c1_64_128
			s.k1 = (s.k1 << 31) | (s.k1 >> (64 - 31))
			s.k1 *= c2_64_128
			s.h1 ^= s.k1

			s.h1 = (s.h1 << 27) | (s.h1 >> (64 - 27))
			s.h1 += s.h2
			s.h1 = s.h1*5 + 0x52dce729

			s.k2 *= c2_64_128
			s.k2 = (s.k2 << 33) | (s.k2 >> (64 - 33))
			s.k2 *= c1_64_128
			s.h2 ^= s.k2

			s.h2 = (s.h2 << 31) | (s.h2 >> (64 - 32))
			s.h2 += s.h1
			s.h2 = s.h2*5 + 0x38495ab5
			s.k1 = 0
			s.k2 = 0

			s.offset = 0
		}
	}
	return length, nil
}

func (s *sum32_128) BlockSize() int { return 16 }
func (s *sum64_128) BlockSize() int { return 16 }

func (s *sum32_128) Size() int { return 16 }
func (s *sum64_128) Size() int { return 16 }
<commit_msg>Some syntactic changes<commit_after>package murmur3

import "hash"

type Hash128 interface {
	hash.Hash
	Sum128() (uint64, uint64)
}

const (
	c1_32_128 = uint32(0x239b961b)
	c2_32_128 = uint32(0xab0e9789)
	c3_32_128 = uint32(0x38b34ae5)
	c4_32_128 = uint32(0xa1e38b93)

	c1_64_128 = uint64(0x87c37b91114253d5)
	c2_64_128 = uint64(0x4cf5ad432745937f)
)

type sum32_128 struct {
	h1     uint32
	h2     uint32
	h3     uint32
	h4     uint32
	k1     uint32
	k2     uint32
	k3     uint32
	k4     uint32
	length uint32
	offset uint8
}

type sum64_128 struct {
	h1     uint64
	h2     uint64
	k1     uint64
	k2     uint64
	length uint64
	offset uint8
}

func New32_128() Hash128 { return &sum32_128{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }
func New64_128() Hash128 { return &sum64_128{0, 0, 0, 0, 0, 0} }

func (s *sum32_128) Reset() {
	s.h1, s.h2, s.h3, s.h4 = 0, 0, 0, 0
	s.k1, s.k2, s.k3, s.k4 = 0, 0, 0, 0
	s.length, s.offset = 0, 0
}

func (s *sum64_128) Reset() {
	s.h1, s.h2, s.k1, s.k2, s.length, s.offset = 0, 0, 0, 0, 0, 0
}

func (s *sum32_128) Sum128() (uint64, uint64) {
	var h1, h2, h3, h4 = s.h1, s.h2, s.h3, s.h4

	//tail
	switch {
	case s.offset > 12:
		s.k4 *= c4_32_128
		s.k4 = (s.k4 << 18) | (s.k4 >> (32 - 18))
		s.k4 *= c1_32_128
		h4 ^= s.k4
		fallthrough

	case s.offset > 8:
		s.k3 *= c3_32_128
		s.k3 = (s.k3 << 17) | (s.k3 >> (32 - 17))
		s.k3 *= c4_32_128
		h3 ^= s.k3
		fallthrough

	case s.offset > 4:
		s.k2 *= c2_32_128
		s.k2 = (s.k2 << 16) | (s.k3 >> (32 - 16))
		s.k2 *= c3_32_128
		h2 ^= s.k2
		fallthrough

	case s.offset > 0:
		s.k1 *= c1_32_128
		s.k1 = (s.k1 << 15) | (s.k1 >> (32 - 15))
		s.k1 *= c2_32_128
		h1 ^= s.k1
	}

	//finalization
	h1 ^= s.length
	h2 ^= s.length
	h3 ^= s.length
	h4 ^= s.length

	h1 += h2
	h1 += h3
	h1 += h4

	h2 += h1
	h3 += h1
	h4 += h1

	h1 ^= h1 >> 16
	h1 *= 0x85ebca6b
	h1 ^= h1 >> 13
	h1 *= 0xc2b2ae35
	h1 ^= h1 >> 16

	h2 ^= h2 >> 16
	h2 *= 0x85ebca6b
	h2 ^= h2 >> 13
	h2 *= 0xc2b2ae35
	h2 ^= h2 >> 16

	h3 ^= h3 >> 16
	h3 *= 0x85ebca6b
	h3 ^= h3 >> 13
	h3 *= 0xc2b2ae35
	h3 ^= h3 >> 16

	h4 ^= h4 >> 16
	h4 *= 0x85ebca6b
	h4 ^= h4 >> 13
	h4 *= 0xc2b2ae35
	h4 ^= h4 >> 16

	h1 += h2
	h1 += h3
	h1 += h4

	h2 += h1
	h3 += h1
	h4 += h1

	return uint64((uint64(h1) << 32) | uint64(h2)),
		uint64((uint64(h3) << 32) | uint64(h4))
}

func (s *sum64_128) Sum128() (uint64, uint64) {
	var h1, h2 uint64 = s.h1, s.h2

	//tail
	switch {
	case s.offset > 8:
		s.k2 *= c2_64_128
		s.k2 = (s.k2 << 33) | (s.k2 >> (64 - 33))
		s.k2 *= c1_64_128
		h2 ^= s.k2
		fallthrough

	case s.offset > 0:
		s.k1 *= c1_64_128
		s.k1 = (s.k1 << 31) | (s.k1 >> (64 - 31))
		s.k1 *= c2_64_128
		h1 ^= s.k1
	}

	//finalization
	h1 ^= s.length
	h2 ^= s.length

	h1 += h2
	h2 += h1

	h1 ^= h1 >> 33
	h1 *= 0xff51afd7ed558ccd
	h1 ^= h1 >> 33
	h1 *= 0xc4ceb9fe1a85ec53
	h1 ^= h1 >> 33

	h2 ^= h2 >> 33
	h2 *= 0xff51afd7ed558ccd
	h2 ^= h2 >> 33
	h2 *= 0xc4ceb9fe1a85ec53
	h2 ^= h2 >> 33

	h1 += h2
	h2 += h1

	return h1, h2
}

func (s *sum32_128) Sum(in []byte) []byte {
	h1, h2 := s.Sum128()
	return append(in, byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), byte(h2>>56),
		byte(h2>>48), byte(h2>>32), byte(h2>>24), byte(h2>>16),
		byte(h2>>8), byte(h2))
}

func (s *sum64_128) Sum(in []byte) []byte {
	h1, h2 := s.Sum128()
	return append(in, byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
		byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), byte(h2>>56),
		byte(h2>>48), byte(h2>>32), byte(h2>>24), byte(h2>>16),
		byte(h2>>8), byte(h2))
}

func (s *sum32_128) Write(data []byte) (int, error) {
	length := len(data)
	if length == 0 {
		return 0, nil
	}
	s.length += uint32(length)

	for _, c := range data {

		// TODO: Might want to check this for endianness for consistency
		// across systems.
		if s.offset < 4 {
			s.k1 |= uint32(uint32(c) << uint32(s.offset*8))
		} else if s.offset >= 4 && s.offset < 8 {
			s.k2 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		} else if s.offset >= 8 && s.offset < 12 {
			s.k3 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		} else if s.offset >= 12 && s.offset < 16 {
			s.k4 |= uint32(uint32(c) << uint32((s.offset%4)*8))
		}
		s.offset++

		if s.offset == 16 {
			s.k1 *= c1_32_128
			s.k1 = (s.k1 << 15) | (s.k1 >> (32 - 15))
			s.k1 *= c2_32_128
			s.h1 ^= s.k1

			s.h1 = (s.h1 << 19) | (s.h1 >> (32 - 19))
			s.h1 += s.h2
			s.h1 = s.h1*5 + 0x561ccd1b

			s.k2 *= c2_32_128
			s.k2 = (s.k2 << 16) | (s.k2 >> (32 - 16))
			s.k2 *= c3_32_128
			s.h2 ^= s.k2

			s.h2 = (s.h2 << 17) | (s.h2 >> (32 - 17))
			s.h2 += s.h3
			s.h2 = s.h2*5 + 0x0bcaa747

			s.k3 *= c3_32_128
			s.k3 = (s.k3 << 17) | (s.k3 >> (32 - 17))
			s.k3 *= c4_32_128
			s.h3 ^= s.k3

			s.h3 = (s.h3 << 15) | (s.h3 >> (32 - 15))
			s.h3 += s.h4
			s.h3 = s.h3*5 + 0x96cd1c35

			s.k4 *= c4_32_128
			s.k4 = (s.k4 << 18) | (s.k4 >> (32 - 18))
			s.k4 *= c1_32_128
			s.h4 ^= s.k4

			s.h4 = (s.h4 << 13) | (s.h4 >> (32 - 13))
			s.h4 += s.h1
			s.h4 = s.h4*5 + 0x32ac3b17

			s.offset = 0
		}
	}
	return length, nil
}

func (s *sum64_128) Write(data []byte) (int, error) {
	length := len(data)
	if length == 0 {
		return 0, nil
	}
	s.length += uint64(length)

	for _, c := range data {

		// TODO: Might want to check this for endianness for consistency
		// across systems.
		if s.offset < 8 {
			s.k1 |= uint64(uint64(c) << uint64(s.offset*8))
		} else if s.offset >= 8 && s.offset < 16 {
			s.k2 |= uint64(uint64(c) << uint64((s.offset%8)*8))
		}
		s.offset++

		if s.offset == 16 {
			s.k1 *= c1_64_128
			s.k1 = (s.k1 << 31) | (s.k1 >> (64 - 31))
			s.k1 *= c2_64_128
			s.h1 ^= s.k1

			s.h1 = (s.h1 << 27) | (s.h1 >> (64 - 27))
			s.h1 += s.h2
			s.h1 = s.h1*5 + 0x52dce729

			s.k2 *= c2_64_128
			s.k2 = (s.k2 << 33) | (s.k2 >> (64 - 33))
			s.k2 *= c1_64_128
			s.h2 ^= s.k2

			s.h2 = (s.h2 << 31) | (s.h2 >> (64 - 32))
			s.h2 += s.h1
			s.h2 = s.h2*5 + 0x38495ab5
			s.k1 = 0
			s.k2 = 0

			s.offset = 0
		}
	}
	return length, nil
}

func (s *sum32_128) BlockSize() int { return 16 }
func (s *sum64_128) BlockSize() int { return 16 }

func (s *sum32_128) Size() int { return 16 }
func (s *sum64_128) Size() int { return 16 }
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC. 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 unstructured provides a generic unstructured client to invoke DCL.
package unstructured

import (
	"context"
	"errors"
	"fmt"
	"sync"

	"github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl"
	"github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/iam"
)

var (
	registrations     []RegisteredResource
	registrationMutex sync.RWMutex

	// ErrNoSuchMethod is the error returned when calling Get, List, Apply, or Delete
	// on an API that doesn't support the requested method.
	ErrNoSuchMethod = errors.New("non-existent API method")
)

// ServiceTypeVersion describes a single DCL resource.
type ServiceTypeVersion struct {
	// Service to which this resource belongs, e.g., "compute".
	Service string
	// Type of the resource, e.g., "ComputeInstance"
	Type string
	// Version of the resource, e.g., "ga". There may be multiple versions of the
	// same Type and Service in a single DCL build.
	Version string
}

// Resource is the untyped representation of a typed DCL resource.
type Resource struct {
	// ResourceWithPolicy is included so that the Resource struct can interact with the
	// IAMClient (IAMClient methods expect a ResourceWithPolicy)
	iam.ResourceWithPolicy

	// Object is a JSON compatible map with string, float, int,
	// bool, []interface{}, or map[string]interface{} children.
	Object map[string]interface{}

	// STV indicates the type of this resource
	STV ServiceTypeVersion
}

// RegisteredResource is used by generated unstructured library code to
// make type-specific operations available in a type-agnostic manner.
type RegisteredResource interface {
	// STV indicates the type of this resource.
	STV() ServiceTypeVersion

	// Get provides an indirection for the type-specific Get call.
	Get(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error)

	// Apply provides an indirection for the type-specific Apply call.
	Apply(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (*Resource, error)

	// HasDiff provides an indirection for the type-specific HasDiff call.
	HasDiff(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (bool, error)

	// Delete provides an indirection for the type-specific Delete call.
	Delete(ctx context.Context, config *dcl.Config, r *Resource) error

	// GetPolicy provides an indirection for the type-specific GetPolicy call.
	GetPolicy(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error)

	// SetPolicy provides an indirection for the type-specific SetPolicy call.
	SetPolicy(ctx context.Context, config *dcl.Config, r *Resource, p *Resource) (*Resource, error)

	// ID returns a string uniquely identifying this resource.
	ID(r *Resource) (string, error)
}

// Equals compares two ServiceTypeVersion structures.
func (stv ServiceTypeVersion) Equals(o ServiceTypeVersion) bool {
	return stv.Service == o.Service && stv.Type == o.Type && stv.Version == o.Version
}

// String returns a loggable description of this ServiceTypeVersion.
func (stv ServiceTypeVersion) String() string {
	return fmt.Sprintf(`"%s.%s.%s"`, stv.Service, stv.Type, stv.Version)
}

// StateHint is a dcl.ApplyOption that acts as the unstructured analog to dcl.stateHint.
type stateHint struct {
	state *Resource
}

// Apply is a no-op to conform to the dcl.ApplyOption interface.
func (s stateHint) Apply(o *dcl.ApplyOpts) {}

// WithStateHint performs the same function as dcl.WithStateHint, but
// takes an unstructured resource.
func WithStateHint(r *Resource) dcl.ApplyOption {
	return stateHint{state: r}
}

// FetchStateHint returns either nil or a Resource representing the pre-apply state.
func FetchStateHint(c []dcl.ApplyOption) *Resource {
	for _, p := range c {
		if sh, ok := p.(stateHint); ok {
			return sh.state
		}
	}
	return nil
}

// Register adds the provided resource to the list of resources available
// via the generic Get/List/Apply/Delete functions.
func Register(rr RegisteredResource) {
	registrationMutex.Lock()
	defer registrationMutex.Unlock()
	registrations = append(registrations, rr)
}

func registration(r *Resource) RegisteredResource {
	registrationMutex.RLock()
	defer registrationMutex.RUnlock()
	for _, rr := range registrations {
		if rr.STV().Equals(r.STV) {
			return rr
		}
	}
	return nil
}

// Get returns the current version of a given resource (usually from the
// result of a previous Apply()).
func Get(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Get(ctx, config, r)
}

// Apply creates or updates the provided resource.
func Apply(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Apply(ctx, config, r, opts...)
}

// HasDiff returns whether the provided resource config matches the live resource, i.e.,
// a return value of true indicates that calling Apply() will cause a creation or update of
// the live resource. The `opts` parameter can optionally include a state hint.
func HasDiff(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (bool, error) {
	rr := registration(r)
	if rr == nil {
		return false, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.HasDiff(ctx, config, r, opts...)
}

// Delete deletes the provided resource.
func Delete(ctx context.Context, config *dcl.Config, r *Resource) error {
	rr := registration(r)
	if rr == nil {
		return fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Delete(ctx, config, r)
}

// GetPolicy gets the IAMPolicy for the provided resource.
func GetPolicy(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.GetPolicy(ctx, config, r)
}

// SetPolicy sets the IAMPolicy for the provided resource.
func SetPolicy(ctx context.Context, config *dcl.Config, r *Resource, p *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.SetPolicy(ctx, config, r, p)
}

// ID returns a unique ID for the provided resource.
func ID(r *Resource) (string, error) {
	rr := registration(r)
	if rr == nil {
		return "", fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.ID(r)
}
<commit_msg>Automated DCL import.<commit_after>// Copyright 2022 Google LLC. 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 unstructured provides a generic unstructured client to invoke DCL.
package unstructured

import (
	"context"
	"errors"
	"fmt"
	"sync"

	"github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl"
	"github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/iam"
)

var (
	registrations     []RegisteredResource
	registrationMutex sync.RWMutex

	// ErrNoSuchMethod is the error returned when calling Get, List, Apply, or Delete
	// on an API that doesn't support the requested method.
	ErrNoSuchMethod = errors.New("non-existent API method")
)

// ServiceTypeVersion describes a single DCL resource.
type ServiceTypeVersion struct {
	// Service to which this resource belongs, e.g., "compute".
	Service string
	// Type of the resource, e.g., "ComputeInstance"
	Type string
	// Version of the resource, e.g., "ga". There may be multiple versions of the
	// same Type and Service in a single DCL build.
	Version string
}

// Resource is the untyped representation of a typed DCL resource.
type Resource struct {
	// ResourceWithPolicy is included so that the Resource struct can interact with the
	// IAMClient (IAMClient methods expect a ResourceWithPolicy)
	iam.ResourceWithPolicy

	// Object is a JSON compatible map with string, float, int,
	// bool, []interface{}, or map[string]interface{} children.
	Object map[string]interface{}

	// STV indicates the type of this resource
	STV ServiceTypeVersion
}

// RegisteredResource is used by generated unstructured library code to
// make type-specific operations available in a type-agnostic manner.
type RegisteredResource interface {
	// STV indicates the type of this resource.
	STV() ServiceTypeVersion

	// Get provides an indirection for the type-specific Get call.
	Get(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error)

	// Apply provides an indirection for the type-specific Apply call.
	Apply(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (*Resource, error)

	// HasDiff provides an indirection for the type-specific HasDiff call.
	HasDiff(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (bool, error)

	// Delete provides an indirection for the type-specific Delete call.
	Delete(ctx context.Context, config *dcl.Config, r *Resource) error

	// GetPolicy provides an indirection for the type-specific GetPolicy call.
	GetPolicy(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error)

	// SetPolicy provides an indirection for the type-specific SetPolicy call.
	SetPolicy(ctx context.Context, config *dcl.Config, r *Resource, p *Resource) (*Resource, error)

	// GetPolicyMember provides an indirection for the type-specific GetPolicyMember call.
	GetPolicyMember(ctx context.Context, config *dcl.Config, r *Resource, role, member string) (*Resource, error)

	// SetPolicyMember provides an indirection for the type-specific SetPolicyMember call.
	SetPolicyMember(ctx context.Context, config *dcl.Config, r *Resource, m *Resource) (*Resource, error)

	// ID returns a string uniquely identifying this resource.
	ID(r *Resource) (string, error)
}

// Equals compares two ServiceTypeVersion structures.
func (stv ServiceTypeVersion) Equals(o ServiceTypeVersion) bool {
	return stv.Service == o.Service && stv.Type == o.Type && stv.Version == o.Version
}

// String returns a loggable description of this ServiceTypeVersion.
func (stv ServiceTypeVersion) String() string {
	return fmt.Sprintf(`"%s.%s.%s"`, stv.Service, stv.Type, stv.Version)
}

// StateHint is a dcl.ApplyOption that acts as the unstructured analog to dcl.stateHint.
type stateHint struct {
	state *Resource
}

// Apply is a no-op to conform to the dcl.ApplyOption interface.
func (s stateHint) Apply(o *dcl.ApplyOpts) {}

// WithStateHint performs the same function as dcl.WithStateHint, but
// takes an unstructured resource.
func WithStateHint(r *Resource) dcl.ApplyOption {
	return stateHint{state: r}
}

// FetchStateHint returns either nil or a Resource representing the pre-apply state.
func FetchStateHint(c []dcl.ApplyOption) *Resource {
	for _, p := range c {
		if sh, ok := p.(stateHint); ok {
			return sh.state
		}
	}
	return nil
}

// Register adds the provided resource to the list of resources available
// via the generic Get/List/Apply/Delete functions.
func Register(rr RegisteredResource) {
	registrationMutex.Lock()
	defer registrationMutex.Unlock()
	registrations = append(registrations, rr)
}

func registration(r *Resource) RegisteredResource {
	registrationMutex.RLock()
	defer registrationMutex.RUnlock()
	for _, rr := range registrations {
		if rr.STV().Equals(r.STV) {
			return rr
		}
	}
	return nil
}

// Get returns the current version of a given resource (usually from the
// result of a previous Apply()).
func Get(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Get(ctx, config, r)
}

// Apply creates or updates the provided resource.
func Apply(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Apply(ctx, config, r, opts...)
}

// HasDiff returns whether the provided resource config matches the live resource, i.e.,
// a return value of true indicates that calling Apply() will cause a creation or update of
// the live resource. The `opts` parameter can optionally include a state hint.
func HasDiff(ctx context.Context, config *dcl.Config, r *Resource, opts ...dcl.ApplyOption) (bool, error) {
	rr := registration(r)
	if rr == nil {
		return false, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.HasDiff(ctx, config, r, opts...)
}

// Delete deletes the provided resource.
func Delete(ctx context.Context, config *dcl.Config, r *Resource) error {
	rr := registration(r)
	if rr == nil {
		return fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.Delete(ctx, config, r)
}

// GetPolicy gets the IAMPolicy for the provided resource.
func GetPolicy(ctx context.Context, config *dcl.Config, r *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.GetPolicy(ctx, config, r)
}

// SetPolicy sets the IAMPolicy for the provided resource.
func SetPolicy(ctx context.Context, config *dcl.Config, r *Resource, p *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.SetPolicy(ctx, config, r, p)
}

// GetPolicyMember gets the IAMPolicyMember for the provided resource.
func GetPolicyMember(ctx context.Context, config *dcl.Config, r *Resource, role, member string) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.GetPolicyMember(ctx, config, r, role, member)
}

// SetPolicyMember sets the IAMPolicyMember for the provided resource.
func SetPolicyMember(ctx context.Context, config *dcl.Config, r *Resource, m *Resource) (*Resource, error) {
	rr := registration(r)
	if rr == nil {
		return nil, fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.SetPolicyMember(ctx, config, r, m)
}

// ID returns a unique ID for the provided resource.
func ID(r *Resource) (string, error) {
	rr := registration(r)
	if rr == nil {
		return "", fmt.Errorf("unknown resource type %s", r.STV.String())
	}
	return rr.ID(r)
}
<|endoftext|>


<commit_before>// Dinit is a mini init replacement useful for use inside Docker containers.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var (
	verbose              bool
	port, sleep          int
	namespace, subsystem string
)

func main() {
	flag.IntVar(&port, "port", envInt("DINIT_PORT", 0), "port to export metricss for prometheus (DINIT_PORT)")
	flag.IntVar(&sleep, "sleep", envInt("DINIT_SLEEP", 5), "how many seconds to sleep before force killing programs (DINIT_SLEEP)")
	flag.StringVar(&namespace, "namespace", envString("DINIT_NAMESPACE", ""), "namespace to use for prometheus (DINIT_NAMESPACE)")
	flag.StringVar(&subsystem, "subsystem", envString("DINIT_SUBSYSTEM", ""), "subsystem to use for prometheus (DINIT_SUBSYSTEM)")
	flag.BoolVar(&verbose, "verbose", envBool("DINIT_VERBOSE", false), "be more verbose (DINIT_VERBOSE)")

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: dinit [OPTION]... PROGRAM [PROGRAM]...")
		fmt.Fprintln(os.Stderr, "Start PROGRAMs by passing the enviroment and reap any zombies.\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if len(flag.Args()) == 0 {
		log.Fatal("dinit: need at least one program")
	}

	if port > 0 {
		metrics()
	}

	cmds := []*exec.Cmd{}
	done := make(chan bool)

	for _, arg := range flag.Args() {
		args := strings.Fields(arg) // Split on spaces and execute.
		cmd := exec.Command(args[0], args[1:]...)
		cmds = append(cmds, cmd)

		go func() {
			err := cmd.Start()
			if err != nil {
				log.Fatal(err)
			}
			if verbose {
				log.Printf("dinit: pid %d started: %v", cmd.Process.Pid, cmd.Args)
			}

			err = cmd.Wait()
			if err != nil && verbose {
				log.Printf("dinit: pid %d, finished with error: %s", cmd.Process.Pid, err)
			} else {
				log.Printf("dinit: pid %d, finished: %v", cmd.Process.Pid, cmd.Args)
			}
			done <- true
		}()
	}

	ints := make(chan os.Signal)
	chld := make(chan os.Signal)
	signal.Notify(ints, syscall.SIGINT, syscall.SIGTERM)
	signal.Notify(chld, syscall.SIGCHLD)

	i := 0
Wait:
	for {
		select {
		case <-chld:
			go reaper()
		case <-done:
			i++
			if len(cmds) == i {
				reaper()
				break Wait
			}
		case sig := <-ints:
			// There is a race here, because the process could have died, we don't care.
			for _, cmd := range cmds {
				if verbose {
					log.Printf("dinit: signal %d sent to pid %d", sig, cmd.Process.Pid)
				}
				cmd.Process.Signal(sig)
			}

			time.Sleep(time.Duration(sleep) * time.Second)

			kill := []*os.Process{}
			for _, cmd := range cmds {
				if p, err := os.FindProcess(cmd.Process.Pid); err != nil {
					kill = append(kill, p)
				}
			}
			for _, p := range kill {
				log.Printf("dinit: SIGKILL sent to pid %d", p.Pid)
				p.Signal(syscall.SIGKILL)
			}
		}
	}
}

func reaper() {
	for {
		var wstatus syscall.WaitStatus
		pid, err := syscall.Wait4(-1, &wstatus, 0, nil)
		if err != nil {
			return
		}
		if verbose {
			log.Printf("dinit: pid %d reaped", pid)
		}
		zombies.Inc()
	}
}

func envBool(k string, d bool) bool {
	x := os.Getenv(k)
	switch strings.ToLower(x) {
	case "true":
		return true
	case "false":
		return false
	}
	return d

}

func envInt(k string, d int) int {
	x := os.Getenv(k)
	if x != "" {
		if x1, e := strconv.Atoi(x); e != nil {
			return x1
		}
	}
	return d
}

func envString(k, d string) string {
	x := os.Getenv(k)
	if x != "" {
		return x
	}
	return d
}
<commit_msg>logging<commit_after>// Dinit is a mini init replacement useful for use inside Docker containers.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var (
	verbose              bool
	port, sleep          int
	namespace, subsystem string
)

func main() {
	flag.IntVar(&port, "port", envInt("DINIT_PORT", 0), "port to export metricss for prometheus (DINIT_PORT)")
	flag.IntVar(&sleep, "sleep", envInt("DINIT_SLEEP", 5), "how many seconds to sleep before force killing programs (DINIT_SLEEP)")
	flag.StringVar(&namespace, "namespace", envString("DINIT_NAMESPACE", ""), "namespace to use for prometheus (DINIT_NAMESPACE)")
	flag.StringVar(&subsystem, "subsystem", envString("DINIT_SUBSYSTEM", ""), "subsystem to use for prometheus (DINIT_SUBSYSTEM)")
	flag.BoolVar(&verbose, "verbose", envBool("DINIT_VERBOSE", false), "be more verbose (DINIT_VERBOSE)")

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: dinit [OPTION]... PROGRAM [PROGRAM]...")
		fmt.Fprintln(os.Stderr, "Start PROGRAMs by passing the enviroment and reap any zombies.\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if len(flag.Args()) == 0 {
		log.Fatal("dinit: need at least one program")
	}

	if port > 0 {
		metrics()
	}

	cmds := []*exec.Cmd{}
	done := make(chan bool)

	for _, arg := range flag.Args() {
		args := strings.Fields(arg) // Split on spaces and execute.
		cmd := exec.Command(args[0], args[1:]...)
		cmds = append(cmds, cmd)

		go func() {
			err := cmd.Start()
			if err != nil {
				log.Fatal(err)
			}
			logf("dinit: pid %d started: %v", cmd.Process.Pid, cmd.Args)

			err = cmd.Wait()
			if err != nil {
				logf("dinit: pid %d, finished with error: %s", cmd.Process.Pid, err)
			} else {
				logf("dinit: pid %d, finished: %v", cmd.Process.Pid, cmd.Args)
			}
			done <- true
		}()
	}

	ints := make(chan os.Signal)
	chld := make(chan os.Signal)
	signal.Notify(ints, syscall.SIGINT, syscall.SIGTERM)
	signal.Notify(chld, syscall.SIGCHLD)

	i := 0
Wait:
	for {
		select {
		case <-chld:
			go reaper()
		case <-done:
			i++
			if len(cmds) == i {
				reaper()
				break Wait
			}
		case sig := <-ints:
			// There is a race here, because the process could have died, we don't care.
			for _, cmd := range cmds {
				logf("dinit: signal %d sent to pid %d", sig, cmd.Process.Pid)
				cmd.Process.Signal(sig)
			}

			time.Sleep(time.Duration(sleep) * time.Second)

			kill := []*os.Process{}
			for _, cmd := range cmds {
				if p, err := os.FindProcess(cmd.Process.Pid); err != nil {
					kill = append(kill, p)
				}
			}
			for _, p := range kill {
				logf("dinit: SIGKILL sent to pid %d", p.Pid)
				p.Signal(syscall.SIGKILL)
			}
		}
	}
}

func reaper() {
	for {
		var wstatus syscall.WaitStatus
		pid, err := syscall.Wait4(-1, &wstatus, 0, nil)
		if err != nil {
			return
		}
		logf("dinit: pid %d reaped", pid)
		zombies.Inc()
	}
}

func logf(format string, v ...interface{}) {
	if !verbose {
		return
	}
	log.Printf("dinit: " + format, v...)
}

func envBool(k string, d bool) bool {
	x := os.Getenv(k)
	switch strings.ToLower(x) {
	case "true":
		return true
	case "false":
		return false
	}
	return d

}

func envInt(k string, d int) int {
	x := os.Getenv(k)
	if x != "" {
		if x1, e := strconv.Atoi(x); e != nil {
			return x1
		}
	}
	return d
}

func envString(k, d string) string {
	x := os.Getenv(k)
	if x != "" {
		return x
	}
	return d
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
)

type Process struct {
	Command string
	Env     Env
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Root    string

	cmd *exec.Cmd
}

func NewProcess(command string, env Env) (p *Process) {
	p = new(Process)
	p.Command = command
	p.Env = env
	p.Stdin = os.Stdin
	p.Stdout = os.Stdout
	p.Stderr = os.Stderr
	p.Root, _ = os.Getwd()
	return
}

func (p *Process) Start() {
	command := []string{"/bin/bash", "-c", fmt.Sprintf("source \"%s\" 2>/dev/null; %s", filepath.Join(p.Root, ".profile"), p.Command)}
	p.cmd = exec.Command(command[0], command[1:]...)
	p.cmd.Dir = p.Root
	p.cmd.Env = p.envAsArray()
	p.cmd.Stdin = p.Stdin
	p.cmd.Stdout = p.Stdout
	p.cmd.Stderr = p.Stderr
	p.cmd.SysProcAttr = &syscall.SysProcAttr{}
	p.cmd.SysProcAttr.Setsid = true
	p.cmd.Start()
}

func (p *Process) Wait() {
  p.cmd.Wait()
}

func (p *Process) Signal(signal syscall.Signal) {
	group, _ := os.FindProcess(-1 * p.cmd.Process.Pid)
	group.Signal(signal)
}

func (p *Process) envAsArray() (env []string) {
	for name, val := range os.Environ() {
		env = append(env, fmt.Sprintf("%s=%s", name, val));
	}
	for name, val := range p.Env {
		env = append(env, fmt.Sprintf("%s=%s", name, val));
	}
	return
}

<commit_msg>fix env<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
)

type Process struct {
	Command string
	Env     Env
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Root    string

	cmd *exec.Cmd
}

func NewProcess(command string, env Env) (p *Process) {
	p = new(Process)
	p.Command = command
	p.Env = env
	p.Stdin = os.Stdin
	p.Stdout = os.Stdout
	p.Stderr = os.Stderr
	p.Root, _ = os.Getwd()
	return
}

func (p *Process) Start() {
	command := []string{"/bin/bash", "-c", fmt.Sprintf("source \"%s\" 2>/dev/null; %s", filepath.Join(p.Root, ".profile"), p.Command)}
	p.cmd = exec.Command(command[0], command[1:]...)
	p.cmd.Dir = p.Root
	p.cmd.Env = p.envAsArray()
	p.cmd.Stdin = p.Stdin
	p.cmd.Stdout = p.Stdout
	p.cmd.Stderr = p.Stderr
	p.cmd.SysProcAttr = &syscall.SysProcAttr{}
	p.cmd.SysProcAttr.Setsid = true
	p.cmd.Start()
}

func (p *Process) Wait() {
  p.cmd.Wait()
}

func (p *Process) Signal(signal syscall.Signal) {
	group, _ := os.FindProcess(-1 * p.cmd.Process.Pid)
	group.Signal(signal)
}

func (p *Process) envAsArray() (env []string) {
	for _, pair := range os.Environ() {
		env = append(env, pair)
	}
	for name, val := range p.Env {
		env = append(env, fmt.Sprintf("%s=%s", name, val))
	}
	return
}

<|endoftext|>