<commit_before>/* stickypipe agent
Takes environment variables:
  // End points are our devices such as a network switch.
	SP_ENDPOINTS="192.168.30.1,c2960g,nexus5k-top"
	// Credentials are our logins to the endpoints.
	SP_ENDPOINT_CREDENTIALS="public"

then pipes the output up to sitckypipe in a JSON based string:
{ name: c2960g, interface-id: 1733017, interface-name: GigabitEthernet0/10, interface-in: 3866362551, interface-out: 345343003, timestamp: 1438023632  }

*/
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/joeshaw/envdecode"
	"github.com/vallard/gosnmp"
	"github.com/vallard/stickypipe-agent/nxapi"
)

var mutex sync.Mutex

func handleError(err error) {
	fmt.Println("error:", err)
}

/* Get NXAPI information
Arguments:
 server - Nexus Switch (10.93.234.2, sw001, or something reachable)
 creds - user/password pair that looks like admin:cisco (notice no :'s are supported in the password.)
 map - map we want to store this stuff.
*/

func getNXAPIData(server string, command string, outputName string, creds string, m map[string]map[string]map[string]string) {
	// argument for string looks like: admin:cisco where admin is the user and cisco is the password.
	up := strings.Split(creds, ":")
	// make sure that we parsed the username and password.
	if len(up) < 2 {
		log.Fatal("Credentials must be of form user:password")
		return
	}
	// The command we run to get the port interface statistics.
	/*
		nxcmd := NewNXAPIPost(command)
		fmt.Println(nxcmd)
		jsonStr, err := json.Marshal(nxcmd)
	*/
	var jsonStr = []byte(`{
					"ins_api": {
							"version":       "1.0",
							"type":          "cli_show",
							"chunk":         "0",
							"sid":           "1",
							"input":         "` + command + `",
							"output_format": "json",
						}
					}
						`)
	// Start formatting our HTTP POST request.
	req, err := http.NewRequest("POST", "http://"+server+"/ins", bytes.NewBuffer(jsonStr))
	if err != nil {
		log.Println("HTTP Post: ", err)
	}
	// The header has to be set to application/json
	req.Header.Set("content-type", "application/json")
	// add the username and password to the header.
	req.SetBasicAuth(up[0], up[1])

	// create a new http client to execute the request.
	client := &http.Client{}
	// execute the request.
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("response error: ", err)
	}
	defer resp.Body.Close()

	//fmt.Println("response Status: ", resp.Status)
	//fmt.Println("response Headers: ", resp.Header)
	body, err := ioutil.ReadAll(resp.Body)
	//fmt.Println("responseBody:", string(body))
	var rr nxapi.NXAPI_Response
	//var rr interface{}
	err = json.Unmarshal(body, &rr)
	if err != nil {
		log.Fatal("Error unmarshalling: ", err)
	}
	// Print out the raw string to debug.
	for _, b := range rr.Ins_api.Outputs {
		if b.Input == "show version" {
			h, ok := b.Body["host_name"].(string)
			if !ok {
				continue
			}
			mutex.Lock()
			{
				if m[server] != nil {
					m[server]["hostname"] = map[string]string{"host_name": "bar"}
				} else {
					m[server] = map[string]map[string]string{"hostname": map[string]string{"host_name": h}}
				}
			}
			mutex.Unlock()
		} else if b.Input == "show interface counters" {
			interfaceCounters := nxapi.NewInterfaceCounters(b.Body)
			//m[server]
		}
	}
	//fmt.Println(m[server])
}

/* walkvalues:
 Arguments:
	server - like a switch (10.93.234.2, or c2960-001, or something like that. )
  creds - this is the SNMP community string (public, 234jfj23vjA233kj3, etc)
	oid - the OID we're going to walk through
	key - the key we want to store the individual values
	map - the map that we want to store this in.
*/
func walkValue(server string, creds string, oid string, key string, m map[string]map[string]map[string]string) {
	s, err := gosnmp.NewGoSNMP(server, creds, gosnmp.Version2c, 5)
	if err != nil {
		handleError(err)
		return
	}

	if resp, err := s.Walk(oid); err != nil {
		handleError(err)
	} else {
		for _, pdu := range resp {
			oidbits := strings.Split(pdu.Name, ".")
			ifIndex := oidbits[len(oidbits)-1]
			value := ""
			switch pdu.Type {
			case gosnmp.OctetString:
				value = pdu.Value.(string)
			case gosnmp.Counter32:
				value = fmt.Sprintf("%d", pdu.Value.(int))
			case gosnmp.Counter64:
				value = fmt.Sprintf("%d", pdu.Value.(int64))
			case gosnmp.Gauge32:
				value = fmt.Sprintf("%d", pdu.Value.(int))
			default:
				value = "decode this"
			}
			//fmt.Printf("%s / %s\n", key, value)
			mutex.Lock()
			{
				if m[server][ifIndex] != nil {
					m[server][ifIndex][key] = value
				} else {
					m[server][ifIndex] = map[string]string{key: value}
				}
			}
			mutex.Unlock()
		}
	}
	s.Close()

}

func main() {
	// We require each program to have endpoints defined.
	var params struct {
		Endpoints   string `env:"SP_ENDPOINTS,required"`
		Credentials string `env:"SP_ENDPOINT_CREDENTIALS,required"`
	}

	if err := envdecode.Decode(&params); err != nil {
		log.Fatalln(err)
	}

	endpoints := strings.Split(params.Endpoints, ",")
	creds := strings.Split(params.Credentials, ",")
	if len(endpoints) != len(creds) {
		log.Fatal("Each endpoint should have a corresponding credential")
	}

	// All the OIDs we'll snmp walk through to get
	oidWork := map[string]string{
		".1.3.6.1.2.1.1.5":         "sysName",
		".1.3.6.1.2.1.2.2.1.2":     "name",
		".1.3.6.1.2.1.2.2.1.10":    "ifInOctets",
		".1.3.6.1.2.1.2.2.1.16":    "ifOutOctets",
		".1.3.6.1.2.1.31.1.1.1.6":  "ifHCInOctets",
		".1.3.6.1.2.1.31.1.1.1.10": "ifHCOutOctets",
		".1.3.6.1.2.1.31.1.1.1.15": "ifHighSpeed",
	}

	// all the commands we walk through NXAPI to get.
	nxapiWork := map[string]string{
		"show version":            "version",
		"show interface counters": "counters",
	}

	// The main waitgroup for each switch waits for
	// each switch to finish its job.
	var mainWg sync.WaitGroup
	// The array of waitgroups are the jobs that each
	// switch must proccess.
	wg := make([]sync.WaitGroup, len(endpoints))

	// we will run in a continuous loop forever!
	// or at least until the user hits ctrl-c or we get a signal interrupt.
	stop := false
	signalChan := make(chan os.Signal, 1)
	interruptChan := make(chan struct{}, 1)
	go func() {
		<-signalChan
		stop = true
		log.Println("Cleaning up...")
		// send stuff to the channel so that it closes.  That way we don't have to wait so long.
		interruptChan <- struct{}{}
	}()
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	for {

		if stop {
			break
		}
		// make sure we wait for each of the switches
		mainWg.Add(len(endpoints))

		// go through each device and grab the counters.
		for i, endpoint := range endpoints {
			// figure out which method to run:
			em := strings.Split(endpoint, ":")
			if len(em) < 2 {
				fmt.Println("Invalid input: ", endpoint)
				fmt.Println("please export SP_ENDPOINTS=<name>:<method> where method is SNMP or NXAPI")
				// don't wait for me any more.
				mainWg.Add(-1)
				// go to the next switch
				continue
			}
			/* big map:
			server {
				interface {
					key : value
				}
			}
			*/
			m := make(map[string]map[string]map[string]string)
			if em[1] == "SNMP" {
				go func(e string, c string, w sync.WaitGroup) {
					defer mainWg.Done()
					// mapping hash table for interface names.
					w.Add(len(oidWork))
					// concurrently execute all of the snmp walks
					for oid, name := range oidWork {
						go func(o string, n string) {
							defer w.Done()
							walkValue(e, c, o, n, m)
						}(oid, name)
					}
					w.Wait()
					processCollectedSNMPData(m[e])
				}(em[0], creds[i], wg[i])
			} else if em[1] == "NXAPI" {
				// Yes.. this is hard to process, so let's walk through this.
				// If this switch is an NXAPI endpoint, we are going to kick off a goroutine
				// This go routine is going to call several commands against the switch.
				go func(e string, cre string, w sync.WaitGroup) {
					// When we finish processing all the commands against this switch, tell the main workGroup we are done.
					defer mainWg.Done()

					// we need to add a waitgroup for this task.
					// This waitgroup is specific for this switch.
					// We dont' want to wait for all the other switches to finish processing before
					// sending the data to the cloud
					w.Add(len(nxapiWork))
					// Go through each command that we want to process.
					for cmd, name := range nxapiWork {
						// kick off a go routine for each of the commands we want to get
						go func(c string, outputName string) {
							// make sure we decrement the switch waitgroup.
							defer w.Done()
							// get the data.  This is where the work takes place.
							getNXAPIData(e, c, outputName, cre, m)
						}(cmd, name)
					}
					// wait for all the switch waitgroups to finish.
					w.Wait()
					// now we have all the data for this switch, let's process it.
					processCollectedNXAPIData(m[e])
				}(em[0], creds[i], wg[i])
			}
		}
		// wait for all the snmpwalks to finish.
		mainWg.Wait()

		// now sleep for a while and then run again.
		timeoutchan := make(chan bool)
		go func() {
			fmt.Println("Sleeping for 60 seconds...")
			<-time.After(60 * time.Second)
			timeoutchan <- true
		}()
		select {
		case <-timeoutchan:
			break
		case <-interruptChan:
			// close the channel so other threads will stop blocking and finish.
			close(interruptChan)
			break
		}
	}
}

// process NXAPI data
func processCollectedNXAPIData(m map[string]map[string]string) {
}

// take all the data we were given and format it to JSON to send up
// to the server.
func processCollectedSNMPData(m map[string]map[string]string) {
	// get the name of the switch:
	sw := m["0"]["sysName"]
	// get the timestamp
	now := time.Now()

	sendMap := make(map[string][]string)
	var sendStrings []string
	//go through each switch for k, v := range m {
	for k, v := range m {
		// don't send if there is no data to send.
		if emptyValues(v) {
			continue
		}
		sendMe := fmt.Sprintf("'switch':'%s', 'ifName':'%s','timeStamp':'%d','ifInOctets':'%s', 'ifHCInOctets': '%s', 'ifOutOctets': '%s', 'ifHCOutOctets': '%s', 'ifHighSpeed': '%s', 'ifId': '%s'\n",
			sw,
			v["name"],
			now.Unix(),
			v["ifInOctets"],
			v["ifHCInOctets"],
			v["ifOutOctets"],
			v["ifHCOutOctets"],
			v["ifHighSpeed"],
			k)

		sendStrings = append(sendStrings, sendMe)
	}
	sendMap[sw] = sendStrings
	// Todo: Send this up to the main server.
	fmt.Println(sendMap)
}

// see if any of the values are empty.
func emptyValues(v map[string]string) bool {
	if v["name"] == "" {
		return true
	}
	if v["ifInOctets"] == "" {
		return true
	}
	if v["ifHCInOctets"] == "" {
		return true
	}
	if v["ifOutOctets"] == "" {
		return true
	}
	if v["ifHCOutOctets"] == "" {
		return true
	}
	if v["ifHighSpeed"] == "" {
		return true
	}
	return false
}
<commit_msg>Now it really runs<commit_after>/* stickypipe agent
Takes environment variables:
  // End points are our devices such as a network switch.
	SP_ENDPOINTS="192.168.30.1,c2960g,nexus5k-top"
	// Credentials are our logins to the endpoints.
	SP_ENDPOINT_CREDENTIALS="public"

then pipes the output up to sitckypipe in a JSON based string:
{ name: c2960g, interface-id: 1733017, interface-name: GigabitEthernet0/10, interface-in: 3866362551, interface-out: 345343003, timestamp: 1438023632  }

*/
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/joeshaw/envdecode"
	"github.com/vallard/gosnmp"
	"github.com/vallard/stickypipe-agent/nxapi"
)

var mutex sync.Mutex

func handleError(err error) {
	fmt.Println("error:", err)
}

/* Get NXAPI information
Arguments:
 server - Nexus Switch (10.93.234.2, sw001, or something reachable)
 creds - user/password pair that looks like admin:cisco (notice no :'s are supported in the password.)
 map - map we want to store this stuff.
*/

func getNXAPIData(server string, command string, outputName string, creds string, m map[string]map[string]map[string]string) {
	// argument for string looks like: admin:cisco where admin is the user and cisco is the password.
	up := strings.Split(creds, ":")
	// make sure that we parsed the username and password.
	if len(up) < 2 {
		log.Fatal("Credentials must be of form user:password")
		return
	}
	// The command we run to get the port interface statistics.
	/*
		nxcmd := NewNXAPIPost(command)
		fmt.Println(nxcmd)
		jsonStr, err := json.Marshal(nxcmd)
	*/
	var jsonStr = []byte(`{
					"ins_api": {
							"version":       "1.0",
							"type":          "cli_show",
							"chunk":         "0",
							"sid":           "1",
							"input":         "` + command + `",
							"output_format": "json",
						}
					}
						`)
	// Start formatting our HTTP POST request.
	req, err := http.NewRequest("POST", "http://"+server+"/ins", bytes.NewBuffer(jsonStr))
	if err != nil {
		log.Println("HTTP Post: ", err)
	}
	// The header has to be set to application/json
	req.Header.Set("content-type", "application/json")
	// add the username and password to the header.
	req.SetBasicAuth(up[0], up[1])

	// create a new http client to execute the request.
	client := &http.Client{}
	// execute the request.
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("response error: ", err)
	}
	defer resp.Body.Close()

	//fmt.Println("response Status: ", resp.Status)
	//fmt.Println("response Headers: ", resp.Header)
	body, err := ioutil.ReadAll(resp.Body)
	//fmt.Println("responseBody:", string(body))
	var rr nxapi.NXAPI_Response
	//var rr interface{}
	err = json.Unmarshal(body, &rr)
	if err != nil {
		log.Fatal("Error unmarshalling: ", err)
	}
	// Print out the raw string to debug.
	for _, b := range rr.Ins_api.Outputs {
		if b.Input == "show version" {
			h, ok := b.Body["host_name"].(string)
			if !ok {
				continue
			}
			mutex.Lock()
			{
				if m[server] != nil {
					m[server]["hostname"] = map[string]string{"host_name": "bar"}
				} else {
					m[server] = map[string]map[string]string{"hostname": map[string]string{"host_name": h}}
				}
			}
			mutex.Unlock()
		} else if b.Input == "show interface counters" {
			interfaceCounters := nxapi.NewInterfaceCounters(b.Body)
			fmt.Println(interfaceCounters)
			//m[server]
		}
	}
	//fmt.Println(m[server])
}

/* walkvalues:
 Arguments:
	server - like a switch (10.93.234.2, or c2960-001, or something like that. )
  creds - this is the SNMP community string (public, 234jfj23vjA233kj3, etc)
	oid - the OID we're going to walk through
	key - the key we want to store the individual values
	map - the map that we want to store this in.
*/
func walkValue(server string, creds string, oid string, key string, m map[string]map[string]map[string]string) {
	s, err := gosnmp.NewGoSNMP(server, creds, gosnmp.Version2c, 5)
	if err != nil {
		handleError(err)
		return
	}

	if resp, err := s.Walk(oid); err != nil {
		handleError(err)
	} else {
		for _, pdu := range resp {
			oidbits := strings.Split(pdu.Name, ".")
			ifIndex := oidbits[len(oidbits)-1]
			value := ""
			switch pdu.Type {
			case gosnmp.OctetString:
				value = pdu.Value.(string)
			case gosnmp.Counter32:
				value = fmt.Sprintf("%d", pdu.Value.(int))
			case gosnmp.Counter64:
				value = fmt.Sprintf("%d", pdu.Value.(int64))
			case gosnmp.Gauge32:
				value = fmt.Sprintf("%d", pdu.Value.(int))
			default:
				value = "decode this"
			}
			//fmt.Printf("%s / %s\n", key, value)
			mutex.Lock()
			{
				if m[server][ifIndex] != nil {
					m[server][ifIndex][key] = value
				} else {
					m[server][ifIndex] = map[string]string{key: value}
				}
			}
			mutex.Unlock()
		}
	}
	s.Close()

}

func main() {
	// We require each program to have endpoints defined.
	var params struct {
		Endpoints   string `env:"SP_ENDPOINTS,required"`
		Credentials string `env:"SP_ENDPOINT_CREDENTIALS,required"`
	}

	if err := envdecode.Decode(&params); err != nil {
		log.Fatalln(err)
	}

	endpoints := strings.Split(params.Endpoints, ",")
	creds := strings.Split(params.Credentials, ",")
	if len(endpoints) != len(creds) {
		log.Fatal("Each endpoint should have a corresponding credential")
	}

	// All the OIDs we'll snmp walk through to get
	oidWork := map[string]string{
		".1.3.6.1.2.1.1.5":         "sysName",
		".1.3.6.1.2.1.2.2.1.2":     "name",
		".1.3.6.1.2.1.2.2.1.10":    "ifInOctets",
		".1.3.6.1.2.1.2.2.1.16":    "ifOutOctets",
		".1.3.6.1.2.1.31.1.1.1.6":  "ifHCInOctets",
		".1.3.6.1.2.1.31.1.1.1.10": "ifHCOutOctets",
		".1.3.6.1.2.1.31.1.1.1.15": "ifHighSpeed",
	}

	// all the commands we walk through NXAPI to get.
	nxapiWork := map[string]string{
		"show version":            "version",
		"show interface counters": "counters",
	}

	// The main waitgroup for each switch waits for
	// each switch to finish its job.
	var mainWg sync.WaitGroup
	// The array of waitgroups are the jobs that each
	// switch must proccess.
	wg := make([]sync.WaitGroup, len(endpoints))

	// we will run in a continuous loop forever!
	// or at least until the user hits ctrl-c or we get a signal interrupt.
	stop := false
	signalChan := make(chan os.Signal, 1)
	interruptChan := make(chan struct{}, 1)
	go func() {
		<-signalChan
		stop = true
		log.Println("Cleaning up...")
		// send stuff to the channel so that it closes.  That way we don't have to wait so long.
		interruptChan <- struct{}{}
	}()
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	for {

		if stop {
			break
		}
		// make sure we wait for each of the switches
		mainWg.Add(len(endpoints))

		// go through each device and grab the counters.
		for i, endpoint := range endpoints {
			// figure out which method to run:
			em := strings.Split(endpoint, ":")
			if len(em) < 2 {
				fmt.Println("Invalid input: ", endpoint)
				fmt.Println("please export SP_ENDPOINTS=<name>:<method> where method is SNMP or NXAPI")
				// don't wait for me any more.
				mainWg.Add(-1)
				// go to the next switch
				continue
			}
			/* big map:
			server {
				interface {
					key : value
				}
			}
			*/
			m := make(map[string]map[string]map[string]string)
			if em[1] == "SNMP" {
				go func(e string, c string, w sync.WaitGroup) {
					defer mainWg.Done()
					// mapping hash table for interface names.
					w.Add(len(oidWork))
					// concurrently execute all of the snmp walks
					for oid, name := range oidWork {
						go func(o string, n string) {
							defer w.Done()
							walkValue(e, c, o, n, m)
						}(oid, name)
					}
					w.Wait()
					processCollectedSNMPData(m[e])
				}(em[0], creds[i], wg[i])
			} else if em[1] == "NXAPI" {
				// Yes.. this is hard to process, so let's walk through this.
				// If this switch is an NXAPI endpoint, we are going to kick off a goroutine
				// This go routine is going to call several commands against the switch.
				go func(e string, cre string, w sync.WaitGroup) {
					// When we finish processing all the commands against this switch, tell the main workGroup we are done.
					defer mainWg.Done()

					// we need to add a waitgroup for this task.
					// This waitgroup is specific for this switch.
					// We dont' want to wait for all the other switches to finish processing before
					// sending the data to the cloud
					w.Add(len(nxapiWork))
					// Go through each command that we want to process.
					for cmd, name := range nxapiWork {
						// kick off a go routine for each of the commands we want to get
						go func(c string, outputName string) {
							// make sure we decrement the switch waitgroup.
							defer w.Done()
							// get the data.  This is where the work takes place.
							getNXAPIData(e, c, outputName, cre, m)
						}(cmd, name)
					}
					// wait for all the switch waitgroups to finish.
					w.Wait()
					// now we have all the data for this switch, let's process it.
					processCollectedNXAPIData(m[e])
				}(em[0], creds[i], wg[i])
			}
		}
		// wait for all the snmpwalks to finish.
		mainWg.Wait()

		// now sleep for a while and then run again.
		timeoutchan := make(chan bool)
		go func() {
			fmt.Println("Sleeping for 60 seconds...")
			<-time.After(60 * time.Second)
			timeoutchan <- true
		}()
		select {
		case <-timeoutchan:
			break
		case <-interruptChan:
			// close the channel so other threads will stop blocking and finish.
			close(interruptChan)
			break
		}
	}
}

// process NXAPI data
func processCollectedNXAPIData(m map[string]map[string]string) {
}

// take all the data we were given and format it to JSON to send up
// to the server.
func processCollectedSNMPData(m map[string]map[string]string) {
	// get the name of the switch:
	sw := m["0"]["sysName"]
	// get the timestamp
	now := time.Now()

	sendMap := make(map[string][]string)
	var sendStrings []string
	//go through each switch for k, v := range m {
	for k, v := range m {
		// don't send if there is no data to send.
		if emptyValues(v) {
			continue
		}
		sendMe := fmt.Sprintf("'switch':'%s', 'ifName':'%s','timeStamp':'%d','ifInOctets':'%s', 'ifHCInOctets': '%s', 'ifOutOctets': '%s', 'ifHCOutOctets': '%s', 'ifHighSpeed': '%s', 'ifId': '%s'\n",
			sw,
			v["name"],
			now.Unix(),
			v["ifInOctets"],
			v["ifHCInOctets"],
			v["ifOutOctets"],
			v["ifHCOutOctets"],
			v["ifHighSpeed"],
			k)

		sendStrings = append(sendStrings, sendMe)
	}
	sendMap[sw] = sendStrings
	// Todo: Send this up to the main server.
	fmt.Println(sendMap)
}

// see if any of the values are empty.
func emptyValues(v map[string]string) bool {
	if v["name"] == "" {
		return true
	}
	if v["ifInOctets"] == "" {
		return true
	}
	if v["ifHCInOctets"] == "" {
		return true
	}
	if v["ifOutOctets"] == "" {
		return true
	}
	if v["ifHCOutOctets"] == "" {
		return true
	}
	if v["ifHighSpeed"] == "" {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"
)

var streams []stream
var boxes []screen
var commands []*exec.Cmd

type stream struct {
	Name     string
	Stream   string
	Image    string
	Favorite bool
}

type screen struct {
	StartX int
	StartY int
	EndX   int
	EndY   int
}

func main() {
	//Read in urls of webcams from configuration file
	data, err := ioutil.ReadFile("streams.json")
	if err != nil {
		panic(err)
	}

	if err := json.Unmarshal(data, &streams); err != nil {
		panic(err)
	}

	setupBoxes()

	//Startup webserver to listen to commands to execute video player
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/all", serveAll)
	http.HandleFunc("/pick", serveOne)
	http.HandleFunc("/stop", serveStop)
	// showAll()
	log.Println("server started...")
	log.Fatal(http.ListenAndServe(":2000", nil))
}

func renderWebsite(w http.ResponseWriter) {
	const tpl = `
<!DOCTYPE html>

		<meta charset="UTF-8">
		<title>Zoo Cam Viewer</title>

All

Stop

		{{range .Streams}}
{{ .Name }}

{{else}}
no streams
{{end}}

`
	t, err := template.New("webpage").Parse(tpl)
	if err != nil {
		panic(err)
	}
	t.Execute(w, struct{ Streams []stream }{Streams: streams})
}

func serveIndex(w http.ResponseWriter, r *http.Request) {
	//Serve up basic website with icons to choose from with "all" option
	renderWebsite(w)
}

func serveAll(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up all the videos
	showAll()
	renderWebsite(w)
}

func serveOne(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up the single video passed in
	name := r.FormValue("name")
	log.Println("got stream name of " + name)
	for _, stream := range streams {
		if stream.Name == name {
			log.Println("Starting stream " + stream.Name)
			showOne(stream)
		}
	}
	renderWebsite(w)
}

func serveStop(w http.ResponseWriter, r *http.Request) {
	killAll()
	renderWebsite(w)
}

func showAll() {
	killAll()

	favoriteStreams := []stream{}
	for _, s := range streams {
		if s.Favorite {
			favoriteStreams = append(favoriteStreams, s)
		}
	}

	for index, s := range favoriteStreams {
		if index >= len(boxes) {
			panic("Boxes were not properly configured")
		}
		box := boxes[index]
		cmd := exec.Command("omxplayer", "--win", fmt.Sprintf("%v,%v,%v,%v", box.StartX, box.StartY, box.EndX, box.EndY), s.Stream)
		// cmd := exec.Command("mplayer", s.Stream)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		commands = append(commands, cmd)
		cmd.Start()
		time.Sleep(1 * time.Second)
	}
}

func showOne(s stream) {
	killAll()
	//Startup in fullscreen
	cmd := exec.Command("omxplayer", "-b", s.Stream)
	// cmd := exec.Command("mplayer", s.Stream)
	cmd.Start()
	commands = append(commands, cmd)
}

func killAll() {
	log.Println("killing all existing streams")
	for _, proc := range commands {
		proc.Process.Kill()
	}
}

func setupBoxes() {
	//For now we're going to hard code a 3x2 grid across the screen
	width := 1920
	height := 1080

	//Create boxes based on the width and height of the screen

	widthStep := width / 3
	heightStep := height / 2

	//Row1
	for i := 0; i < 3; i++ {
		box := screen{
			StartX: i * widthStep,
			StartY: 0,
			EndX:   (i * widthStep) + widthStep,
			EndY:   heightStep,
		}
		boxes = append(boxes, box)
	}
	//Row2
	for i := 0; i < 3; i++ {
		box := screen{
			StartX: i * widthStep,
			StartY: heightStep,
			EndX:   (i * widthStep) + widthStep,
			EndY:   heightStep + heightStep,
		}
		boxes = append(boxes, box)
	}

	for _, box := range boxes {
		log.Printf("screen size: %v,%v,%v,%v\n", box.StartX, box.StartY, box.EndX, box.EndY)
	}
}
<commit_msg>logging PID to know why the commands aren't stopping<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"
)

var streams []stream
var boxes []screen
var commands []*exec.Cmd

type stream struct {
	Name     string
	Stream   string
	Image    string
	Favorite bool
}

type screen struct {
	StartX int
	StartY int
	EndX   int
	EndY   int
}

func main() {
	//Read in urls of webcams from configuration file
	data, err := ioutil.ReadFile("streams.json")
	if err != nil {
		panic(err)
	}

	if err := json.Unmarshal(data, &streams); err != nil {
		panic(err)
	}

	setupBoxes()

	//Startup webserver to listen to commands to execute video player
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/all", serveAll)
	http.HandleFunc("/pick", serveOne)
	http.HandleFunc("/stop", serveStop)
	// showAll()
	log.Println("server started...")
	log.Fatal(http.ListenAndServe(":2000", nil))
}

func renderWebsite(w http.ResponseWriter) {
	const tpl = `
<!DOCTYPE html>

		<meta charset="UTF-8">
		<title>Zoo Cam Viewer</title>

All

Stop

		{{range .Streams}}
{{ .Name }}

{{else}}
no streams
{{end}}

`
	t, err := template.New("webpage").Parse(tpl)
	if err != nil {
		panic(err)
	}
	t.Execute(w, struct{ Streams []stream }{Streams: streams})
}

func serveIndex(w http.ResponseWriter, r *http.Request) {
	//Serve up basic website with icons to choose from with "all" option
	renderWebsite(w)
}

func serveAll(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up all the videos
	showAll()
	renderWebsite(w)
}

func serveOne(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up the single video passed in
	name := r.FormValue("name")
	log.Println("got stream name of " + name)
	for _, stream := range streams {
		if stream.Name == name {
			log.Println("Starting stream " + stream.Name)
			showOne(stream)
		}
	}
	renderWebsite(w)
}

func serveStop(w http.ResponseWriter, r *http.Request) {
	killAll()
	renderWebsite(w)
}

func showAll() {
	killAll()

	favoriteStreams := []stream{}
	for _, s := range streams {
		if s.Favorite {
			favoriteStreams = append(favoriteStreams, s)
		}
	}

	for index, s := range favoriteStreams {
		if index >= len(boxes) {
			panic("Boxes were not properly configured")
		}
		box := boxes[index]
		cmd := exec.Command("omxplayer", "--win", fmt.Sprintf("%v,%v,%v,%v", box.StartX, box.StartY, box.EndX, box.EndY), s.Stream)
		// cmd := exec.Command("mplayer", s.Stream)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		commands = append(commands, cmd)
		cmd.Start()
		time.Sleep(1 * time.Second)
	}
}

func showOne(s stream) {
	killAll()
	//Startup in fullscreen
	cmd := exec.Command("omxplayer", "-b", s.Stream)
	// cmd := exec.Command("mplayer", s.Stream)
	cmd.Start()
	commands = append(commands, cmd)
}

func killAll() {
	log.Println("killing all existing streams")
	for _, proc := range commands {
		log.Println("killing process ", proc.Process.Pid)
		proc.Process.Kill()
	}
}

func setupBoxes() {
	//For now we're going to hard code a 3x2 grid across the screen
	width := 1920
	height := 1080

	//Create boxes based on the width and height of the screen

	widthStep := width / 3
	heightStep := height / 2

	//Row1
	for i := 0; i < 3; i++ {
		box := screen{
			StartX: i * widthStep,
			StartY: 0,
			EndX:   (i * widthStep) + widthStep,
			EndY:   heightStep,
		}
		boxes = append(boxes, box)
	}
	//Row2
	for i := 0; i < 3; i++ {
		box := screen{
			StartX: i * widthStep,
			StartY: heightStep,
			EndX:   (i * widthStep) + widthStep,
			EndY:   heightStep + heightStep,
		}
		boxes = append(boxes, box)
	}

	for _, box := range boxes {
		log.Printf("screen size: %v,%v,%v,%v\n", box.StartX, box.StartY, box.EndX, box.EndY)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/ghodss/yaml"
	"github.com/kr/pretty"
)

func main() {

	hooks := parseYamlConfig()

	for _, hook := range hooks {
		hook.init()
	}

	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		fmt.Fprintf(w, "pong")
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}
		w.WriteHeader(200)
		response := pretty.Sprintf("Configuration:\n\n%+v", hooks)
		fmt.Fprintf(w, response)
	})

	port := envDefault("PORT", "8080")

	cert, key := os.Getenv("CERT"), os.Getenv("KEY")

	var err error

	if cert != "" && key != "" {
		fmt.Println("Starting TLS server on port", port)
		err = http.ListenAndServeTLS(":"+port, cert, key, nil)
	} else {
		fmt.Println("Starting server on port", port)
		err = http.ListenAndServe(":"+port, nil)
	}
	handleError(err)
	runCmd("sleep", []string{"1000"})

}

func parseYamlConfig() []webhook {
	hooks := make([]webhook, 0)

	configFile := envDefault("CONFIG_FILE", "config.yml")

	data, err := ioutil.ReadFile(configFile)
	handleError(err)

	err = yaml.Unmarshal(data, &hooks)
	handleError(err)

	fmt.Printf("%+v\n\n", hooks)

	return hooks
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func handleError(err error, fatal ...bool) {
	if err != nil {
		if len(fatal) > 0 && fatal[0] {
			log.Fatal(err)
		} else {
			log.Print(err)
		}
	}
}

func envDefault(env, fallback string) (val string) {
	val = os.Getenv(env)
	if val == "" {
		val = fallback
	}
	return val
}
<commit_msg>Pretty print json<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"encoding/json"

	"github.com/ghodss/yaml"
)

func main() {

	hooks := parseYamlConfig()

	for _, hook := range hooks {
		hook.init()
	}
	hooks = append(hooks, webhook{
		Repo: "hi",
	})

	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		fmt.Fprintf(w, "pong")
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}
		w.WriteHeader(200)
		response, _ := json.MarshalIndent(hooks, "", "\t")
		fmt.Fprintf(w, "Configuration:\n\n%s", string(response))
	})

	port := envDefault("PORT", "8080")

	cert, key := os.Getenv("CERT"), os.Getenv("KEY")

	var err error

	if cert != "" && key != "" {
		fmt.Println("Starting TLS server on port", port)
		err = http.ListenAndServeTLS(":"+port, cert, key, nil)
	} else {
		fmt.Println("Starting server on port", port)
		err = http.ListenAndServe(":"+port, nil)
	}
	handleError(err)
	runCmd("sleep", []string{"1000"})

}

func parseYamlConfig() []webhook {
	hooks := make([]webhook, 0)

	configFile := envDefault("CONFIG_FILE", "config.yml")

	data, err := ioutil.ReadFile(configFile)
	handleError(err)

	err = yaml.Unmarshal(data, &hooks)
	handleError(err)

	fmt.Printf("%+v\n\n", hooks)

	return hooks
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func handleError(err error, fatal ...bool) {
	if err != nil {
		if len(fatal) > 0 && fatal[0] {
			log.Fatal(err)
		} else {
			log.Print(err)
		}
	}
}

func envDefault(env, fallback string) (val string) {
	val = os.Getenv(env)
	if val == "" {
		val = fallback
	}
	return val
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/auth0/go-jwt-middleware"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/justinas/nosurf"
	"github.com/unrolled/render"
	"net/http"
)

var (
	env  = flag.String("env", "prod", "environment ('prod' or 'dev')")
	port = flag.String("port", "5000", "server port")
)

// should be settings
const (
	staticURL    = "/static/"
	staticDir    = "./static/"
	devServerURL = "http://localhost:8080"
)

func main() {

	flag.Parse()

	router := mux.NewRouter()

	router.PathPrefix(staticURL).Handler(
		http.StripPrefix(staticURL, http.FileServer(http.Dir(staticDir))))

	render := render.New()

	router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		ctx := map[string]string{
			"staticURL": staticURL,
		}
		render.HTML(w, http.StatusOK, "index", ctx)
	})

	router.HandleFunc("/auth/", func(w http.ResponseWriter, r *http.Request) {
		user := context.Get(r, "user")
		fmt.Fprintf(w, "This is an authenticated request:\n")
		if user == nil {
			fmt.Fprintf(w, "No token set")
		} else {
			fmt.Fprintf(w, "Claim content:\n")
			for k, v := range user.(*jwt.Token).Claims {
				fmt.Fprintf(w, "%s: \t%#v", k, v)
			}
		}
	})

	jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			return []byte("My Secret"), nil
		},
		CredentialsOptional: true,
		SigningMethod:       jwt.SigningMethodHS256,
	})

	chain := alice.New(nosurf.NewPure, jwtMiddleware.Handler).Then(router)

	if err := http.ListenAndServe(":"+*port, chain); err != nil {
		panic(err)
	}

}
<commit_msg>More jwt stuff<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/auth0/go-jwt-middleware"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/justinas/nosurf"
	"github.com/unrolled/render"
	"net/http"
	"time"
)

var (
	env  = flag.String("env", "prod", "environment ('prod' or 'dev')")
	port = flag.String("port", "5000", "server port")
)

// should be settings
const (
	staticURL    = "/static/"
	staticDir    = "./static/"
	devServerURL = "http://localhost:8080"
)

func main() {

	flag.Parse()

	router := mux.NewRouter()

	router.PathPrefix(staticURL).Handler(
		http.StripPrefix(staticURL, http.FileServer(http.Dir(staticDir))))

	render := render.New()

	router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		ctx := map[string]string{
			"staticURL": staticURL,
		}
		render.HTML(w, http.StatusOK, "index", ctx)
	})

	router.HandleFunc("/auth/", func(w http.ResponseWriter, r *http.Request) {
		token := jwt.New(jwt.SigningMethodHS256)
		token.Claims["id"] = "1234567"
		token.Claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
		tokenString, err := token.SignedString([]byte("My Secret"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Fprintf(w, tokenString)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(tokenString))
	})

	router.HandleFunc("/secure/", func(w http.ResponseWriter, r *http.Request) {
		user := context.Get(r, "user")
		fmt.Fprintf(w, "This is an authenticated request:\n")
		if user == nil {
			fmt.Fprintf(w, "No token set")
		} else {
			fmt.Fprintf(w, "Claim content:\n")
			for k, v := range user.(*jwt.Token).Claims {
				fmt.Fprintf(w, "%s: \t%#v", k, v)
			}
		}
	})

	jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			return []byte("My Secret"), nil
		},
		CredentialsOptional: true,
		SigningMethod:       jwt.SigningMethodHS256,
	})

	chain := alice.New(nosurf.NewPure, jwtMiddleware.Handler).Then(router)

	if err := http.ListenAndServe(":"+*port, chain); err != nil {
		panic(err)
	}

}
<|endoftext|>


<commit_before>package vanish

func ExampleFile() {
	File(func(name string) {
		// 'name' is the name of a temporary file that’ll be deleted at the end
		// of the function. You can thus use it for your tests without worrying
		// about it
	})
}

func ExampleDir() {
	Dir(func(name string) {
		// Dir works like File, but it creates a directory instead. Use it as
		// you wish, it’ll be deleted at the end of this function.
	})
}

func ExampleEnv() {
	Env(func() {
		// Env saves the environment before calling this function, ensuring
		// it’s restored as it was at its end.
	})
}
<commit_msg>tests added<commit_after>package vanish

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func fileExists(path string) bool {
	// we don’t really check for errors here
	_, err := os.Stat(path)
	return err == nil
}

func TestFile(t *testing.T) {
	var filename string

	File(func(name string) {
		filename = name
		assert.True(t, fileExists(filename))
	})

	assert.False(t, fileExists(filename))
}

func TestDir(t *testing.T) {
	var dirname string

	Dir(func(name string) {
		dirname = name
		assert.True(t, fileExists(dirname))
	})

	assert.False(t, fileExists(dirname))
}

func TestEnv(t *testing.T) {
	key := "VANISH_TEST_ENV_42XYZ"
	val := "foob&ar+/xqye$@"
	os.Setenv(key, val)

	Env(func() {
		assert.Equal(t, val, os.Getenv(key))
		os.Setenv(key, "yolofoo")
	})

	assert.Equal(t, val, os.Getenv(key))
}

func ExampleFile() {
	File(func(name string) {
		// 'name' is the name of a temporary file that’ll be deleted at the end
		// of the function. You can thus use it for your tests without worrying
		// about it
	})
}

func ExampleDir() {
	Dir(func(name string) {
		// Dir works like File, but it creates a directory instead. Use it as
		// you wish, it’ll be deleted at the end of this function.
	})
}

func ExampleEnv() {
	Env(func() {
		// Env saves the environment before calling this function, ensuring
		// it’s restored as it was at its end.
	})
}
<|endoftext|>


<commit_before>package driver

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

	"github.com/blang/semver"
	"github.com/concourse/semver-resource/version"
)

var gitRepoDir string
var privateKeyPath string
var netRcPath string

var ErrEncryptedKey = errors.New("private keys with passphrases are not supported")
var RetriesOnErrorWriteVersion = 3

func init() {
	gitRepoDir = filepath.Join(os.TempDir(), "semver-git-repo")
	privateKeyPath = filepath.Join(os.TempDir(), "private-key")
	netRcPath = filepath.Join(os.Getenv("HOME"), ".netrc")
}

type GitDriver struct {
	InitialVersion semver.Version

	URI           string
	Branch        string
	PrivateKey    string
	Username      string
	Password      string
	File          string
	GitUser       string
	Depth         string
	CommitMessage string
}

func (driver *GitDriver) Bump(bump version.Bump) (semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return semver.Version{}, err
	}

	err = driver.setUserInfo()
	if err != nil {
		return semver.Version{}, err
	}

	var newVersion semver.Version

	for i := 1; i <= RetriesOnErrorWriteVersion; i++ {
		err = driver.setUpRepo()
		if err != nil {
			return semver.Version{}, err
		}

		currentVersion, exists, err := driver.readVersion()
		if err != nil {
			return semver.Version{}, err
		}

		if !exists {
			currentVersion = driver.InitialVersion
		}

		newVersion = bump.Apply(currentVersion)

		wrote, err := driver.writeVersion(newVersion)
		if wrote {
			break
		}
	}
	if err != nil {
		return semver.Version{}, err
	}

	return newVersion, nil
}

func (driver *GitDriver) Set(newVersion semver.Version) error {
	err := driver.setUpAuth()
	if err != nil {
		return err
	}

	err = driver.setUserInfo()
	if err != nil {
		return err
	}

	for {
		err = driver.setUpRepo()
		if err != nil {
			return err
		}

		wrote, err := driver.writeVersion(newVersion)
		if err != nil {
			return err
		}

		if wrote {
			break
		}
	}

	return nil
}

func (driver *GitDriver) Check(cursor *semver.Version) ([]semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return nil, err
	}

	err = driver.setUpRepo()
	if err != nil {
		return nil, err
	}

	currentVersion, exists, err := driver.readVersion()
	if err != nil {
		return nil, err
	}

	if !exists {
		return []semver.Version{driver.InitialVersion}, nil
	}

	if cursor == nil || currentVersion.GTE(*cursor) {
		return []semver.Version{currentVersion}, nil
	}

	return []semver.Version{}, nil
}

func (driver *GitDriver) setUpRepo() error {
	_, err := os.Stat(gitRepoDir)
	if err != nil {
		gitClone := exec.Command("git", "clone", driver.URI, "--branch", driver.Branch)
		if len(driver.Depth) > 0 {
			gitClone.Args = append(gitClone.Args, "--depth", driver.Depth)
		}
		gitClone.Args = append(gitClone.Args, "--single-branch", gitRepoDir)
		gitClone.Stdout = os.Stderr
		gitClone.Stderr = os.Stderr
		if err := gitClone.Run(); err != nil {
			return err
		}
	} else {
		gitFetch := exec.Command("git", "fetch", "origin", driver.Branch)
		gitFetch.Dir = gitRepoDir
		gitFetch.Stdout = os.Stderr
		gitFetch.Stderr = os.Stderr
		if err := gitFetch.Run(); err != nil {
			return err
		}
	}

	gitCheckout := exec.Command("git", "reset", "--hard", "origin/"+driver.Branch)
	gitCheckout.Dir = gitRepoDir
	gitCheckout.Stdout = os.Stderr
	gitCheckout.Stderr = os.Stderr
	if err := gitCheckout.Run(); err != nil {
		return err
	}

	return nil
}

func (driver *GitDriver) setUpAuth() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	} else {
		err := os.Remove(netRcPath)
		if err != nil {
			return err
		}
	}

	if len(driver.PrivateKey) > 0 {
		err := driver.setUpKey()
		if err != nil {
			return err
		}
	}

	if len(driver.Username) > 0 && len(driver.Password) > 0 {
		err := driver.setUpUsernamePassword()
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUpKey() error {
	_, err := os.Stat(privateKeyPath)
	if err != nil {
		if os.IsNotExist(err) {
			err := ioutil.WriteFile(privateKeyPath, []byte(driver.PrivateKey), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	if isPrivateKeyEncrypted(privateKeyPath) {
		return ErrEncryptedKey
	}

	return os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -i "+privateKeyPath)
}

func isPrivateKeyEncrypted(path string) bool {
	passphrase := ``
	cmd := exec.Command(`ssh-keygen`, `-y`, `-f`, path, `-P`, passphrase)
	err := cmd.Run()

	return err != nil
}

func (driver *GitDriver) setUpUsernamePassword() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if os.IsNotExist(err) {
			content := fmt.Sprintf("default login %s password %s", driver.Username, driver.Password)
			err := ioutil.WriteFile(netRcPath, []byte(content), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUserInfo() error {
	if len(driver.GitUser) == 0 {
		return nil
	}

	e, err := mail.ParseAddress(driver.GitUser)
	if err != nil {
		return err
	}

	if len(e.Name) > 0 {
		gitName := exec.Command("git", "config", "--global", "user.name", e.Name)
		gitName.Stdout = os.Stderr
		gitName.Stderr = os.Stderr
		if err := gitName.Run(); err != nil {
			return err
		}
	}

	gitEmail := exec.Command("git", "config", "--global", "user.email", e.Address)
	gitEmail.Stdout = os.Stderr
	gitEmail.Stderr = os.Stderr
	if err := gitEmail.Run(); err != nil {
		return err
	}
	return nil
}

func (driver *GitDriver) readVersion() (semver.Version, bool, error) {
	var currentVersionStr string
	versionFile, err := os.Open(filepath.Join(gitRepoDir, driver.File))
	if err != nil {
		if os.IsNotExist(err) {
			return semver.Version{}, false, nil
		}

		return semver.Version{}, false, err
	}

	defer versionFile.Close()

	_, err = fmt.Fscanf(versionFile, "%s", &currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	currentVersion, err := semver.Parse(currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	return currentVersion, true, nil
}

const nothingToCommitString = "nothing to commit"
const falsePushString = "Everything up-to-date"
const pushRejectedString = "[rejected]"
const pushRemoteRejectedString = "[remote rejected]"

func (driver *GitDriver) writeVersion(newVersion semver.Version) (bool, error) {
	err := ioutil.WriteFile(filepath.Join(gitRepoDir, driver.File), []byte(newVersion.String()+"\n"), 0644)
	if err != nil {
		return false, err
	}

	gitAdd := exec.Command("git", "add", driver.File)
	gitAdd.Dir = gitRepoDir
	gitAdd.Stdout = os.Stderr
	gitAdd.Stderr = os.Stderr
	if err := gitAdd.Run(); err != nil {
		return false, err
	}
	var commitMessage string
	if driver.CommitMessage == "" {
		commitMessage = "bump to " + newVersion.String()
	} else {
		commitMessage = strings.Replace(driver.CommitMessage, "%version%", newVersion.String(), -1)
		commitMessage = strings.Replace(commitMessage, "%file%", driver.File, -1)
	}

	gitCommit := exec.Command("git", "commit", "-m", commitMessage)
	gitCommit.Dir = gitRepoDir

	commitOutput, err := gitCommit.CombinedOutput()

	if strings.Contains(string(commitOutput), nothingToCommitString) {
		return true, nil
	}

	if err != nil {
		os.Stderr.Write(commitOutput)
		return false, err
	}

	gitPush := exec.Command("git", "push", "origin", "HEAD:"+driver.Branch)
	gitPush.Dir = gitRepoDir

	pushOutput, err := gitPush.CombinedOutput()

	if strings.Contains(string(pushOutput), falsePushString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRejectedString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRemoteRejectedString) {
		return false, nil
	}

	if err != nil {
		os.Stderr.Write(pushOutput)
		return false, err
	}

	return true, nil
}
<commit_msg>fix another loop<commit_after>package driver

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

	"github.com/blang/semver"
	"github.com/concourse/semver-resource/version"
)

var gitRepoDir string
var privateKeyPath string
var netRcPath string

var ErrEncryptedKey = errors.New("private keys with passphrases are not supported")
var RetriesOnErrorWriteVersion = 3

func init() {
	gitRepoDir = filepath.Join(os.TempDir(), "semver-git-repo")
	privateKeyPath = filepath.Join(os.TempDir(), "private-key")
	netRcPath = filepath.Join(os.Getenv("HOME"), ".netrc")
}

type GitDriver struct {
	InitialVersion semver.Version

	URI           string
	Branch        string
	PrivateKey    string
	Username      string
	Password      string
	File          string
	GitUser       string
	Depth         string
	CommitMessage string
}

func (driver *GitDriver) Bump(bump version.Bump) (semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return semver.Version{}, err
	}

	err = driver.setUserInfo()
	if err != nil {
		return semver.Version{}, err
	}

	var newVersion semver.Version

	for i := 1; i <= RetriesOnErrorWriteVersion; i++ {
		err = driver.setUpRepo()
		if err != nil {
			return semver.Version{}, err
		}

		currentVersion, exists, err := driver.readVersion()
		if err != nil {
			return semver.Version{}, err
		}

		if !exists {
			currentVersion = driver.InitialVersion
		}

		newVersion = bump.Apply(currentVersion)

		wrote, err := driver.writeVersion(newVersion)
		if wrote {
			break
		}
	}
	if err != nil {
		return semver.Version{}, err
	}

	return newVersion, nil
}

func (driver *GitDriver) Set(newVersion semver.Version) error {
	err := driver.setUpAuth()
	if err != nil {
		return err
	}

	err = driver.setUserInfo()
	if err != nil {
		return err
	}

	for i := 1; i <= RetriesOnErrorWriteVersion; i++ {
		err = driver.setUpRepo()
		if err != nil {
			return err
		}

		wrote, err := driver.writeVersion(newVersion)
		if err != nil {
			return err
		}

		if wrote {
			break
		}
	}

	return nil
}

func (driver *GitDriver) Check(cursor *semver.Version) ([]semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return nil, err
	}

	err = driver.setUpRepo()
	if err != nil {
		return nil, err
	}

	currentVersion, exists, err := driver.readVersion()
	if err != nil {
		return nil, err
	}

	if !exists {
		return []semver.Version{driver.InitialVersion}, nil
	}

	if cursor == nil || currentVersion.GTE(*cursor) {
		return []semver.Version{currentVersion}, nil
	}

	return []semver.Version{}, nil
}

func (driver *GitDriver) setUpRepo() error {
	_, err := os.Stat(gitRepoDir)
	if err != nil {
		gitClone := exec.Command("git", "clone", driver.URI, "--branch", driver.Branch)
		if len(driver.Depth) > 0 {
			gitClone.Args = append(gitClone.Args, "--depth", driver.Depth)
		}
		gitClone.Args = append(gitClone.Args, "--single-branch", gitRepoDir)
		gitClone.Stdout = os.Stderr
		gitClone.Stderr = os.Stderr
		if err := gitClone.Run(); err != nil {
			return err
		}
	} else {
		gitFetch := exec.Command("git", "fetch", "origin", driver.Branch)
		gitFetch.Dir = gitRepoDir
		gitFetch.Stdout = os.Stderr
		gitFetch.Stderr = os.Stderr
		if err := gitFetch.Run(); err != nil {
			return err
		}
	}

	gitCheckout := exec.Command("git", "reset", "--hard", "origin/"+driver.Branch)
	gitCheckout.Dir = gitRepoDir
	gitCheckout.Stdout = os.Stderr
	gitCheckout.Stderr = os.Stderr
	if err := gitCheckout.Run(); err != nil {
		return err
	}

	return nil
}

func (driver *GitDriver) setUpAuth() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	} else {
		err := os.Remove(netRcPath)
		if err != nil {
			return err
		}
	}

	if len(driver.PrivateKey) > 0 {
		err := driver.setUpKey()
		if err != nil {
			return err
		}
	}

	if len(driver.Username) > 0 && len(driver.Password) > 0 {
		err := driver.setUpUsernamePassword()
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUpKey() error {
	_, err := os.Stat(privateKeyPath)
	if err != nil {
		if os.IsNotExist(err) {
			err := ioutil.WriteFile(privateKeyPath, []byte(driver.PrivateKey), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	if isPrivateKeyEncrypted(privateKeyPath) {
		return ErrEncryptedKey
	}

	return os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -i "+privateKeyPath)
}

func isPrivateKeyEncrypted(path string) bool {
	passphrase := ``
	cmd := exec.Command(`ssh-keygen`, `-y`, `-f`, path, `-P`, passphrase)
	err := cmd.Run()

	return err != nil
}

func (driver *GitDriver) setUpUsernamePassword() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if os.IsNotExist(err) {
			content := fmt.Sprintf("default login %s password %s", driver.Username, driver.Password)
			err := ioutil.WriteFile(netRcPath, []byte(content), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUserInfo() error {
	if len(driver.GitUser) == 0 {
		return nil
	}

	e, err := mail.ParseAddress(driver.GitUser)
	if err != nil {
		return err
	}

	if len(e.Name) > 0 {
		gitName := exec.Command("git", "config", "--global", "user.name", e.Name)
		gitName.Stdout = os.Stderr
		gitName.Stderr = os.Stderr
		if err := gitName.Run(); err != nil {
			return err
		}
	}

	gitEmail := exec.Command("git", "config", "--global", "user.email", e.Address)
	gitEmail.Stdout = os.Stderr
	gitEmail.Stderr = os.Stderr
	if err := gitEmail.Run(); err != nil {
		return err
	}
	return nil
}

func (driver *GitDriver) readVersion() (semver.Version, bool, error) {
	var currentVersionStr string
	versionFile, err := os.Open(filepath.Join(gitRepoDir, driver.File))
	if err != nil {
		if os.IsNotExist(err) {
			return semver.Version{}, false, nil
		}

		return semver.Version{}, false, err
	}

	defer versionFile.Close()

	_, err = fmt.Fscanf(versionFile, "%s", &currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	currentVersion, err := semver.Parse(currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	return currentVersion, true, nil
}

const nothingToCommitString = "nothing to commit"
const falsePushString = "Everything up-to-date"
const pushRejectedString = "[rejected]"
const pushRemoteRejectedString = "[remote rejected]"

func (driver *GitDriver) writeVersion(newVersion semver.Version) (bool, error) {
	err := ioutil.WriteFile(filepath.Join(gitRepoDir, driver.File), []byte(newVersion.String()+"\n"), 0644)
	if err != nil {
		return false, err
	}

	gitAdd := exec.Command("git", "add", driver.File)
	gitAdd.Dir = gitRepoDir
	gitAdd.Stdout = os.Stderr
	gitAdd.Stderr = os.Stderr
	if err := gitAdd.Run(); err != nil {
		return false, err
	}
	var commitMessage string
	if driver.CommitMessage == "" {
		commitMessage = "bump to " + newVersion.String()
	} else {
		commitMessage = strings.Replace(driver.CommitMessage, "%version%", newVersion.String(), -1)
		commitMessage = strings.Replace(commitMessage, "%file%", driver.File, -1)
	}

	gitCommit := exec.Command("git", "commit", "-m", commitMessage)
	gitCommit.Dir = gitRepoDir

	commitOutput, err := gitCommit.CombinedOutput()

	if strings.Contains(string(commitOutput), nothingToCommitString) {
		return true, nil
	}

	if err != nil {
		os.Stderr.Write(commitOutput)
		return false, err
	}

	gitPush := exec.Command("git", "push", "origin", "HEAD:"+driver.Branch)
	gitPush.Dir = gitRepoDir

	pushOutput, err := gitPush.CombinedOutput()

	if strings.Contains(string(pushOutput), falsePushString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRejectedString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRemoteRejectedString) {
		return false, nil
	}

	if err != nil {
		os.Stderr.Write(pushOutput)
		return false, err
	}

	return true, nil
}
<|endoftext|>


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

package cobra

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	mangen "github.com/cpuguy83/go-md2man/md2man"
	"github.com/spf13/pflag"
)

// GenManTree will call cmd.GenManTree(header, dir)
func GenManTree(cmd *Command, header *GenManHeader, dir string) {
	cmd.GenManTree(header, dir)
}

// GenManTree will generate a man page for this command and all decendants
// in the directory given. The header may be nil. This function may not work
// correctly if your command names have - in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`.
func (cmd *Command) GenManTree(header *GenManHeader, dir string) {
	if header == nil {
		header = &GenManHeader{}
	}
	for _, c := range cmd.Commands() {
		if !c.IsAvailableCommand() || c == cmd.helpCommand {
			continue
		}
		GenManTree(c, header, dir)
	}
	out := new(bytes.Buffer)

	cmd.GenMan(header, out)

	filename := cmd.CommandPath()
	filename = dir + strings.Replace(filename, " ", "-", -1) + ".1"
	outFile, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer outFile.Close()
	_, err = outFile.Write(out.Bytes())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// GenManHeader is a lot like the .TH header at the start of man pages. These
// include the title, section, date, source, and manual. We will use the
// current time if Date if unset and will use "Auto generated by spf13/cobra"
// if the Source is unset.
type GenManHeader struct {
	Title   string
	Section string
	Date    *time.Time
	date    string
	Source  string
	Manual  string
}

// GenMan will call cmd.GenMan(header, out)
func GenMan(cmd *Command, header *GenManHeader, out *bytes.Buffer) {
	cmd.GenMan(header, out)
}

// GenMan will generate a man page for the given command in the out buffer.
// The header argument may be nil, however obviously out may not.
func (cmd *Command) GenMan(header *GenManHeader, out *bytes.Buffer) {
	if header == nil {
		header = &GenManHeader{}
	}
	buf := genMarkdown(cmd, header)
	final := mangen.Render(buf)
	out.Write(final)
}

func fillHeader(header *GenManHeader, name string) {
	if header.Title == "" {
		header.Title = name
	}
	if header.Section == "" {
		header.Section = "1"
	}
	if header.Date == nil {
		now := time.Now()
		header.Date = &now
	}
	header.date = (*header.Date).Format("Jan 2006")
	if header.Source == "" {
		header.Source = "Auto generated by spf13/cobra"
	}
}

func manPreamble(out *bytes.Buffer, header *GenManHeader, name, short, long string) {
	fmt.Fprintf(out, `%% %s(%s)%s
%% %s
%% %s
# NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual)
	fmt.Fprintf(out, "%s \\- %s\n\n", name, short)
	fmt.Fprintf(out, "# SYNOPSIS\n")
	fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name)
	fmt.Fprintf(out, "# DESCRIPTION\n")
	fmt.Fprintf(out, "%s\n\n", long)
}

func manPrintFlags(out *bytes.Buffer, flags *pflag.FlagSet) {
	flags.VisitAll(func(flag *pflag.Flag) {
		if len(flag.Deprecated) > 0 || flag.Hidden {
			return
		}
		format := ""
		if len(flag.Shorthand) > 0 {
			format = "**-%s**, **--%s**"
		} else {
			format = "%s**--%s**"
		}
		if len(flag.NoOptDefVal) > 0 {
			format = format + "["
		}
		if flag.Value.Type() == "string" {
			// put quotes on the value
			format = format + "=%q"
		} else {
			format = format + "=%s"
		}
		if len(flag.NoOptDefVal) > 0 {
			format = format + "]"
		}
		format = format + "\n\t%s\n\n"
		fmt.Fprintf(out, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
	})
}

func manPrintOptions(out *bytes.Buffer, command *Command) {
	flags := command.NonInheritedFlags()
	if flags.HasFlags() {
		fmt.Fprintf(out, "# OPTIONS\n")
		manPrintFlags(out, flags)
		fmt.Fprintf(out, "\n")
	}
	flags = command.InheritedFlags()
	if flags.HasFlags() {
		fmt.Fprintf(out, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
		manPrintFlags(out, flags)
		fmt.Fprintf(out, "\n")
	}
}

func genMarkdown(cmd *Command, header *GenManHeader) []byte {
	fillHeader(header, cmd.Name())
	// something like `rootcmd subcmd1 subcmd2`
	commandName := cmd.CommandPath()
	// something like `rootcmd-subcmd1-subcmd2`
	dashCommandName := strings.Replace(commandName, " ", "-", -1)

	buf := new(bytes.Buffer)

	short := cmd.Short
	long := cmd.Long
	if len(long) == 0 {
		long = short
	}

	manPreamble(buf, header, commandName, short, long)
	manPrintOptions(buf, cmd)
	if len(cmd.Example) > 0 {
		fmt.Fprintf(buf, "# EXAMPLE\n")
		fmt.Fprintf(buf, "```\n%s\n```\n", cmd.Example)
	}
	if cmd.hasSeeAlso() {
		fmt.Fprintf(buf, "# SEE ALSO\n")
		if cmd.HasParent() {
			parentPath := cmd.Parent().CommandPath()
			dashParentPath := strings.Replace(parentPath, " ", "-", -1)
			fmt.Fprintf(buf, "**%s(%s)**, ", dashParentPath, header.Section)
			cmd.VisitParents(func(c *Command) {
				if c.DisableAutoGenTag {
					cmd.DisableAutoGenTag = c.DisableAutoGenTag
				}
			})
		}
		children := cmd.Commands()
		sort.Sort(byName(children))
		for _, c := range children {
			if !c.IsAvailableCommand() || c == cmd.helpCommand {
				continue
			}
			fmt.Fprintf(buf, "**%s-%s(%s)**, ", dashCommandName, c.Name(), header.Section)
		}
		fmt.Fprintf(buf, "\n")
	}
	if !cmd.DisableAutoGenTag {
		fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006"))
	}
	return buf.Bytes()
}
<commit_msg>Fix Lintian warning: manpage-has-bad-whatis-entry<commit_after>// Copyright 2015 Red Hat Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cobra

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	mangen "github.com/cpuguy83/go-md2man/md2man"
	"github.com/spf13/pflag"
)

// GenManTree will call cmd.GenManTree(header, dir)
func GenManTree(cmd *Command, header *GenManHeader, dir string) {
	cmd.GenManTree(header, dir)
}

// GenManTree will generate a man page for this command and all decendants
// in the directory given. The header may be nil. This function may not work
// correctly if your command names have - in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`.
func (cmd *Command) GenManTree(header *GenManHeader, dir string) {
	if header == nil {
		header = &GenManHeader{}
	}
	for _, c := range cmd.Commands() {
		if !c.IsAvailableCommand() || c == cmd.helpCommand {
			continue
		}
		GenManTree(c, header, dir)
	}
	out := new(bytes.Buffer)

	cmd.GenMan(header, out)

	filename := cmd.CommandPath()
	filename = dir + strings.Replace(filename, " ", "-", -1) + ".1"
	outFile, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer outFile.Close()
	_, err = outFile.Write(out.Bytes())
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// GenManHeader is a lot like the .TH header at the start of man pages. These
// include the title, section, date, source, and manual. We will use the
// current time if Date if unset and will use "Auto generated by spf13/cobra"
// if the Source is unset.
type GenManHeader struct {
	Title   string
	Section string
	Date    *time.Time
	date    string
	Source  string
	Manual  string
}

// GenMan will call cmd.GenMan(header, out)
func GenMan(cmd *Command, header *GenManHeader, out *bytes.Buffer) {
	cmd.GenMan(header, out)
}

// GenMan will generate a man page for the given command in the out buffer.
// The header argument may be nil, however obviously out may not.
func (cmd *Command) GenMan(header *GenManHeader, out *bytes.Buffer) {
	if header == nil {
		header = &GenManHeader{}
	}
	buf := genMarkdown(cmd, header)
	final := mangen.Render(buf)
	out.Write(final)
}

func fillHeader(header *GenManHeader, name string) {
	if header.Title == "" {
		header.Title = name
	}
	if header.Section == "" {
		header.Section = "1"
	}
	if header.Date == nil {
		now := time.Now()
		header.Date = &now
	}
	header.date = (*header.Date).Format("Jan 2006")
	if header.Source == "" {
		header.Source = "Auto generated by spf13/cobra"
	}
}

func manPreamble(out *bytes.Buffer, header *GenManHeader, name, short, long string) {
	dashName := strings.Replace(name, " ", "-", -1)
	fmt.Fprintf(out, `%% %s(%s)%s
%% %s
%% %s
# NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual)
	fmt.Fprintf(out, "%s \\- %s\n\n", dashName, short)
	fmt.Fprintf(out, "# SYNOPSIS\n")
	fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name)
	fmt.Fprintf(out, "# DESCRIPTION\n")
	fmt.Fprintf(out, "%s\n\n", long)
}

func manPrintFlags(out *bytes.Buffer, flags *pflag.FlagSet) {
	flags.VisitAll(func(flag *pflag.Flag) {
		if len(flag.Deprecated) > 0 || flag.Hidden {
			return
		}
		format := ""
		if len(flag.Shorthand) > 0 {
			format = "**-%s**, **--%s**"
		} else {
			format = "%s**--%s**"
		}
		if len(flag.NoOptDefVal) > 0 {
			format = format + "["
		}
		if flag.Value.Type() == "string" {
			// put quotes on the value
			format = format + "=%q"
		} else {
			format = format + "=%s"
		}
		if len(flag.NoOptDefVal) > 0 {
			format = format + "]"
		}
		format = format + "\n\t%s\n\n"
		fmt.Fprintf(out, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
	})
}

func manPrintOptions(out *bytes.Buffer, command *Command) {
	flags := command.NonInheritedFlags()
	if flags.HasFlags() {
		fmt.Fprintf(out, "# OPTIONS\n")
		manPrintFlags(out, flags)
		fmt.Fprintf(out, "\n")
	}
	flags = command.InheritedFlags()
	if flags.HasFlags() {
		fmt.Fprintf(out, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
		manPrintFlags(out, flags)
		fmt.Fprintf(out, "\n")
	}
}

func genMarkdown(cmd *Command, header *GenManHeader) []byte {
	fillHeader(header, cmd.Name())
	// something like `rootcmd subcmd1 subcmd2`
	commandName := cmd.CommandPath()
	// something like `rootcmd-subcmd1-subcmd2`
	dashCommandName := strings.Replace(commandName, " ", "-", -1)

	buf := new(bytes.Buffer)

	short := cmd.Short
	long := cmd.Long
	if len(long) == 0 {
		long = short
	}

	manPreamble(buf, header, commandName, short, long)
	manPrintOptions(buf, cmd)
	if len(cmd.Example) > 0 {
		fmt.Fprintf(buf, "# EXAMPLE\n")
		fmt.Fprintf(buf, "```\n%s\n```\n", cmd.Example)
	}
	if cmd.hasSeeAlso() {
		fmt.Fprintf(buf, "# SEE ALSO\n")
		if cmd.HasParent() {
			parentPath := cmd.Parent().CommandPath()
			dashParentPath := strings.Replace(parentPath, " ", "-", -1)
			fmt.Fprintf(buf, "**%s(%s)**, ", dashParentPath, header.Section)
			cmd.VisitParents(func(c *Command) {
				if c.DisableAutoGenTag {
					cmd.DisableAutoGenTag = c.DisableAutoGenTag
				}
			})
		}
		children := cmd.Commands()
		sort.Sort(byName(children))
		for _, c := range children {
			if !c.IsAvailableCommand() || c == cmd.helpCommand {
				continue
			}
			fmt.Fprintf(buf, "**%s-%s(%s)**, ", dashCommandName, c.Name(), header.Section)
		}
		fmt.Fprintf(buf, "\n")
	}
	if !cmd.DisableAutoGenTag {
		fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006"))
	}
	return buf.Bytes()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"

	"github.com/flynn/flynn-host/types"
	"github.com/flynn/go-dockerclient"
	"github.com/flynn/go-flynn/cluster"
)

func parseEnviron() map[string]string {
	env := os.Environ()
	res := make(map[string]string, len(env))
	for _, v := range env {
		kv := strings.SplitN(v, "=", 2)
		res[kv[0]] = kv[1]
	}
	return res
}

type ManifestData struct {
	ExternalIP string
	InternalIP string
	TCPPorts   []int
	Volumes    map[string]struct{}
	Env        map[string]string
	Services   map[string]*ManifestData

	readonly bool
	ports    <-chan int
}

func (m *ManifestData) TCPPort(id int) (int, error) {
	if m.readonly {
		return 0, fmt.Errorf("lorne: invalid TCPPort(%d), ManifestData is read-only", id)
	}
	if id < len(m.TCPPorts) {
		return m.TCPPorts[id], nil
	} else if id > len(m.TCPPorts) {
		return 0, fmt.Errorf("lorne: invalid TCPPort(%d), expecting id <= %d", len(m.TCPPorts))
	}

	port := <-m.ports
	m.TCPPorts = append(m.TCPPorts, port)
	return port, nil
}

func (m *ManifestData) Volume(v string) string {
	if m.Volumes == nil {
		m.Volumes = make(map[string]struct{})
	}
	m.Volumes[v] = struct{}{}
	return v
}

type manifestRunner struct {
	env        map[string]string
	externalIP string
	ports      <-chan int
	processor  interface {
		processJob(<-chan int, *host.Job) (*docker.Container, error)
	}
	docker interface {
		InspectContainer(string) (*docker.Container, error)
	}
}

type manifestService struct {
	Args []string          `json:"args"`
	Env  map[string]string `json:"env"`
}

func dockerEnv(m map[string]string) []string {
	res := make([]string, 0, len(m))
	for k, v := range m {
		res = append(res, k+"="+v)
	}
	return res
}

func (m *manifestRunner) runManifest(r io.Reader) (map[string]*ManifestData, error) {
	services := make(map[string]manifestService)
	if err := json.NewDecoder(r).Decode(&services); err != nil {
		return nil, err
	}

	serviceData := make(map[string]*ManifestData, len(services))
	for name, service := range services {
		data := &ManifestData{
			Env:        parseEnviron(),
			Services:   serviceData,
			ExternalIP: m.externalIP,
			ports:      m.ports,
		}
		var buf bytes.Buffer

		interp := func(s string) (string, error) {
			t, err := template.New("arg").Parse(s)
			if err != nil {
				return "", err
			}
			if err := t.Execute(&buf, data); err != nil {
				return "", err
			}
			defer buf.Reset()
			return buf.String(), nil
		}

		var err error
		for i, arg := range service.Args {
			service.Args[i], err = interp(arg)
			if err != nil {
				return nil, err
			}
		}
		for k, v := range service.Env {
			service.Env[k], err = interp(v)
			if err != nil {
				return nil, err
			}
		}
		data.Env = service.Env

		// Always include at least one port
		if len(data.TCPPorts) == 0 {
			data.TCPPorts = append(data.TCPPorts, <-m.ports)
		}

		image := name
		if !strings.Contains(image, "/") {
			image = "flynn/" + name
		}

		// Preload ports channel with the pre-allocated ports for this job
		ports := make(chan int, len(data.TCPPorts))
		for _, p := range data.TCPPorts {
			ports <- p
		}

		job := &host.Job{
			ID:       cluster.RandomJobID("flynn-" + name),
			TCPPorts: len(data.TCPPorts),
			Config: &docker.Config{
				Image:        image,
				Cmd:          service.Args,
				AttachStdout: true,
				AttachStderr: true,
				Env:          dockerEnv(data.Env),
				Volumes:      data.Volumes,
			},
		}

		container, err := m.processor.processJob(ports, job)
		if err != nil {
			return nil, err
		}
		container, err = m.docker.InspectContainer(container.ID)
		if err != nil {
			return nil, err
		}

		data.InternalIP = container.NetworkSettings.IPAddress
		data.readonly = true
		serviceData[name] = data
	}

	return serviceData, nil
}
<commit_msg>host: Fix fmt args typo<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"

	"github.com/flynn/flynn-host/types"
	"github.com/flynn/go-dockerclient"
	"github.com/flynn/go-flynn/cluster"
)

func parseEnviron() map[string]string {
	env := os.Environ()
	res := make(map[string]string, len(env))
	for _, v := range env {
		kv := strings.SplitN(v, "=", 2)
		res[kv[0]] = kv[1]
	}
	return res
}

type ManifestData struct {
	ExternalIP string
	InternalIP string
	TCPPorts   []int
	Volumes    map[string]struct{}
	Env        map[string]string
	Services   map[string]*ManifestData

	readonly bool
	ports    <-chan int
}

func (m *ManifestData) TCPPort(id int) (int, error) {
	if m.readonly {
		return 0, fmt.Errorf("lorne: invalid TCPPort(%d), ManifestData is read-only", id)
	}
	if id < len(m.TCPPorts) {
		return m.TCPPorts[id], nil
	} else if id > len(m.TCPPorts) {
		return 0, fmt.Errorf("lorne: invalid TCPPort(%d), expecting id <= %d", id, len(m.TCPPorts))
	}

	port := <-m.ports
	m.TCPPorts = append(m.TCPPorts, port)
	return port, nil
}

func (m *ManifestData) Volume(v string) string {
	if m.Volumes == nil {
		m.Volumes = make(map[string]struct{})
	}
	m.Volumes[v] = struct{}{}
	return v
}

type manifestRunner struct {
	env        map[string]string
	externalIP string
	ports      <-chan int
	processor  interface {
		processJob(<-chan int, *host.Job) (*docker.Container, error)
	}
	docker interface {
		InspectContainer(string) (*docker.Container, error)
	}
}

type manifestService struct {
	Args []string          `json:"args"`
	Env  map[string]string `json:"env"`
}

func dockerEnv(m map[string]string) []string {
	res := make([]string, 0, len(m))
	for k, v := range m {
		res = append(res, k+"="+v)
	}
	return res
}

func (m *manifestRunner) runManifest(r io.Reader) (map[string]*ManifestData, error) {
	services := make(map[string]manifestService)
	if err := json.NewDecoder(r).Decode(&services); err != nil {
		return nil, err
	}

	serviceData := make(map[string]*ManifestData, len(services))
	for name, service := range services {
		data := &ManifestData{
			Env:        parseEnviron(),
			Services:   serviceData,
			ExternalIP: m.externalIP,
			ports:      m.ports,
		}
		var buf bytes.Buffer

		interp := func(s string) (string, error) {
			t, err := template.New("arg").Parse(s)
			if err != nil {
				return "", err
			}
			if err := t.Execute(&buf, data); err != nil {
				return "", err
			}
			defer buf.Reset()
			return buf.String(), nil
		}

		var err error
		for i, arg := range service.Args {
			service.Args[i], err = interp(arg)
			if err != nil {
				return nil, err
			}
		}
		for k, v := range service.Env {
			service.Env[k], err = interp(v)
			if err != nil {
				return nil, err
			}
		}
		data.Env = service.Env

		// Always include at least one port
		if len(data.TCPPorts) == 0 {
			data.TCPPorts = append(data.TCPPorts, <-m.ports)
		}

		image := name
		if !strings.Contains(image, "/") {
			image = "flynn/" + name
		}

		// Preload ports channel with the pre-allocated ports for this job
		ports := make(chan int, len(data.TCPPorts))
		for _, p := range data.TCPPorts {
			ports <- p
		}

		job := &host.Job{
			ID:       cluster.RandomJobID("flynn-" + name),
			TCPPorts: len(data.TCPPorts),
			Config: &docker.Config{
				Image:        image,
				Cmd:          service.Args,
				AttachStdout: true,
				AttachStderr: true,
				Env:          dockerEnv(data.Env),
				Volumes:      data.Volumes,
			},
		}

		container, err := m.processor.processJob(ports, job)
		if err != nil {
			return nil, err
		}
		container, err = m.docker.InspectContainer(container.ID)
		if err != nil {
			return nil, err
		}

		data.InternalIP = container.NetworkSettings.IPAddress
		data.readonly = true
		serviceData[name] = data
	}

	return serviceData, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/spf13/pflag"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	clientgokubescheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/events"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	cliflag "k8s.io/component-base/cli/flag"
	componentbaseconfig "k8s.io/component-base/config"
	"k8s.io/klog"

	syncerappconfig "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/cmd/syncer/app/config"
	vcclient "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/client/clientset/versioned"
	vcinformers "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/client/informers/externalversions"
	syncerconfig "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/apis/config"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
)

// ResourceSyncerOptions is the main context object for the resource syncer.
type ResourceSyncerOptions struct {
	// The syncer configuration.
	ComponentConfig syncerconfig.SyncerConfiguration

	SuperMaster           string
	SuperMasterKubeconfig string

	Address  string
	Port     string
	CertFile string
	KeyFile  string
}

// NewResourceSyncerOptions creates a new resource syncer with a default config.
func NewResourceSyncerOptions() (*ResourceSyncerOptions, error) {
	return &ResourceSyncerOptions{
		ComponentConfig: syncerconfig.SyncerConfiguration{
			LeaderElection: syncerconfig.SyncerLeaderElectionConfiguration{
				LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
					LeaderElect:   true,
					LeaseDuration: v1.Duration{Duration: 15 * time.Second},
					RenewDeadline: v1.Duration{Duration: 10 * time.Second},
					RetryPeriod:   v1.Duration{Duration: 2 * time.Second},
					ResourceLock:  resourcelock.ConfigMapsResourceLock,
				},
				LockObjectName: "vc-syncer-leaderelection-lock",
			},
			ClientConnection:           componentbaseconfig.ClientConnectionConfiguration{},
			DisableServiceAccountToken: true,
			DefaultOpaqueMetaDomains:   []string{"kubernetes.io", "k8s.io"},
		},
		Address:  "",
		Port:     "80",
		CertFile: "",
		KeyFile:  "",
	}, nil
}

func (o *ResourceSyncerOptions) Flags() cliflag.NamedFlagSets {
	fss := cliflag.NamedFlagSets{}

	fs := fss.FlagSet("server")
	fs.StringVar(&o.SuperMaster, "super-master", o.SuperMaster, "The address of the super master Kubernetes API server (overrides any value in super-master-kubeconfig).")
	fs.StringVar(&o.ComponentConfig.ClientConnection.Kubeconfig, "super-master-kubeconfig", o.ComponentConfig.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
	fs.BoolVar(&o.ComponentConfig.DisableServiceAccountToken, "disable-service-account-token", o.ComponentConfig.DisableServiceAccountToken, "DisableServiceAccountToken indicates whether disable service account token automatically mounted.")

	serverFlags := fss.FlagSet("metricsServer")
	serverFlags.StringVar(&o.Address, "address", o.Address, "The server address.")
	serverFlags.StringVar(&o.Port, "port", o.Port, "The server port.")
	serverFlags.StringVar(&o.CertFile, "cert-file", o.CertFile, "CertFile is the file containing x509 Certificate for HTTPS.")
	serverFlags.StringVar(&o.KeyFile, "key-file", o.KeyFile, "KeyFile is the file containing x509 private key matching certFile.")

	BindFlags(&o.ComponentConfig.LeaderElection, fss.FlagSet("leader election"))

	return fss
}

// BindFlags binds the LeaderElectionConfiguration struct fields to a flagset
func BindFlags(l *syncerconfig.SyncerLeaderElectionConfiguration, fs *pflag.FlagSet) {
	fs.BoolVar(&l.LeaderElect, "leader-elect", l.LeaderElect, ""+
		"Start a leader election client and gain leadership before "+
		"executing the main loop. Enable this when running replicated "+
		"components for high availability.")
	fs.DurationVar(&l.LeaseDuration.Duration, "leader-elect-lease-duration", l.LeaseDuration.Duration, ""+
		"The duration that non-leader candidates will wait after observing a leadership "+
		"renewal until attempting to acquire leadership of a led but unrenewed leader "+
		"slot. This is effectively the maximum duration that a leader can be stopped "+
		"before it is replaced by another candidate. This is only applicable if leader "+
		"election is enabled.")
	fs.DurationVar(&l.RenewDeadline.Duration, "leader-elect-renew-deadline", l.RenewDeadline.Duration, ""+
		"The interval between attempts by the acting master to renew a leadership slot "+
		"before it stops leading. This must be less than or equal to the lease duration. "+
		"This is only applicable if leader election is enabled.")
	fs.DurationVar(&l.RetryPeriod.Duration, "leader-elect-retry-period", l.RetryPeriod.Duration, ""+
		"The duration the clients should wait between attempting acquisition and renewal "+
		"of a leadership. This is only applicable if leader election is enabled.")
	fs.StringVar(&l.ResourceLock, "leader-elect-resource-lock", l.ResourceLock, ""+
		"The type of resource object that is used for locking during "+
		"leader election. Supported options are `endpoints` and `configmaps` (default).")
	fs.StringVar(&l.LockObjectNamespace, "lock-object-namespace", l.LockObjectNamespace, "DEPRECATED: define the namespace of the lock object.")
	fs.StringVar(&l.LockObjectName, "lock-object-name", l.LockObjectName, "DEPRECATED: define the name of the lock object.")
}

// Config return a syncer config object
func (o *ResourceSyncerOptions) Config() (*syncerappconfig.Config, error) {
	c := &syncerappconfig.Config{}
	c.ComponentConfig = o.ComponentConfig

	// Prepare kube clients
	leaderElectionClient, virtualClusterClient, superMasterClient, err := createClients(c.ComponentConfig.ClientConnection, o.SuperMaster, c.ComponentConfig.LeaderElection.RenewDeadline.Duration)
	if err != nil {
		return nil, err
	}

	// Prepare event clients.
	eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: superMasterClient.EventsV1beta1().Events("")})
	recorder := eventBroadcaster.NewRecorder(clientgokubescheme.Scheme, constants.ResourceSyncerUserAgent)
	leaderElectionBroadcaster := record.NewBroadcaster()
	leaderElectionRecorder := leaderElectionBroadcaster.NewRecorder(clientgokubescheme.Scheme, corev1.EventSource{Component: constants.ResourceSyncerUserAgent})

	// Set up leader election if enabled.
	var leaderElectionConfig *leaderelection.LeaderElectionConfig
	if c.ComponentConfig.LeaderElection.LeaderElect {
		leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, leaderElectionRecorder)
		if err != nil {
			return nil, err
		}
	}

	c.SecretClient = superMasterClient.CoreV1()
	c.VirtualClusterClient = virtualClusterClient
	c.VirtualClusterInformer = vcinformers.NewSharedInformerFactory(virtualClusterClient, 0).Tenancy().V1alpha1().Virtualclusters()
	c.SuperMasterClient = superMasterClient
	c.SuperMasterInformerFactory = informers.NewSharedInformerFactory(superMasterClient, 0)
	c.Broadcaster = eventBroadcaster
	c.Recorder = recorder
	c.LeaderElectionClient = leaderElectionClient
	c.LeaderElection = leaderElectionConfig

	c.Address = o.Address
	c.Port = o.Port
	c.CertFile = o.CertFile
	c.KeyFile = o.KeyFile

	return c, nil
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(config syncerconfig.SyncerLeaderElectionConfiguration, client clientset.Interface, recorder record.EventRecorder) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := hostname + "_" + string(uuid.NewUUID())

	if config.LockObjectNamespace == "" {
		var err error
		config.LockObjectNamespace, err = getInClusterNamespace()
		if err != nil {
			return nil, fmt.Errorf("unable to find leader election namespace: %v", err)
		}
	}

	rl, err := resourcelock.New(config.ResourceLock,
		config.LockObjectNamespace,
		config.LockObjectName,
		client.CoreV1(),
		client.CoordinationV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: config.LeaseDuration.Duration,
		RenewDeadline: config.RenewDeadline.Duration,
		RetryPeriod:   config.RetryPeriod.Duration,
		WatchDog:      leaderelection.NewLeaderHealthzAdaptor(time.Second * 20),
		Name:          constants.ResourceSyncerUserAgent,
	}, nil
}

func getInClusterNamespace() (string, error) {
	// Check whether the namespace file exists.
	// If not, we are not running in cluster so can't guess the namespace.
	_, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
	if os.IsNotExist(err) {
		return "", fmt.Errorf("not running in-cluster, please specify LeaderElectionNamespace")
	} else if err != nil {
		return "", fmt.Errorf("error checking namespace file: %v", err)
	}

	// Load the namespace file and return its content
	namespace, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
	if err != nil {
		return "", fmt.Errorf("error reading namespace file: %v", err)
	}
	return string(namespace), nil
}

// createClients creates a meta cluster kube client and a super master custer client from the given config and masterOverride.
func createClients(config componentbaseconfig.ClientConnectionConfiguration, masterOverride string, timeout time.Duration) (clientset.Interface,
	vcclient.Interface, clientset.Interface, error) {
	// This creates a client, first loading any specified kubeconfig
	// file, and then overriding the Master flag, if non-empty.
	var (
		restConfig *restclient.Config
		err        error
	)
	if len(config.Kubeconfig) == 0 && len(masterOverride) == 0 {
		klog.Info("Neither kubeconfig file nor master URL was specified. Falling back to in-cluster config.")
		restConfig, err = rest.InClusterConfig()
	} else {
		// This creates a client, first loading any specified kubeconfig
		// file, and then overriding the Master flag, if non-empty.
		restConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
			&clientcmd.ClientConfigLoadingRules{ExplicitPath: config.Kubeconfig},
			&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterOverride}}).ClientConfig()
	}

	if err != nil {
		return nil, nil, nil, err
	}

	restConfig.ContentConfig.ContentType = config.AcceptContentTypes
	restConfig.QPS = config.QPS
	if restConfig.QPS == 0 {
		restConfig.QPS = constants.DefaultSyncerClientQPS
	}
	restConfig.Burst = int(config.Burst)
	if restConfig.Burst == 0 {
		restConfig.Burst = constants.DefaultSyncerClientBurst
	}

	superMasterClient, err := clientset.NewForConfig(restclient.AddUserAgent(restConfig, constants.ResourceSyncerUserAgent))
	if err != nil {
		return nil, nil, nil, err
	}

	virtualClusterClient, err := vcclient.NewForConfig(restConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	// shallow copy, do not modify the kubeConfig.Timeout.
	leaderElectionRestConfig := *restConfig
	restConfig.Timeout = timeout
	leaderElectionClient, err := clientset.NewForConfig(restclient.AddUserAgent(&leaderElectionRestConfig, "leader-election"))
	if err != nil {
		return nil, nil, nil, err
	}

	return leaderElectionClient, virtualClusterClient, superMasterClient, nil
}
<commit_msg>add cmd options 'default-opaque-meta-domains'<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/spf13/pflag"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	clientgokubescheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/events"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	cliflag "k8s.io/component-base/cli/flag"
	componentbaseconfig "k8s.io/component-base/config"
	"k8s.io/klog"

	syncerappconfig "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/cmd/syncer/app/config"
	vcclient "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/client/clientset/versioned"
	vcinformers "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/client/informers/externalversions"
	syncerconfig "github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/apis/config"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
)

// ResourceSyncerOptions is the main context object for the resource syncer.
type ResourceSyncerOptions struct {
	// The syncer configuration.
	ComponentConfig syncerconfig.SyncerConfiguration

	SuperMaster           string
	SuperMasterKubeconfig string

	Address  string
	Port     string
	CertFile string
	KeyFile  string
}

// NewResourceSyncerOptions creates a new resource syncer with a default config.
func NewResourceSyncerOptions() (*ResourceSyncerOptions, error) {
	return &ResourceSyncerOptions{
		ComponentConfig: syncerconfig.SyncerConfiguration{
			LeaderElection: syncerconfig.SyncerLeaderElectionConfiguration{
				LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
					LeaderElect:   true,
					LeaseDuration: v1.Duration{Duration: 15 * time.Second},
					RenewDeadline: v1.Duration{Duration: 10 * time.Second},
					RetryPeriod:   v1.Duration{Duration: 2 * time.Second},
					ResourceLock:  resourcelock.ConfigMapsResourceLock,
				},
				LockObjectName: "vc-syncer-leaderelection-lock",
			},
			ClientConnection:           componentbaseconfig.ClientConnectionConfiguration{},
			DisableServiceAccountToken: true,
			DefaultOpaqueMetaDomains:   []string{"kubernetes.io", "k8s.io"},
		},
		Address:  "",
		Port:     "80",
		CertFile: "",
		KeyFile:  "",
	}, nil
}

func (o *ResourceSyncerOptions) Flags() cliflag.NamedFlagSets {
	fss := cliflag.NamedFlagSets{}

	fs := fss.FlagSet("server")
	fs.StringVar(&o.SuperMaster, "super-master", o.SuperMaster, "The address of the super master Kubernetes API server (overrides any value in super-master-kubeconfig).")
	fs.StringVar(&o.ComponentConfig.ClientConnection.Kubeconfig, "super-master-kubeconfig", o.ComponentConfig.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
	fs.BoolVar(&o.ComponentConfig.DisableServiceAccountToken, "disable-service-account-token", o.ComponentConfig.DisableServiceAccountToken, "DisableServiceAccountToken indicates whether disable service account token automatically mounted.")
	fs.StringSliceVar(&o.ComponentConfig.DefaultOpaqueMetaDomains, "default-opaque-meta-domains", o.ComponentConfig.DefaultOpaqueMetaDomains, "DefaultOpaqueMetaDomains is the default opaque meta configuration for each Virtual Cluster.")

	serverFlags := fss.FlagSet("metricsServer")
	serverFlags.StringVar(&o.Address, "address", o.Address, "The server address.")
	serverFlags.StringVar(&o.Port, "port", o.Port, "The server port.")
	serverFlags.StringVar(&o.CertFile, "cert-file", o.CertFile, "CertFile is the file containing x509 Certificate for HTTPS.")
	serverFlags.StringVar(&o.KeyFile, "key-file", o.KeyFile, "KeyFile is the file containing x509 private key matching certFile.")

	BindFlags(&o.ComponentConfig.LeaderElection, fss.FlagSet("leader election"))

	return fss
}

// BindFlags binds the LeaderElectionConfiguration struct fields to a flagset
func BindFlags(l *syncerconfig.SyncerLeaderElectionConfiguration, fs *pflag.FlagSet) {
	fs.BoolVar(&l.LeaderElect, "leader-elect", l.LeaderElect, ""+
		"Start a leader election client and gain leadership before "+
		"executing the main loop. Enable this when running replicated "+
		"components for high availability.")
	fs.DurationVar(&l.LeaseDuration.Duration, "leader-elect-lease-duration", l.LeaseDuration.Duration, ""+
		"The duration that non-leader candidates will wait after observing a leadership "+
		"renewal until attempting to acquire leadership of a led but unrenewed leader "+
		"slot. This is effectively the maximum duration that a leader can be stopped "+
		"before it is replaced by another candidate. This is only applicable if leader "+
		"election is enabled.")
	fs.DurationVar(&l.RenewDeadline.Duration, "leader-elect-renew-deadline", l.RenewDeadline.Duration, ""+
		"The interval between attempts by the acting master to renew a leadership slot "+
		"before it stops leading. This must be less than or equal to the lease duration. "+
		"This is only applicable if leader election is enabled.")
	fs.DurationVar(&l.RetryPeriod.Duration, "leader-elect-retry-period", l.RetryPeriod.Duration, ""+
		"The duration the clients should wait between attempting acquisition and renewal "+
		"of a leadership. This is only applicable if leader election is enabled.")
	fs.StringVar(&l.ResourceLock, "leader-elect-resource-lock", l.ResourceLock, ""+
		"The type of resource object that is used for locking during "+
		"leader election. Supported options are `endpoints` and `configmaps` (default).")
	fs.StringVar(&l.LockObjectNamespace, "lock-object-namespace", l.LockObjectNamespace, "DEPRECATED: define the namespace of the lock object.")
	fs.StringVar(&l.LockObjectName, "lock-object-name", l.LockObjectName, "DEPRECATED: define the name of the lock object.")
}

// Config return a syncer config object
func (o *ResourceSyncerOptions) Config() (*syncerappconfig.Config, error) {
	c := &syncerappconfig.Config{}
	c.ComponentConfig = o.ComponentConfig

	// Prepare kube clients
	leaderElectionClient, virtualClusterClient, superMasterClient, err := createClients(c.ComponentConfig.ClientConnection, o.SuperMaster, c.ComponentConfig.LeaderElection.RenewDeadline.Duration)
	if err != nil {
		return nil, err
	}

	// Prepare event clients.
	eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: superMasterClient.EventsV1beta1().Events("")})
	recorder := eventBroadcaster.NewRecorder(clientgokubescheme.Scheme, constants.ResourceSyncerUserAgent)
	leaderElectionBroadcaster := record.NewBroadcaster()
	leaderElectionRecorder := leaderElectionBroadcaster.NewRecorder(clientgokubescheme.Scheme, corev1.EventSource{Component: constants.ResourceSyncerUserAgent})

	// Set up leader election if enabled.
	var leaderElectionConfig *leaderelection.LeaderElectionConfig
	if c.ComponentConfig.LeaderElection.LeaderElect {
		leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, leaderElectionRecorder)
		if err != nil {
			return nil, err
		}
	}

	c.SecretClient = superMasterClient.CoreV1()
	c.VirtualClusterClient = virtualClusterClient
	c.VirtualClusterInformer = vcinformers.NewSharedInformerFactory(virtualClusterClient, 0).Tenancy().V1alpha1().Virtualclusters()
	c.SuperMasterClient = superMasterClient
	c.SuperMasterInformerFactory = informers.NewSharedInformerFactory(superMasterClient, 0)
	c.Broadcaster = eventBroadcaster
	c.Recorder = recorder
	c.LeaderElectionClient = leaderElectionClient
	c.LeaderElection = leaderElectionConfig

	c.Address = o.Address
	c.Port = o.Port
	c.CertFile = o.CertFile
	c.KeyFile = o.KeyFile

	return c, nil
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(config syncerconfig.SyncerLeaderElectionConfiguration, client clientset.Interface, recorder record.EventRecorder) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := hostname + "_" + string(uuid.NewUUID())

	if config.LockObjectNamespace == "" {
		var err error
		config.LockObjectNamespace, err = getInClusterNamespace()
		if err != nil {
			return nil, fmt.Errorf("unable to find leader election namespace: %v", err)
		}
	}

	rl, err := resourcelock.New(config.ResourceLock,
		config.LockObjectNamespace,
		config.LockObjectName,
		client.CoreV1(),
		client.CoordinationV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: config.LeaseDuration.Duration,
		RenewDeadline: config.RenewDeadline.Duration,
		RetryPeriod:   config.RetryPeriod.Duration,
		WatchDog:      leaderelection.NewLeaderHealthzAdaptor(time.Second * 20),
		Name:          constants.ResourceSyncerUserAgent,
	}, nil
}

func getInClusterNamespace() (string, error) {
	// Check whether the namespace file exists.
	// If not, we are not running in cluster so can't guess the namespace.
	_, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
	if os.IsNotExist(err) {
		return "", fmt.Errorf("not running in-cluster, please specify LeaderElectionNamespace")
	} else if err != nil {
		return "", fmt.Errorf("error checking namespace file: %v", err)
	}

	// Load the namespace file and return its content
	namespace, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
	if err != nil {
		return "", fmt.Errorf("error reading namespace file: %v", err)
	}
	return string(namespace), nil
}

// createClients creates a meta cluster kube client and a super master custer client from the given config and masterOverride.
func createClients(config componentbaseconfig.ClientConnectionConfiguration, masterOverride string, timeout time.Duration) (clientset.Interface,
	vcclient.Interface, clientset.Interface, error) {
	// This creates a client, first loading any specified kubeconfig
	// file, and then overriding the Master flag, if non-empty.
	var (
		restConfig *restclient.Config
		err        error
	)
	if len(config.Kubeconfig) == 0 && len(masterOverride) == 0 {
		klog.Info("Neither kubeconfig file nor master URL was specified. Falling back to in-cluster config.")
		restConfig, err = rest.InClusterConfig()
	} else {
		// This creates a client, first loading any specified kubeconfig
		// file, and then overriding the Master flag, if non-empty.
		restConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
			&clientcmd.ClientConfigLoadingRules{ExplicitPath: config.Kubeconfig},
			&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterOverride}}).ClientConfig()
	}

	if err != nil {
		return nil, nil, nil, err
	}

	restConfig.ContentConfig.ContentType = config.AcceptContentTypes
	restConfig.QPS = config.QPS
	if restConfig.QPS == 0 {
		restConfig.QPS = constants.DefaultSyncerClientQPS
	}
	restConfig.Burst = int(config.Burst)
	if restConfig.Burst == 0 {
		restConfig.Burst = constants.DefaultSyncerClientBurst
	}

	superMasterClient, err := clientset.NewForConfig(restclient.AddUserAgent(restConfig, constants.ResourceSyncerUserAgent))
	if err != nil {
		return nil, nil, nil, err
	}

	virtualClusterClient, err := vcclient.NewForConfig(restConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	// shallow copy, do not modify the kubeConfig.Timeout.
	leaderElectionRestConfig := *restConfig
	restConfig.Timeout = timeout
	leaderElectionClient, err := clientset.NewForConfig(restclient.AddUserAgent(&leaderElectionRestConfig, "leader-election"))
	if err != nil {
		return nil, nil, nil, err
	}

	return leaderElectionClient, virtualClusterClient, superMasterClient, nil
}
<|endoftext|>