<commit_before>package main

import "github.com/codegangsta/cli"

var (

	// Main flags
	flURL = cli.StringFlag{
		Name:   "url, u",
		Usage:  "Sets the URL to use",
		EnvVar: "PULSE_URL",
		Value:  "http://localhost:8181",
	}
	flRunning = cli.BoolFlag{
		Name:  "running",
		Usage: "Shows running plugins",
	}

	// Plugin flags
	flPluginAsc = cli.StringFlag{
		Name:  "plugin-asc, a",
		Usage: "The plugin asc",
	}
	flPluginType = cli.StringFlag{
		Name:  "plugin-type, t",
		Usage: "The plugin type",
	}
	flPluginName = cli.StringFlag{
		Name:  "plugin-name, n",
		Usage: "The plugin name",
	}
	flPluginVersion = cli.IntFlag{
		Name:  "plugin-version, v",
		Usage: "The plugin version",
	}

	// Task flags
	flTaskName = cli.StringFlag{
		Name:  "name, n",
		Usage: "Optional requirement for giving task names",
		Value: "",
	}
	flTaskManifest = cli.StringFlag{
		Name:  "task-manifest, t",
		Usage: "File path for task manifest to use for task creation.",
	}

	flWorkfowManifest = cli.StringFlag{
		Name:  "workflow-manifest, w",
		Usage: "File path for workflow manifest to use for task creation",
	}

	flTaskSchedInterval = cli.StringFlag{
		Name:  "interval, i",
		Usage: "Interval for the task schedule [ex: 250ms, 1s, 30m]",
	}

	flTaskSchedStartTime = cli.StringFlag{
		Name:  "start-time",
		Usage: "Start time for the task schedule [defaults to now]",
	}
	flTaskSchedStopTime = cli.StringFlag{
		Name:  "stop-time",
		Usage: "Start time for the task schedule [defaults to now]",
	}

	flTaskSchedStartDate = cli.StringFlag{
		Name:  "start-date",
		Usage: "Start date for the task schedule [defaults to today]",
	}
	flTaskSchedStopDate = cli.StringFlag{
		Name:  "stop-date",
		Usage: "Stop date for the task schedule [defaults to today]",
	}
	flTaskSchedDuration = cli.StringFlag{
		Name:  "duration, d",
		Usage: "The amount of time to run the task [appends to start or creates a start time before a stop]",
	}
	flTaskSchedNoStart = cli.BoolFlag{
		Name:  "--no-start",
		Usage: "Do not start task on creation [normally started on creation]",
	}

	// metric
	flMetricVersion = cli.IntFlag{
		Name:  "metric-version, v",
		Usage: "The metric version",
	}
	flMetricNamespace = cli.StringFlag{
		Name:  "metric-namespace, m",
		Usage: "A metric namespace",
	}
)
<commit_msg>Remove -- from no-start flag in order for it to work properly<commit_after>package main

import "github.com/codegangsta/cli"

var (

	// Main flags
	flURL = cli.StringFlag{
		Name:   "url, u",
		Usage:  "Sets the URL to use",
		EnvVar: "PULSE_URL",
		Value:  "http://localhost:8181",
	}
	flRunning = cli.BoolFlag{
		Name:  "running",
		Usage: "Shows running plugins",
	}

	// Plugin flags
	flPluginAsc = cli.StringFlag{
		Name:  "plugin-asc, a",
		Usage: "The plugin asc",
	}
	flPluginType = cli.StringFlag{
		Name:  "plugin-type, t",
		Usage: "The plugin type",
	}
	flPluginName = cli.StringFlag{
		Name:  "plugin-name, n",
		Usage: "The plugin name",
	}
	flPluginVersion = cli.IntFlag{
		Name:  "plugin-version, v",
		Usage: "The plugin version",
	}

	// Task flags
	flTaskName = cli.StringFlag{
		Name:  "name, n",
		Usage: "Optional requirement for giving task names",
		Value: "",
	}
	flTaskManifest = cli.StringFlag{
		Name:  "task-manifest, t",
		Usage: "File path for task manifest to use for task creation.",
	}

	flWorkfowManifest = cli.StringFlag{
		Name:  "workflow-manifest, w",
		Usage: "File path for workflow manifest to use for task creation",
	}

	flTaskSchedInterval = cli.StringFlag{
		Name:  "interval, i",
		Usage: "Interval for the task schedule [ex: 250ms, 1s, 30m]",
	}

	flTaskSchedStartTime = cli.StringFlag{
		Name:  "start-time",
		Usage: "Start time for the task schedule [defaults to now]",
	}
	flTaskSchedStopTime = cli.StringFlag{
		Name:  "stop-time",
		Usage: "Start time for the task schedule [defaults to now]",
	}

	flTaskSchedStartDate = cli.StringFlag{
		Name:  "start-date",
		Usage: "Start date for the task schedule [defaults to today]",
	}
	flTaskSchedStopDate = cli.StringFlag{
		Name:  "stop-date",
		Usage: "Stop date for the task schedule [defaults to today]",
	}
	flTaskSchedDuration = cli.StringFlag{
		Name:  "duration, d",
		Usage: "The amount of time to run the task [appends to start or creates a start time before a stop]",
	}
	flTaskSchedNoStart = cli.BoolFlag{
		Name:  "no-start",
		Usage: "Do not start task on creation [normally started on creation]",
	}

	// metric
	flMetricVersion = cli.IntFlag{
		Name:  "metric-version, v",
		Usage: "The metric version",
	}
	flMetricNamespace = cli.StringFlag{
		Name:  "metric-namespace, m",
		Usage: "A metric namespace",
	}
)
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"regexp"

	"github.com/ssh-vault/crypto"
	"github.com/ssh-vault/crypto/aead"
	sv "github.com/ssh-vault/ssh-vault"
)

var version string

func exit1(err error) {
	fmt.Fprintf(os.Stderr, "%s", err)
	os.Exit(1)
}

func main() {
	var (
		f             = flag.Bool("f", false, "Print ssh key `fingerprint` or create a vault using the key matching the specified fingerprint, example:\n            echo \"secret\" | ssh-vault -u <user> -f 00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff create")
		k             = flag.String("k", "~/.ssh/id_rsa.pub", "Public `ssh key or index` when using option -u")
		o             = flag.String("o", "", "Write output to `file` instead of stdout. Only for option view, example:\n            ssh-vault -o /tmp/out.txt view vault.ssh")
		u             = flag.String("u", "", "GitHub `username or URL`, optional [-k N] where N is the key index to use, example:\n            ssh-vault -u <user> create      # Using first key found in github.com/<user>.keys\n            ssh-vault -u <user> -k 2 create # Using second key")
		v             = flag.Bool("v", false, fmt.Sprintf("Print version: %s", version))
		options       = []string{"create", "edit", "view"}
		rxFingerprint = regexp.MustCompile(`^([0-9a-f]{2}[:-]){15}([0-9a-f]{2})$`)
		err           error
		fingerprint   string
		option        string
		outFile       string
	)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-f fingerprint] [-k key] [-o file] [-u user] [create|edit|view] vault\n\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n\n",
			os.Args[0],
			"  Options:",
			"    create    Creates a new vault, if no vault defined outputs to stdout.",
			"              Can read from stdin, example:",
			"                  echo \"secret\" | ssh-vault -u <user> create",
			"    edit      Edit an existing vault.",
			"    view      View an existing vault, can read from stdin, example:",
			"                  echo \"SSH-VAULT...\" | ssh-vault view",
			"    vault     Path off the file where the output will be written, example: vault.ssh",
		)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *v {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	// only print fingerprint
	if flag.NArg() < 1 && !*f {
		exit1(fmt.Errorf("Missing option, use (\"%s -h\") for help.", os.Args[0]))
	}

	// set option to be the first argument if no -f <fingerprint> is defined
	option = flag.Arg(0)
	outFile = flag.Arg(1)

	// using -f with fingerprint
	if *f {
		if flag.NArg() == 1 {
			exit1(fmt.Errorf("Missing fingerprint/option, use (\"%s -h\") for help.", os.Args[0]))
		}
		if flag.NArg() >= 1 {
			if !rxFingerprint.Match([]byte(flag.Arg(0))) {
				exit1(fmt.Errorf("Bad fingerprint format, use (\"%s -h\") for help.", os.Args[0]))
			}
			if flag.Arg(1) != "create" {
				exit1(fmt.Errorf("-f fingerprint can only be used with the %q option, use (\"%s -h\") for help.", "create", os.Args[0]))
			}
			// create using fingerprint
			*f = false
			fingerprint = flag.Arg(0)
			option = flag.Arg(1)
			outFile = flag.Arg(2)

			flagset := make(map[string]bool)
			flag.Visit(func(f *flag.Flag) { flagset[f.Name] = true })
			if flagset["k"] {
				exit1(fmt.Errorf("-f fingerprint have no effect when specifying key using -k, use (\"%s -h\") for help.", os.Args[0]))
			}
		}
	}

	usr, _ := user.Current()
	if len(*k) > 2 {
		if (*k)[:2] == "~/" {
			*k = filepath.Join(usr.HomeDir, (*k)[2:])
		}
	}

	vault, err := sv.New(fingerprint, *k, *u, option, outFile)
	if err != nil {
		exit1(err)
	}

	// ssh-keygen -f id_rsa.pub -e -m PKCS8
	PKCS8, err := vault.PKCS8()
	if err != nil {
		exit1(err)
	}

	vault.PublicKey, err = vault.GetRSAPublicKey(PKCS8)
	if err != nil {
		exit1(err)
	}
	vault.Fingerprint, err = vault.GenFingerprint(PKCS8)
	if err != nil {
		exit1(err)
	}

	if *f {
		fmt.Printf("%s\n", vault.Fingerprint)
		os.Exit(0)
	}

	// check options
	exit := true
	for _, v := range options {
		if option == v {
			exit = false
			break
		}
	}
	if exit {
		exit1(fmt.Errorf("Invalid option, use (\"%s -h\") for help.\n", os.Args[0]))
	}

	vault.Password, err = crypto.GenerateNonce(32)
	if err != nil {
		exit1(err)
	}

	switch option {
	case "create":
		data, err := vault.Create()
		if err != nil {
			exit1(err)
		}
		out, err := aead.Encrypt(vault.Password, data, []byte(vault.Fingerprint))
		if err != nil {
			exit1(err)
		}
		err = vault.Close(out)
		if err != nil {
			exit1(err)
		}
	case "edit":
		data, err := vault.View()
		if err != nil {
			exit1(fmt.Errorf("Missing vault name, use (\"%s -h\") for help.\n", os.Args[0]))
		}
		out, err := vault.Edit(data)
		if err != nil {
			exit1(err)
		}
		out, err = aead.Encrypt(vault.Password, out, []byte(vault.Fingerprint))
		if err != nil {
			exit1(err)
		}
		err = vault.Close(out)
		if err != nil {
			exit1(err)
		}
	case "view":
		out, err := vault.View()
		if err != nil {
			exit1(err)
		}
		if *o != "" {
			if err := ioutil.WriteFile(*o, out, 0600); err != nil {
				exit1(err)
			}
		} else {
			fmt.Printf("%s", out)
		}
	}
}
<commit_msg>println os.Stderr<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"regexp"

	"github.com/ssh-vault/crypto"
	"github.com/ssh-vault/crypto/aead"
	sv "github.com/ssh-vault/ssh-vault"
)

var version string

func exit1(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func main() {
	var (
		f             = flag.Bool("f", false, "Print ssh key `fingerprint` or create a vault using the key matching the specified fingerprint, example:\n            echo \"secret\" | ssh-vault -u <user> -f 00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff create")
		k             = flag.String("k", "~/.ssh/id_rsa.pub", "Public `ssh key or index` when using option -u")
		o             = flag.String("o", "", "Write output to `file` instead of stdout. Only for option view, example:\n            ssh-vault -o /tmp/out.txt view vault.ssh")
		u             = flag.String("u", "", "GitHub `username or URL`, optional [-k N] where N is the key index to use, example:\n            ssh-vault -u <user> create      # Using first key found in github.com/<user>.keys\n            ssh-vault -u <user> -k 2 create # Using second key")
		v             = flag.Bool("v", false, fmt.Sprintf("Print version: %s", version))
		options       = []string{"create", "edit", "view"}
		rxFingerprint = regexp.MustCompile(`^([0-9a-f]{2}[:-]){15}([0-9a-f]{2})$`)
		err           error
		fingerprint   string
		option        string
		outFile       string
	)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-f fingerprint] [-k key] [-o file] [-u user] [create|edit|view] vault\n\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n\n",
			os.Args[0],
			"  Options:",
			"    create    Creates a new vault, if no vault defined outputs to stdout.",
			"              Can read from stdin, example:",
			"                  echo \"secret\" | ssh-vault -u <user> create",
			"    edit      Edit an existing vault.",
			"    view      View an existing vault, can read from stdin, example:",
			"                  echo \"SSH-VAULT...\" | ssh-vault view",
			"    vault     Path off the file where the output will be written, example: vault.ssh",
		)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *v {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	// only print fingerprint
	if flag.NArg() < 1 && !*f {
		exit1(fmt.Errorf("Missing option, use (\"%s -h\") for help.", os.Args[0]))
	}

	// set option to be the first argument if no -f <fingerprint> is defined
	option = flag.Arg(0)
	outFile = flag.Arg(1)

	// using -f with fingerprint
	if *f {
		if flag.NArg() == 1 {
			exit1(fmt.Errorf("Missing fingerprint/option, use (\"%s -h\") for help.", os.Args[0]))
		}
		if flag.NArg() >= 1 {
			if !rxFingerprint.Match([]byte(flag.Arg(0))) {
				exit1(fmt.Errorf("Bad fingerprint format, use (\"%s -h\") for help.", os.Args[0]))
			}
			if flag.Arg(1) != "create" {
				exit1(fmt.Errorf("-f fingerprint can only be used with the %q option, use (\"%s -h\") for help.", "create", os.Args[0]))
			}
			// create using fingerprint
			*f = false
			fingerprint = flag.Arg(0)
			option = flag.Arg(1)
			outFile = flag.Arg(2)

			flagset := make(map[string]bool)
			flag.Visit(func(f *flag.Flag) { flagset[f.Name] = true })
			if flagset["k"] {
				exit1(fmt.Errorf("-f fingerprint have no effect when specifying key using -k, use (\"%s -h\") for help.", os.Args[0]))
			}
		}
	}

	usr, _ := user.Current()
	if len(*k) > 2 {
		if (*k)[:2] == "~/" {
			*k = filepath.Join(usr.HomeDir, (*k)[2:])
		}
	}

	vault, err := sv.New(fingerprint, *k, *u, option, outFile)
	if err != nil {
		exit1(err)
	}

	// ssh-keygen -f id_rsa.pub -e -m PKCS8
	PKCS8, err := vault.PKCS8()
	if err != nil {
		exit1(err)
	}

	vault.PublicKey, err = vault.GetRSAPublicKey(PKCS8)
	if err != nil {
		exit1(err)
	}
	vault.Fingerprint, err = vault.GenFingerprint(PKCS8)
	if err != nil {
		exit1(err)
	}

	if *f {
		fmt.Printf("%s\n", vault.Fingerprint)
		os.Exit(0)
	}

	// check options
	exit := true
	for _, v := range options {
		if option == v {
			exit = false
			break
		}
	}
	if exit {
		exit1(fmt.Errorf("Invalid option, use (\"%s -h\") for help.\n", os.Args[0]))
	}

	vault.Password, err = crypto.GenerateNonce(32)
	if err != nil {
		exit1(err)
	}

	switch option {
	case "create":
		data, err := vault.Create()
		if err != nil {
			exit1(err)
		}
		out, err := aead.Encrypt(vault.Password, data, []byte(vault.Fingerprint))
		if err != nil {
			exit1(err)
		}
		err = vault.Close(out)
		if err != nil {
			exit1(err)
		}
	case "edit":
		data, err := vault.View()
		if err != nil {
			exit1(fmt.Errorf("Missing vault name, use (\"%s -h\") for help.\n", os.Args[0]))
		}
		out, err := vault.Edit(data)
		if err != nil {
			exit1(err)
		}
		out, err = aead.Encrypt(vault.Password, out, []byte(vault.Fingerprint))
		if err != nil {
			exit1(err)
		}
		err = vault.Close(out)
		if err != nil {
			exit1(err)
		}
	case "view":
		out, err := vault.View()
		if err != nil {
			exit1(err)
		}
		if *o != "" {
			if err := ioutil.WriteFile(*o, out, 0600); err != nil {
				exit1(err)
			}
		} else {
			fmt.Printf("%s", out)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"sync"
	"time"

	"github.com/Impyy/tox4go/bootstrap"
	"github.com/Impyy/tox4go/crypto"
	"github.com/Impyy/tox4go/dht"
	"github.com/Impyy/tox4go/dht/ping"
	"github.com/Impyy/tox4go/relay"
	"github.com/Impyy/tox4go/transport"
	"github.com/didip/tollbooth"
)

const (
	enableIpv6  = true
	probeRate   = 1 * time.Minute
	refreshRate = 5 * time.Minute
)

var (
	lastScan    int64
	lastRefresh int64
	nodes       = []*toxNode{}
	nodesMutex  = sync.Mutex{}
	tcpPorts    = []int{443, 3389, 33445}
)

func main() {
	if parseFlags() {
		return
	}

	// load state if available
	state, err := loadState()
	if err != nil {
		log.Fatalf("error loading state: %s", err.Error())
	}
	lastScan = state.LastScan
	lastRefresh = state.LastRefresh
	nodes = state.Nodes

	if err := loadCountries(); err != nil {
		log.Fatalf("error loading countries.json: %s", err.Error())
	}

	inst, err := NewInstance(":33450")
	if err != nil {
		log.Fatalf("fatal: %s", err.Error())
	}
	inst.UDPTransport.Handle(dht.PacketIDSendNodes, inst.handleSendNodesPacket)
	inst.UDPTransport.Handle(bootstrap.PacketIDBootstrapInfo, handleBootstrapInfoPacket)

	//handle stop signal
	interruptChan := make(chan os.Signal)
	signal.Notify(interruptChan, os.Interrupt)

	//setup http server
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", httpListenPort))
	if err != nil {
		log.Fatalf("error in net.Listen: %s", err.Error())
	}
	limiter := tollbooth.NewLimiter(1, 2*time.Second)
	limiter.Methods = []string{"POST"}
	limiter.IPLookups = []string{"X-Forwarded-For", "RemoteAddr", "X-Real-IP"}
	serveMux := http.NewServeMux()
	serveMux.HandleFunc("/", handleHTTPRequest)
	serveMux.Handle("/test", tollbooth.LimitFuncHandler(limiter, handleHTTPRequest))
	serveMux.HandleFunc("/json", handleJSONRequest)
	go func() {
		err := http.Serve(listener, serveMux)
		if err != nil {
			log.Printf("http server error: %s\n", err.Error())
			interruptChan <- os.Interrupt
		}
	}()

	//listen for tox packets
	go func() {
		err := inst.UDPTransport.Listen()
		if err != nil {
			log.Printf("udp transport error: %s\n", err.Error())
			interruptChan <- os.Interrupt
		}
	}()
	//go tcpTransport.Listen()

	err = refreshNodes()
	if err != nil {
		log.Fatal(err.Error())
	}
	inst.probeNodes()

	probeTicker := time.NewTicker(probeRate)
	refreshTicker := time.NewTicker(refreshRate)
	updateTicker := time.NewTicker(30 * time.Second)
	run := true

	for run {
		select {
		case <-interruptChan:
			fmt.Printf("killing routines\n")
			probeTicker.Stop()
			refreshTicker.Stop()
			updateTicker.Stop()
			inst.UDPTransport.Stop()
			//tcpTransport.Stop()
			listener.Close()
			run = false
		case <-probeTicker.C:
			// we want an empty ping list at the start of every probe
			inst.PingsMutex.Lock()
			inst.Pings.Clear(false)
			inst.PingsMutex.Unlock()

			nodesMutex.Lock()
			err := inst.probeNodes()
			nodesMutex.Unlock()
			if err != nil {
				log.Printf("error while trying to probe nodes: %s", err.Error())
			}
		case <-refreshTicker.C:
			err := refreshNodes()
			if err != nil {
				log.Printf("error while trying to refresh nodes: %s", err.Error())
			}
		case <-updateTicker.C:
			inst.PingsMutex.Lock()
			inst.Pings.Clear(true)
			inst.PingsMutex.Unlock()

			nodesMutex.Lock()
			for _, node := range nodes {
				if time.Now().Sub(time.Unix(node.LastPing, 0)) > time.Minute*2 {
					node.UDPStatus = false
				}
			}
			sort.Stable(nodeSlice(nodes))

			state := getState()
			saveState(state)
			nodesMutex.Unlock()
		}
	}
}

func refreshNodes() error {
	parsedNodes, err := parseNodes()
	if err != nil {
		return err
	}

	nodesMutex.Lock()
	for _, freshNode := range parsedNodes {
		for _, node := range nodes {
			if freshNode.PublicKey == node.PublicKey {
				freshNode.LastPing = node.LastPing
				freshNode.UDPStatus = node.UDPStatus
				freshNode.TCPStatus = node.TCPStatus
				freshNode.TCPPorts = node.TCPPorts
				freshNode.MOTD = node.MOTD
				freshNode.Version = node.Version
				break
			}
		}
	}
	nodes = parsedNodes
	sort.Stable(nodeSlice(nodes))
	nodesMutex.Unlock()

	lastRefresh = time.Now().Unix()
	return nil
}

func (i *instance) probeNodes() error {
	for _, node := range nodes {
		err := i.getBootstrapInfo(node)
		if err != nil {
			fmt.Println(err.Error())
		}

		p, err := i.getNodes(node)
		if err != nil {
			fmt.Println(err.Error())
		} else {
			i.PingsMutex.Lock()
			err = i.Pings.Add(p)
			if err != nil {
				fmt.Println(err.Error())
			}
			i.PingsMutex.Unlock()
		}

		ports := tcpPorts
		exists := false
		for _, i := range ports {
			if i == node.Port {
				exists = true
			}
		}
		if !exists {
			ports = append(ports, node.Port)
		}

		go i.probeNodeTCPPorts(node, ports)
	}

	lastScan = time.Now().Unix()
	return nil
}

func (i *instance) probeNodeTCPPorts(node *toxNode, ports []int) {
	c := make(chan int)
	for _, port := range ports {
		go func(p int) {
			conn, err := connectTCP(node, p)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- -1
				return
			}

			err = i.tcpHandshake(node, conn)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- -1
			} else {
				c <- p
			}
			conn.Close()
		}(port)
	}

	nodesMutex.Lock()
	node.TCPPorts = []int{}

	for i := 0; i < len(ports); i++ {
		port := <-c
		if port != -1 {
			fmt.Printf("tcp port for %s: %d\n", node.Maintainer, port)
			node.TCPPorts = append(node.TCPPorts, port)
		}
	}
	if len(node.TCPPorts) > 0 {
		node.LastPing = time.Now().Unix()
	}
	node.TCPStatus = len(node.TCPPorts) > 0
	sort.Stable(nodeSlice(nodes))
	nodesMutex.Unlock()
}

func (i *instance) tcpHandshake(node *toxNode, conn *net.TCPConn) error {
	nodePublicKey := new([crypto.PublicKeySize]byte)
	decPublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return err
	}
	copy(nodePublicKey[:], decPublicKey)

	relayConn, err := relay.NewConnection()
	if err != nil {
		return err
	}

	req, err := relayConn.StartHandshake()
	if err != nil {
		return err
	}

	reqBytes, err := req.MarshalBinary()
	if err != nil {
		return err
	}

	encryptedReqBytes, nonce, err := i.Ident.EncryptBlob(reqBytes, nodePublicKey)
	if err != nil {
		return err
	}

	reqPacket := &relay.HandshakeRequestPacket{
		PublicKey: i.Ident.PublicKey,
		Nonce:     nonce,
		Payload:   encryptedReqBytes,
	}

	reqPacketBytes, err := reqPacket.MarshalBinary()
	if err != nil {
		return err
	}

	conn.SetDeadline(time.Now().Add(2 * time.Second))
	_, err = conn.Write(reqPacketBytes)
	if err != nil {
		return err
	}

	buffer := make([]byte, 96)
	left := len(buffer)
	for left > 0 {
		read, readErr := conn.Read(buffer[len(buffer)-left:])
		if readErr != nil {
			return readErr
		}
		left -= read
	}

	res := relay.HandshakeResponsePacket{}
	err = res.UnmarshalBinary(buffer)
	if err != nil {
		return err
	}

	decryptedBytes, err := i.Ident.DecryptBlob(res.Payload, nodePublicKey, res.Nonce)
	if err != nil {
		return err
	}

	resPacket := &relay.HandshakePayload{}
	err = resPacket.UnmarshalBinary(decryptedBytes)
	if err != nil {
		return err
	}

	return relayConn.EndHandshake(resPacket)
}

func (i *instance) getNodes(node *toxNode) (*ping.Ping, error) {
	nodePublicKey := new([crypto.PublicKeySize]byte)
	decPublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return nil, err
	}
	copy(nodePublicKey[:], decPublicKey)

	p, err := ping.NewPing(nodePublicKey)
	if err != nil {
		return nil, err
	}

	packet := &dht.GetNodesPacket{
		PublicKey: i.Ident.PublicKey,
		PingID:    p.ID,
	}

	dhtPacket, err := i.Ident.EncryptPacket(transport.Packet(packet), nodePublicKey)
	if err != nil {
		return nil, err
	}

	payload, err := dhtPacket.MarshalBinary()
	if err != nil {
		return nil, err
	}

	if err := i.sendToUDP(payload, node); err != nil {
		return nil, err
	}

	return p, nil
}

func (i *instance) getBootstrapInfo(node *toxNode) error {
	packet, err := bootstrap.ConstructPacket(&bootstrap.InfoRequestPacket{})
	if err != nil {
		return err
	}

	payload, err := packet.MarshalBinary()
	if err != nil {
		return err
	}

	return i.sendToUDP(payload, node)
}

func (i *instance) sendToUDP(data []byte, node *toxNode) error {
	ip, err := getNodeIP(node)
	if err != nil {
		return err
	}

	return i.UDPTransport.Send(
		&transport.Message{
			Data: data,
			Addr: &net.UDPAddr{
				IP:   ip,
				Port: node.Port,
			},
		},
	)
}

func getNodeIP(node *toxNode) (net.IP, error) {
	if node.ip4 != nil {
		return node.ip4, nil
	} else if enableIpv6 && node.ip6 != nil {
		return node.ip6, nil
	}

	return nil, fmt.Errorf("no valid ip found for %s", node.Maintainer)
}

func connectTCP(node *toxNode, port int) (*net.TCPConn, error) {
	ip, err := getNodeIP(node)
	if err != nil {
		return nil, err
	}

	dialer := net.Dialer{}
	dialer.Deadline = time.Now().Add(2 * time.Second)

	tempConn, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		return nil, err
	}

	conn, ok := tempConn.(*net.TCPConn)
	if !ok {
		return nil, errors.New("not a tcp conn")
	}

	return conn, nil
}

func (i *instance) handleSendNodesPacket(msg *transport.Message) error {
	dhtPacket := &dht.Packet{}
	err := dhtPacket.UnmarshalBinary(msg.Data)
	if err != nil {
		return err
	}

	decryptedPacket, err := i.Ident.DecryptPacket(dhtPacket)
	if err != nil {
		return err
	}

	packet, ok := decryptedPacket.(*dht.SendNodesPacket)
	if !ok {
		return nil
	}

	i.PingsMutex.Lock()
	nodesMutex.Lock()
	if i.Pings.Find(dhtPacket.SenderPublicKey, packet.PingID, true) != nil {
		for _, node := range nodes {
			publicKey, err := hex.DecodeString(node.PublicKey)
			if err != nil {
				continue
			}

			if bytes.Equal(publicKey, dhtPacket.SenderPublicKey[:]) {
				node.UDPStatus = true
				node.LastPing = time.Now().Unix()
				break
			}
		}
	}
	sort.Stable(nodeSlice(nodes))
	i.PingsMutex.Unlock()
	nodesMutex.Unlock()

	return nil
}

func handleBootstrapInfoPacket(msg *transport.Message) error {
	bootstrapPacket := &bootstrap.Packet{}
	err := bootstrapPacket.UnmarshalBinary(msg.Data)
	if err != nil {
		return err
	}

	transPacket, err := bootstrap.DestructPacket(bootstrapPacket)
	if err != nil {
		return err
	}

	packet, ok := transPacket.(*bootstrap.InfoResponsePacket)
	if !ok {
		return errors.New("wtf")
	}

	nodesMutex.Lock()
	for _, node := range nodes {
		if (node.ip4 != nil && node.ip4.Equal(msg.Addr.IP)) ||
			(node.ip6 != nil && node.ip6.Equal(msg.Addr.IP)) {
			node.MOTD = packet.MOTD
			node.Version = fmt.Sprintf("%d", packet.Version)
			break
		}
	}
	nodesMutex.Unlock()

	return nil
}
<commit_msg>Restructured the locking logic inside probeNodeTCPPorts a bit<commit_after>package main

import (
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"sync"
	"time"

	"github.com/Impyy/tox4go/bootstrap"
	"github.com/Impyy/tox4go/crypto"
	"github.com/Impyy/tox4go/dht"
	"github.com/Impyy/tox4go/dht/ping"
	"github.com/Impyy/tox4go/relay"
	"github.com/Impyy/tox4go/transport"
	"github.com/didip/tollbooth"
)

const (
	enableIpv6  = true
	probeRate   = 1 * time.Minute
	refreshRate = 5 * time.Minute
)

var (
	lastScan    int64
	lastRefresh int64
	nodes       = []*toxNode{}
	nodesMutex  = sync.Mutex{}
	tcpPorts    = []int{443, 3389, 33445}
)

func main() {
	if parseFlags() {
		return
	}

	// load state if available
	state, err := loadState()
	if err != nil {
		log.Fatalf("error loading state: %s", err.Error())
	}
	lastScan = state.LastScan
	lastRefresh = state.LastRefresh
	nodes = state.Nodes

	if err := loadCountries(); err != nil {
		log.Fatalf("error loading countries.json: %s", err.Error())
	}

	inst, err := NewInstance(":33450")
	if err != nil {
		log.Fatalf("fatal: %s", err.Error())
	}
	inst.UDPTransport.Handle(dht.PacketIDSendNodes, inst.handleSendNodesPacket)
	inst.UDPTransport.Handle(bootstrap.PacketIDBootstrapInfo, handleBootstrapInfoPacket)

	//handle stop signal
	interruptChan := make(chan os.Signal)
	signal.Notify(interruptChan, os.Interrupt)

	//setup http server
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", httpListenPort))
	if err != nil {
		log.Fatalf("error in net.Listen: %s", err.Error())
	}
	limiter := tollbooth.NewLimiter(1, 2*time.Second)
	limiter.Methods = []string{"POST"}
	limiter.IPLookups = []string{"X-Forwarded-For", "RemoteAddr", "X-Real-IP"}
	serveMux := http.NewServeMux()
	serveMux.HandleFunc("/", handleHTTPRequest)
	serveMux.Handle("/test", tollbooth.LimitFuncHandler(limiter, handleHTTPRequest))
	serveMux.HandleFunc("/json", handleJSONRequest)
	go func() {
		err := http.Serve(listener, serveMux)
		if err != nil {
			log.Printf("http server error: %s\n", err.Error())
			interruptChan <- os.Interrupt
		}
	}()

	//listen for tox packets
	go func() {
		err := inst.UDPTransport.Listen()
		if err != nil {
			log.Printf("udp transport error: %s\n", err.Error())
			interruptChan <- os.Interrupt
		}
	}()
	//go tcpTransport.Listen()

	err = refreshNodes()
	if err != nil {
		log.Fatal(err.Error())
	}
	inst.probeNodes()

	probeTicker := time.NewTicker(probeRate)
	refreshTicker := time.NewTicker(refreshRate)
	updateTicker := time.NewTicker(30 * time.Second)
	run := true

	for run {
		select {
		case <-interruptChan:
			fmt.Printf("killing routines\n")
			probeTicker.Stop()
			refreshTicker.Stop()
			updateTicker.Stop()
			inst.UDPTransport.Stop()
			//tcpTransport.Stop()
			listener.Close()
			run = false
		case <-probeTicker.C:
			// we want an empty ping list at the start of every probe
			inst.PingsMutex.Lock()
			inst.Pings.Clear(false)
			inst.PingsMutex.Unlock()

			nodesMutex.Lock()
			err := inst.probeNodes()
			nodesMutex.Unlock()
			if err != nil {
				log.Printf("error while trying to probe nodes: %s", err.Error())
			}
		case <-refreshTicker.C:
			err := refreshNodes()
			if err != nil {
				log.Printf("error while trying to refresh nodes: %s", err.Error())
			}
		case <-updateTicker.C:
			inst.PingsMutex.Lock()
			inst.Pings.Clear(true)
			inst.PingsMutex.Unlock()

			nodesMutex.Lock()
			for _, node := range nodes {
				if time.Now().Sub(time.Unix(node.LastPing, 0)) > time.Minute*2 {
					node.UDPStatus = false
				}
			}
			sort.Stable(nodeSlice(nodes))

			state := getState()
			saveState(state)
			nodesMutex.Unlock()
		}
	}
}

func refreshNodes() error {
	parsedNodes, err := parseNodes()
	if err != nil {
		return err
	}

	nodesMutex.Lock()
	for _, freshNode := range parsedNodes {
		for _, node := range nodes {
			if freshNode.PublicKey == node.PublicKey {
				freshNode.LastPing = node.LastPing
				freshNode.UDPStatus = node.UDPStatus
				freshNode.TCPStatus = node.TCPStatus
				freshNode.TCPPorts = node.TCPPorts
				freshNode.MOTD = node.MOTD
				freshNode.Version = node.Version
				break
			}
		}
	}
	nodes = parsedNodes
	sort.Stable(nodeSlice(nodes))
	nodesMutex.Unlock()

	lastRefresh = time.Now().Unix()
	return nil
}

func (i *instance) probeNodes() error {
	for _, node := range nodes {
		err := i.getBootstrapInfo(node)
		if err != nil {
			fmt.Println(err.Error())
		}

		p, err := i.getNodes(node)
		if err != nil {
			fmt.Println(err.Error())
		} else {
			i.PingsMutex.Lock()
			err = i.Pings.Add(p)
			if err != nil {
				fmt.Println(err.Error())
			}
			i.PingsMutex.Unlock()
		}

		ports := tcpPorts
		exists := false
		for _, i := range ports {
			if i == node.Port {
				exists = true
			}
		}
		if !exists {
			ports = append(ports, node.Port)
		}

		go i.probeNodeTCPPorts(node, ports)
	}

	lastScan = time.Now().Unix()
	return nil
}

func (i *instance) probeNodeTCPPorts(node *toxNode, ports []int) {
	c := make(chan int)
	for _, port := range ports {
		go func(p int) {
			conn, err := connectTCP(node, p)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- -1
				return
			}

			err = i.tcpHandshake(node, conn)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- -1
			} else {
				c <- p
			}
			conn.Close()
		}(port)
	}

	newPorts := []int{}
	for i := 0; i < len(ports); i++ {
		port := <-c
		if port != -1 {
			fmt.Printf("tcp port for %s: %d\n", node.Maintainer, port)
			newPorts = append(newPorts, port)
		}
	}

	nodesMutex.Lock()
	node.TCPPorts = newPorts
	if len(node.TCPPorts) > 0 {
		node.LastPing = time.Now().Unix()
	}
	node.TCPStatus = len(node.TCPPorts) > 0
	sort.Stable(nodeSlice(nodes))
	nodesMutex.Unlock()
}

func (i *instance) tcpHandshake(node *toxNode, conn *net.TCPConn) error {
	nodePublicKey := new([crypto.PublicKeySize]byte)
	decPublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return err
	}
	copy(nodePublicKey[:], decPublicKey)

	relayConn, err := relay.NewConnection()
	if err != nil {
		return err
	}

	req, err := relayConn.StartHandshake()
	if err != nil {
		return err
	}

	reqBytes, err := req.MarshalBinary()
	if err != nil {
		return err
	}

	encryptedReqBytes, nonce, err := i.Ident.EncryptBlob(reqBytes, nodePublicKey)
	if err != nil {
		return err
	}

	reqPacket := &relay.HandshakeRequestPacket{
		PublicKey: i.Ident.PublicKey,
		Nonce:     nonce,
		Payload:   encryptedReqBytes,
	}

	reqPacketBytes, err := reqPacket.MarshalBinary()
	if err != nil {
		return err
	}

	conn.SetDeadline(time.Now().Add(2 * time.Second))
	_, err = conn.Write(reqPacketBytes)
	if err != nil {
		return err
	}

	buffer := make([]byte, 96)
	left := len(buffer)
	for left > 0 {
		read, readErr := conn.Read(buffer[len(buffer)-left:])
		if readErr != nil {
			return readErr
		}
		left -= read
	}

	res := relay.HandshakeResponsePacket{}
	err = res.UnmarshalBinary(buffer)
	if err != nil {
		return err
	}

	decryptedBytes, err := i.Ident.DecryptBlob(res.Payload, nodePublicKey, res.Nonce)
	if err != nil {
		return err
	}

	resPacket := &relay.HandshakePayload{}
	err = resPacket.UnmarshalBinary(decryptedBytes)
	if err != nil {
		return err
	}

	return relayConn.EndHandshake(resPacket)
}

func (i *instance) getNodes(node *toxNode) (*ping.Ping, error) {
	nodePublicKey := new([crypto.PublicKeySize]byte)
	decPublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return nil, err
	}
	copy(nodePublicKey[:], decPublicKey)

	p, err := ping.NewPing(nodePublicKey)
	if err != nil {
		return nil, err
	}

	packet := &dht.GetNodesPacket{
		PublicKey: i.Ident.PublicKey,
		PingID:    p.ID,
	}

	dhtPacket, err := i.Ident.EncryptPacket(transport.Packet(packet), nodePublicKey)
	if err != nil {
		return nil, err
	}

	payload, err := dhtPacket.MarshalBinary()
	if err != nil {
		return nil, err
	}

	if err := i.sendToUDP(payload, node); err != nil {
		return nil, err
	}

	return p, nil
}

func (i *instance) getBootstrapInfo(node *toxNode) error {
	packet, err := bootstrap.ConstructPacket(&bootstrap.InfoRequestPacket{})
	if err != nil {
		return err
	}

	payload, err := packet.MarshalBinary()
	if err != nil {
		return err
	}

	return i.sendToUDP(payload, node)
}

func (i *instance) sendToUDP(data []byte, node *toxNode) error {
	ip, err := getNodeIP(node)
	if err != nil {
		return err
	}

	return i.UDPTransport.Send(
		&transport.Message{
			Data: data,
			Addr: &net.UDPAddr{
				IP:   ip,
				Port: node.Port,
			},
		},
	)
}

func getNodeIP(node *toxNode) (net.IP, error) {
	if node.ip4 != nil {
		return node.ip4, nil
	} else if enableIpv6 && node.ip6 != nil {
		return node.ip6, nil
	}

	return nil, fmt.Errorf("no valid ip found for %s", node.Maintainer)
}

func connectTCP(node *toxNode, port int) (*net.TCPConn, error) {
	ip, err := getNodeIP(node)
	if err != nil {
		return nil, err
	}

	dialer := net.Dialer{}
	dialer.Deadline = time.Now().Add(2 * time.Second)

	tempConn, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		return nil, err
	}

	conn, ok := tempConn.(*net.TCPConn)
	if !ok {
		return nil, errors.New("not a tcp conn")
	}

	return conn, nil
}

func (i *instance) handleSendNodesPacket(msg *transport.Message) error {
	dhtPacket := &dht.Packet{}
	err := dhtPacket.UnmarshalBinary(msg.Data)
	if err != nil {
		return err
	}

	decryptedPacket, err := i.Ident.DecryptPacket(dhtPacket)
	if err != nil {
		return err
	}

	packet, ok := decryptedPacket.(*dht.SendNodesPacket)
	if !ok {
		return nil
	}

	i.PingsMutex.Lock()
	nodesMutex.Lock()
	if i.Pings.Find(dhtPacket.SenderPublicKey, packet.PingID, true) != nil {
		for _, node := range nodes {
			publicKey, err := hex.DecodeString(node.PublicKey)
			if err != nil {
				continue
			}

			if bytes.Equal(publicKey, dhtPacket.SenderPublicKey[:]) {
				node.UDPStatus = true
				node.LastPing = time.Now().Unix()
				break
			}
		}
	}
	sort.Stable(nodeSlice(nodes))
	i.PingsMutex.Unlock()
	nodesMutex.Unlock()

	return nil
}

func handleBootstrapInfoPacket(msg *transport.Message) error {
	bootstrapPacket := &bootstrap.Packet{}
	err := bootstrapPacket.UnmarshalBinary(msg.Data)
	if err != nil {
		return err
	}

	transPacket, err := bootstrap.DestructPacket(bootstrapPacket)
	if err != nil {
		return err
	}

	packet, ok := transPacket.(*bootstrap.InfoResponsePacket)
	if !ok {
		return errors.New("wtf")
	}

	nodesMutex.Lock()
	for _, node := range nodes {
		if (node.ip4 != nil && node.ip4.Equal(msg.Addr.IP)) ||
			(node.ip6 != nil && node.ip6.Equal(msg.Addr.IP)) {
			node.MOTD = packet.MOTD
			node.Version = fmt.Sprintf("%d", packet.Version)
			break
		}
	}
	nodesMutex.Unlock()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"github.com/robfig/cron"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

func main() {
	configPath := flag.String("c", "wl520cron.json", "配置文件路径")
	wlc := flag.String("wlc", "", "wl520配置文件路径")
	flag.Parse()
	wl520Crons := readConfig(*configPath)
	c := cron.New()
	for _, v := range wl520Crons {
		cmdCloned := v.Cmd
		if *wlc != "" {
			cmdCloned = cmdCloned + " -c=" + *wlc
		}
		log.Printf("%s:%s\n", v.Cron, cmdCloned)
		c.AddFunc(v.Cron, func() {
			cmd := exec.Command("wl520", strings.Split(cmdCloned, " ")...)
			out, err := cmd.CombinedOutput()
			if err != nil {
				panic(err)
			}
			log.Println(string(out))
		})
	}
	c.Start()
	select {}
}

//读取配置文件
func readConfig(configPath string) []Wl520Cron {
	bytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		createCronFile(configPath)
		panicf("配置文件不存在，已创建默认配置文件%s\n", configPath)
	}
	wl520Crons := make([]Wl520Cron, 0)
	json.Unmarshal(bytes, &wl520Crons)
	return wl520Crons
}

type Wl520Cron struct {
	Cron string `json:"cron"`
	Cmd  string `json:"cmd"`
}

//创建默认配置文件
func createCronFile(path string) {
	var f, _ = os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.ModeAppend)
	defer f.Close()
	wl520Crons := []Wl520Cron{{Cron: "* */30 * * * *", Cmd: "-a -p"},
		{Cron: "* 0 1,13 * * *", Cmd: "-t -v=20 -farm-sign"}}
	bytes, _ := json.MarshalIndent(wl520Crons, "", "  ")
	f.Write(bytes)
}
<commit_msg>Fix Travis ci<commit_after>package main

import (
	"encoding/json"
	"flag"
	"github.com/robfig/cron"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

func main() {
	configPath := flag.String("c", "wl520cron.json", "配置文件路径")
	wlc := flag.String("wlc", "", "wl520配置文件路径")
	flag.Parse()
	wl520Crons := readConfig(*configPath)
	c := cron.New()
	for _, v := range wl520Crons {
		cmdCloned := v.Cmd
		if *wlc != "" {
			cmdCloned = cmdCloned + " -c=" + *wlc
		}
		log.Printf("%s:%s\n", v.Cron, cmdCloned)
		c.AddFunc(v.Cron, func() {
			cmd := exec.Command("wl520", strings.Split(cmdCloned, " ")...)
			out, err := cmd.CombinedOutput()
			if err != nil {
				panic(err)
			}
			log.Println(string(out))
		})
	}
	c.Start()
	select {}
}

//读取配置文件
func readConfig(configPath string) []Wl520Cron {
	bytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		createCronFile(configPath)
		log.Fatalf("配置文件不存在，已创建默认配置文件%s\n", configPath)
	}
	wl520Crons := make([]Wl520Cron, 0)
	json.Unmarshal(bytes, &wl520Crons)
	return wl520Crons
}

type Wl520Cron struct {
	Cron string `json:"cron"`
	Cmd  string `json:"cmd"`
}

//创建默认配置文件
func createCronFile(path string) {
	var f, _ = os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.ModeAppend)
	defer f.Close()
	wl520Crons := []Wl520Cron{{Cron: "* */30 * * * *", Cmd: "-a -p"},
		{Cron: "* 0 1,13 * * *", Cmd: "-t -v=20 -farm-sign"}}
	bytes, _ := json.MarshalIndent(wl520Crons, "", "  ")
	f.Write(bytes)
}
<|endoftext|>


<commit_before>/*
 *
 * 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 cmds

import (
	"fmt"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	"github.com/spf13/cobra"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

type tenantDeleteFlags struct {
	cmd  *cobra.Command
	args []string

	confirm bool
	tenant  string
}

func NewCmdTenantDelete(f *cmdutil.Factory) *cobra.Command {
	p := &tenantDeleteFlags{}
	cmd := &cobra.Command{
		Use:   "delete",
		Short: "Deletes all your tenant resources",
		Run: func(cmd *cobra.Command, args []string) {
			p.cmd = cmd
			p.args = args
			if cmd.Flags().Lookup(yesFlag).Value.String() == "true" {
				p.confirm = true
			}
			handleError(p.tenantDelete(f))
		},
	}
	flags := cmd.Flags()
	flags.StringVarP(&p.tenant, "tenant", "t", "", "the name of the tenant to delete. If blank it will be discovered")
	return cmd
}

func (p *tenantDeleteFlags) tenantDelete(f *cmdutil.Factory) error {
	c, cfg := client.NewClient(f)
	ns, _, _ := f.DefaultNamespace()
	oc, _ := client.NewOpenShiftClient(cfg)
	initSchema()

	var err error
	userNS := p.tenant
	if len(userNS) == 0 {
		userNS, err = detectCurrentUserNamespace(ns, c, oc)
		if err != nil {
			return err
		}
	}
	if !p.confirm {
		confirm := ""
		util.Warn("WARNING this command will delete all of your tenant resource, pipelines, jobs, and apps\n")
		util.Info("for your tenant: ")
		util.Successf("%s", userNS)
		util.Warn("\nContinue [y/N]: ")
		fmt.Scanln(&confirm)
		if confirm != "y" {
			util.Warn("Aborted\n")
			return nil
		}
	}

	util.Info("Deleting tenant: ")
	util.Successf("%s\n\n", userNS)

	ocCLI := "oc"
	stageNS := fmt.Sprintf("%s-stage", userNS)
	runNS := fmt.Sprintf("%s-run", userNS)
	cheNS := fmt.Sprintf("%s-che", userNS)
	jenkinsNS := fmt.Sprintf("%s-jenkins", userNS)

	// zap jenkins resources
	util.Infof("Removing jenkins namespace resources in %s", jenkinsNS)
	err = runCommand(ocCLI, "delete", "all", "--all", "-n", jenkinsNS, "--cascade=true", "--grace-period=-50")
	if err != nil {
		return nil
	}

	// zap other projects
	projectsToRemove := []string{stageNS, runNS, cheNS, userNS}
	for _, ns := range projectsToRemove {
		util.Infof("Removing project %s", ns)
		err = runCommand(ocCLI, "delete", "project", ns, "--cascade=true", "--grace-period=-50")
		if err != nil {
			return nil
		}
	}
	util.Infof("Tenant %s now deleted.\n", userNS)
	util.Infof("Now please Update your Tenant via: https://github.com/openshiftio/openshift.io/wiki/FAQ#how-do-i-update-my-tenant-\n\n")
	return nil
}
<commit_msg>lets remove the PVCs when deleting the tenant<commit_after>/*
 *
 * 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 cmds

import (
	"fmt"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	"github.com/spf13/cobra"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

type tenantDeleteFlags struct {
	cmd  *cobra.Command
	args []string

	confirm bool
	tenant  string
}

func NewCmdTenantDelete(f *cmdutil.Factory) *cobra.Command {
	p := &tenantDeleteFlags{}
	cmd := &cobra.Command{
		Use:   "delete",
		Short: "Deletes all your tenant resources",
		Run: func(cmd *cobra.Command, args []string) {
			p.cmd = cmd
			p.args = args
			if cmd.Flags().Lookup(yesFlag).Value.String() == "true" {
				p.confirm = true
			}
			handleError(p.tenantDelete(f))
		},
	}
	flags := cmd.Flags()
	flags.StringVarP(&p.tenant, "tenant", "t", "", "the name of the tenant to delete. If blank it will be discovered")
	return cmd
}

func (p *tenantDeleteFlags) tenantDelete(f *cmdutil.Factory) error {
	c, cfg := client.NewClient(f)
	ns, _, _ := f.DefaultNamespace()
	oc, _ := client.NewOpenShiftClient(cfg)
	initSchema()

	var err error
	userNS := p.tenant
	if len(userNS) == 0 {
		userNS, err = detectCurrentUserNamespace(ns, c, oc)
		if err != nil {
			return err
		}
	}
	if !p.confirm {
		confirm := ""
		util.Warn("WARNING this command will delete all of your tenant resource, pipelines, jobs, and apps\n")
		util.Info("for your tenant: ")
		util.Successf("%s", userNS)
		util.Warn("\nContinue [y/N]: ")
		fmt.Scanln(&confirm)
		if confirm != "y" {
			util.Warn("Aborted\n")
			return nil
		}
	}

	util.Info("Deleting tenant: ")
	util.Successf("%s\n\n", userNS)

	ocCLI := "oc"
	stageNS := fmt.Sprintf("%s-stage", userNS)
	runNS := fmt.Sprintf("%s-run", userNS)
	cheNS := fmt.Sprintf("%s-che", userNS)
	jenkinsNS := fmt.Sprintf("%s-jenkins", userNS)

	// zap jenkins resources
	util.Infof("Removing jenkins namespace resources in %s\n", jenkinsNS)
	err = runCommand(ocCLI, "delete", "all", "--all", "-n", jenkinsNS, "--cascade=true", "--grace-period=-50")
	if err != nil {
		return nil
	}
	err = runCommand(ocCLI, "delete", "pvc", "--all", "-n", jenkinsNS, "--cascade=true", "--grace-period=-50")
	if err != nil {
		return nil
	}

	// zap other projects
	projectsToRemove := []string{stageNS, runNS, cheNS, userNS}
	for _, ns := range projectsToRemove {
		util.Infof("Removing project %s\n", ns)
		err = runCommand(ocCLI, "delete", "project", ns, "--cascade=true", "--grace-period=-50")
		if err != nil {
			return nil
		}
	}
	util.Infof("Tenant %s now deleted.\n", userNS)
	util.Infof("Now please Update your Tenant via: https://github.com/openshiftio/openshift.io/wiki/FAQ#how-do-i-update-my-tenant-\n\n")
	return nil
}
<|endoftext|>


<commit_before>package html

import (
	"bytes"
	"io"
	"strings"

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

func Parse(r io.Reader) (*Node, error) {
	doc, err := html.Parse(r)
	return (*Node)(doc), err
}

func ParseBytes(b []byte) (*Node, error) {
	return Parse(bytes.NewBuffer(b))
}

func ParseString(s string) (*Node, error) {
	return Parse(strings.NewReader(s))
}
<commit_msg>ParseUrl<commit_after>package html

import (
	"bytes"
	"io"
	"net/http"
	"strings"

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

func Parse(r io.Reader) (*Node, error) {
	doc, err := html.Parse(r)
	return (*Node)(doc), err
}

func ParseBytes(b []byte) (*Node, error) {
	return Parse(bytes.NewBuffer(b))
}

func ParseString(s string) (*Node, error) {
	return Parse(strings.NewReader(s))
}

func ParseUrl(url string) (*Node, error) {
	r, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	doc, err := Parse(r.Body)
	if err != nil {
		return nil, err
	}
	return doc, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 sync

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/pkg/controller/node/ipam/cidrset"
)

const (
	// InvalidPodCIDR is the event recorded when a node is found with an
	// invalid PodCIDR.
	InvalidPodCIDR = "CloudCIDRAllocatorInvalidPodCIDR"
	// InvalidModeEvent is the event recorded when the CIDR range cannot be
	// sync'd due to the cluster running in the wrong mode.
	InvalidModeEvent = "CloudCIDRAllocatorInvalidMode"
	// MismatchEvent is the event recorded when the CIDR range allocated in the
	// node spec does not match what has been allocated in the cloud.
	MismatchEvent = "CloudCIDRAllocatorMismatch"
)

// cloudAlias is the interface to the cloud platform APIs.
type cloudAlias interface {
	// Alias returns the IP alias for the node.
	Alias(ctx context.Context, nodeName string) (*net.IPNet, error)
	// AddAlias adds an alias to the node.
	AddAlias(ctx context.Context, nodeName string, cidrRange *net.IPNet) error
}

// kubeAPI is the interface to the Kubernetes APIs.
type kubeAPI interface {
	// Node returns the spec for the Node object.
	Node(ctx context.Context, name string) (*v1.Node, error)
	// UpdateNodePodCIDR updates the PodCIDR in the Node spec.
	UpdateNodePodCIDR(ctx context.Context, node *v1.Node, cidrRange *net.IPNet) error
	// UpdateNodeNetworkUnavailable updates the network unavailable status for the node.
	UpdateNodeNetworkUnavailable(nodeName string, unavailable bool) error
	// EmitNodeWarningEvent emits an event for the given node.
	EmitNodeWarningEvent(nodeName, reason, fmt string, args ...interface{})
}

// controller is the interface to the controller.
type controller interface {
	// ReportResult updates the controller with the result of the latest
	// sync operation.
	ReportResult(err error)
	// ResyncTimeout returns the amount of time to wait before retrying
	// a sync with a node.
	ResyncTimeout() time.Duration
}

// NodeSyncMode is the mode the cloud CIDR allocator runs in.
type NodeSyncMode string

var (
	// SyncFromCloud is the mode that synchronizes the IP allocation from the cloud
	// platform to the node.
	SyncFromCloud NodeSyncMode = "SyncFromCloud"
	// SyncFromCluster is the mode that synchronizes the IP allocation determined
	// by the k8s controller to the cloud provider.
	SyncFromCluster NodeSyncMode = "SyncFromCluster"
)

// IsValidMode returns true if the given mode is valid.
func IsValidMode(m NodeSyncMode) bool {
	switch m {
	case SyncFromCloud:
	case SyncFromCluster:
	default:
		return false
	}
	return true
}

// NodeSync synchronizes the state for a single node in the cluster.
type NodeSync struct {
	c          controller
	cloudAlias cloudAlias
	kubeAPI    kubeAPI
	mode       NodeSyncMode
	nodeName   string
	opChan     chan syncOp
	set        *cidrset.CidrSet
}

// New returns a new syncer for a given node.
func New(c controller, cloudAlias cloudAlias, kubeAPI kubeAPI, mode NodeSyncMode, nodeName string, set *cidrset.CidrSet) *NodeSync {
	return &NodeSync{
		c:          c,
		cloudAlias: cloudAlias,
		kubeAPI:    kubeAPI,
		mode:       mode,
		nodeName:   nodeName,
		opChan:     make(chan syncOp, 1),
		set:        set,
	}
}

// Loop runs the sync loop for a given node. done is an optional channel that
// is closed when the Loop() returns.
func (sync *NodeSync) Loop(done chan struct{}) {
	glog.V(2).Infof("Starting sync loop for node %q", sync.nodeName)

	defer func() {
		if done != nil {
			close(done)
		}
	}()

	timeout := sync.c.ResyncTimeout()
	delayTimer := time.NewTimer(timeout)
	glog.V(4).Infof("Resync node %q in %v", sync.nodeName, timeout)

	for {
		select {
		case op, more := <-sync.opChan:
			if !more {
				glog.V(2).Infof("Stopping sync loop")
				return
			}
			sync.c.ReportResult(op.run(sync))
			if !delayTimer.Stop() {
				<-delayTimer.C
			}
		case <-delayTimer.C:
			glog.V(4).Infof("Running resync for node %q", sync.nodeName)
			sync.c.ReportResult((&updateOp{}).run(sync))
		}

		timeout := sync.c.ResyncTimeout()
		delayTimer.Reset(timeout)
		glog.V(4).Infof("Resync node %q in %v", sync.nodeName, timeout)
	}
}

// Update causes an update operation on the given node. If node is nil, then
// the syncer will fetch the node spec from the API server before syncing.
//
// This method is safe to call from multiple goroutines.
func (sync *NodeSync) Update(node *v1.Node) {
	sync.opChan <- &updateOp{node}
}

// Delete performs the sync operations necessary to remove the node from the
// IPAM state.
//
// This method is safe to call from multiple goroutines.
func (sync *NodeSync) Delete(node *v1.Node) {
	sync.opChan <- &deleteOp{node}
	close(sync.opChan)
}

// syncOp is the interface for generic sync operation.
type syncOp interface {
	// run the requested sync operation.
	run(sync *NodeSync) error
}

// updateOp handles creation and updates of a node.
type updateOp struct {
	node *v1.Node
}

func (op *updateOp) String() string {
	if op.node == nil {
		return fmt.Sprintf("updateOp(nil)")
	}
	return fmt.Sprintf("updateOp(%q,%v)", op.node.Name, op.node.Spec.PodCIDR)
}

func (op *updateOp) run(sync *NodeSync) error {
	glog.V(3).Infof("Running updateOp %+v", op)

	ctx := context.Background()

	if op.node == nil {
		glog.V(3).Infof("Getting node spec for %q", sync.nodeName)
		node, err := sync.kubeAPI.Node(ctx, sync.nodeName)
		if err != nil {
			glog.Errorf("Error getting node %q spec: %v", sync.nodeName, err)
			return err
		}
		op.node = node
	}

	aliasRange, err := sync.cloudAlias.Alias(ctx, sync.nodeName)
	if err != nil {
		glog.Errorf("Error getting cloud alias for node %q: %v", sync.nodeName, err)
		return err
	}

	switch {
	case op.node.Spec.PodCIDR == "" && aliasRange == nil:
		err = op.allocateRange(ctx, sync, op.node)
	case op.node.Spec.PodCIDR == "" && aliasRange != nil:
		err = op.updateNodeFromAlias(ctx, sync, op.node, aliasRange)
	case op.node.Spec.PodCIDR != "" && aliasRange == nil:
		err = op.updateAliasFromNode(ctx, sync, op.node)
	case op.node.Spec.PodCIDR != "" && aliasRange != nil:
		err = op.validateRange(ctx, sync, op.node, aliasRange)
	}

	return err
}

// validateRange checks that the allocated range and the alias range
// match.
func (op *updateOp) validateRange(ctx context.Context, sync *NodeSync, node *v1.Node, aliasRange *net.IPNet) error {
	if node.Spec.PodCIDR != aliasRange.String() {
		glog.Errorf("Inconsistency detected between node PodCIDR and node alias (%v != %v)",
			node.Spec.PodCIDR, aliasRange)
		sync.kubeAPI.EmitNodeWarningEvent(node.Name, MismatchEvent,
			"Node.Spec.PodCIDR != cloud alias (%v != %v)", node.Spec.PodCIDR, aliasRange)
		// User intervention is required in this case, as this is most likely due
		// to the user mucking around with their VM aliases on the side.
	} else {
		glog.V(4).Infof("Node %q CIDR range %v is matches cloud assignment", node.Name, node.Spec.PodCIDR)
	}
	return nil
}

// updateNodeFromAlias updates the the node from the cloud allocated
// alias.
func (op *updateOp) updateNodeFromAlias(ctx context.Context, sync *NodeSync, node *v1.Node, aliasRange *net.IPNet) error {
	if sync.mode != SyncFromCloud {
		fmt.Errorf("Detect mode %q while expect mode %q when syncing from cloud", sync.mode, SyncFromCloud)
	}

	glog.V(2).Infof("Updating node spec with alias range, node.PodCIDR = %v", aliasRange)

	if err := sync.set.Occupy(aliasRange); err != nil {
		glog.Errorf("Error occupying range %v for node %v", aliasRange, sync.nodeName)
		return err
	}

	if err := sync.kubeAPI.UpdateNodePodCIDR(ctx, node, aliasRange); err != nil {
		glog.Errorf("Could not update node %q PodCIDR to %v: %v", node.Name, aliasRange, err)
		return err
	}

	glog.V(2).Infof("Node %q PodCIDR set to %v", node.Name, aliasRange)

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Updated node %q PodCIDR from cloud alias %v", node.Name, aliasRange)

	return nil
}

// updateAliasFromNode updates the cloud alias given the node allocation.
func (op *updateOp) updateAliasFromNode(ctx context.Context, sync *NodeSync, node *v1.Node) error {
	if sync.mode != SyncFromCluster {
		fmt.Errorf("Detect mode %q while expect mode %q when syncing from cluster", sync.mode, SyncFromCluster)
	}

	_, aliasRange, err := net.ParseCIDR(node.Spec.PodCIDR)
	if err != nil {
		glog.Errorf("Could not parse PodCIDR (%q) for node %q: %v",
			node.Spec.PodCIDR, node.Name, err)
		return err
	}

	if err := sync.set.Occupy(aliasRange); err != nil {
		glog.Errorf("Error occupying range %v for node %v", aliasRange, sync.nodeName)
		return err
	}

	if err := sync.cloudAlias.AddAlias(ctx, node.Name, aliasRange); err != nil {
		glog.Errorf("Could not add alias %v for node %q: %v", aliasRange, node.Name, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Updated node %q cloud alias with node spec, node.PodCIDR = %v",
		node.Name, node.Spec.PodCIDR)

	return nil
}

// allocateRange allocates a new range and updates both the cloud
// platform and the node allocation.
func (op *updateOp) allocateRange(ctx context.Context, sync *NodeSync, node *v1.Node) error {
	if sync.mode != SyncFromCluster {
		sync.kubeAPI.EmitNodeWarningEvent(node.Name, InvalidModeEvent,
			"Cannot allocate CIDRs in mode %q", sync.mode)
		return fmt.Errorf("controller cannot allocate CIDRS in mode %q", sync.mode)
	}

	cidrRange, err := sync.set.AllocateNext()
	if err != nil {
		return err
	}
	// If addAlias returns a hard error, cidrRange will be leaked as there
	// is no durable record of the range. The missing space will be
	// recovered on the next restart of the controller.
	if err := sync.cloudAlias.AddAlias(ctx, node.Name, cidrRange); err != nil {
		glog.Errorf("Could not add alias %v for node %q: %v", cidrRange, node.Name, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodePodCIDR(ctx, node, cidrRange); err != nil {
		glog.Errorf("Could not update node %q PodCIDR to %v: %v", node.Name, cidrRange, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Allocated PodCIDR %v for node %q", cidrRange, node.Name)

	return nil
}

// deleteOp handles deletion of a node.
type deleteOp struct {
	node *v1.Node
}

func (op *deleteOp) String() string {
	if op.node == nil {
		return fmt.Sprintf("deleteOp(nil)")
	}
	return fmt.Sprintf("deleteOp(%q,%v)", op.node.Name, op.node.Spec.PodCIDR)
}

func (op *deleteOp) run(sync *NodeSync) error {
	glog.V(3).Infof("Running deleteOp %+v", op)
	if op.node.Spec.PodCIDR == "" {
		glog.V(2).Infof("Node %q was deleted, node had no PodCIDR range assigned", op.node.Name)
		return nil
	}

	_, cidrRange, err := net.ParseCIDR(op.node.Spec.PodCIDR)
	if err != nil {
		glog.Errorf("Deleted node %q has an invalid podCIDR %q: %v",
			op.node.Name, op.node.Spec.PodCIDR, err)
		sync.kubeAPI.EmitNodeWarningEvent(op.node.Name, InvalidPodCIDR,
			"Node %q has an invalid PodCIDR: %q", op.node.Name, op.node.Spec.PodCIDR)
		return nil
	}

	sync.set.Release(cidrRange)
	glog.V(2).Infof("Node %q was deleted, releasing CIDR range %v",
		op.node.Name, op.node.Spec.PodCIDR)

	return nil
}
<commit_msg>Synced the changed made in PR #56260.<commit_after>/*
Copyright 2017 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 sync

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/pkg/controller/node/ipam/cidrset"
)

const (
	// InvalidPodCIDR is the event recorded when a node is found with an
	// invalid PodCIDR.
	InvalidPodCIDR = "CloudCIDRAllocatorInvalidPodCIDR"
	// InvalidModeEvent is the event recorded when the CIDR range cannot be
	// sync'd due to the cluster running in the wrong mode.
	InvalidModeEvent = "CloudCIDRAllocatorInvalidMode"
	// MismatchEvent is the event recorded when the CIDR range allocated in the
	// node spec does not match what has been allocated in the cloud.
	MismatchEvent = "CloudCIDRAllocatorMismatch"
)

// cloudAlias is the interface to the cloud platform APIs.
type cloudAlias interface {
	// Alias returns the IP alias for the node.
	Alias(ctx context.Context, nodeName string) (*net.IPNet, error)
	// AddAlias adds an alias to the node.
	AddAlias(ctx context.Context, nodeName string, cidrRange *net.IPNet) error
}

// kubeAPI is the interface to the Kubernetes APIs.
type kubeAPI interface {
	// Node returns the spec for the Node object.
	Node(ctx context.Context, name string) (*v1.Node, error)
	// UpdateNodePodCIDR updates the PodCIDR in the Node spec.
	UpdateNodePodCIDR(ctx context.Context, node *v1.Node, cidrRange *net.IPNet) error
	// UpdateNodeNetworkUnavailable updates the network unavailable status for the node.
	UpdateNodeNetworkUnavailable(nodeName string, unavailable bool) error
	// EmitNodeWarningEvent emits an event for the given node.
	EmitNodeWarningEvent(nodeName, reason, fmt string, args ...interface{})
}

// controller is the interface to the controller.
type controller interface {
	// ReportResult updates the controller with the result of the latest
	// sync operation.
	ReportResult(err error)
	// ResyncTimeout returns the amount of time to wait before retrying
	// a sync with a node.
	ResyncTimeout() time.Duration
}

// NodeSyncMode is the mode the cloud CIDR allocator runs in.
type NodeSyncMode string

var (
	// SyncFromCloud is the mode that synchronizes the IP allocation from the cloud
	// platform to the node.
	SyncFromCloud NodeSyncMode = "SyncFromCloud"
	// SyncFromCluster is the mode that synchronizes the IP allocation determined
	// by the k8s controller to the cloud provider.
	SyncFromCluster NodeSyncMode = "SyncFromCluster"
)

// IsValidMode returns true if the given mode is valid.
func IsValidMode(m NodeSyncMode) bool {
	switch m {
	case SyncFromCloud:
	case SyncFromCluster:
	default:
		return false
	}
	return true
}

// NodeSync synchronizes the state for a single node in the cluster.
type NodeSync struct {
	c          controller
	cloudAlias cloudAlias
	kubeAPI    kubeAPI
	mode       NodeSyncMode
	nodeName   string
	opChan     chan syncOp
	set        *cidrset.CidrSet
}

// New returns a new syncer for a given node.
func New(c controller, cloudAlias cloudAlias, kubeAPI kubeAPI, mode NodeSyncMode, nodeName string, set *cidrset.CidrSet) *NodeSync {
	return &NodeSync{
		c:          c,
		cloudAlias: cloudAlias,
		kubeAPI:    kubeAPI,
		mode:       mode,
		nodeName:   nodeName,
		opChan:     make(chan syncOp, 1),
		set:        set,
	}
}

// Loop runs the sync loop for a given node. done is an optional channel that
// is closed when the Loop() returns.
func (sync *NodeSync) Loop(done chan struct{}) {
	glog.V(2).Infof("Starting sync loop for node %q", sync.nodeName)

	defer func() {
		if done != nil {
			close(done)
		}
	}()

	timeout := sync.c.ResyncTimeout()
	delayTimer := time.NewTimer(timeout)
	glog.V(4).Infof("Resync node %q in %v", sync.nodeName, timeout)

	for {
		select {
		case op, more := <-sync.opChan:
			if !more {
				glog.V(2).Infof("Stopping sync loop")
				return
			}
			sync.c.ReportResult(op.run(sync))
			if !delayTimer.Stop() {
				<-delayTimer.C
			}
		case <-delayTimer.C:
			glog.V(4).Infof("Running resync for node %q", sync.nodeName)
			sync.c.ReportResult((&updateOp{}).run(sync))
		}

		timeout := sync.c.ResyncTimeout()
		delayTimer.Reset(timeout)
		glog.V(4).Infof("Resync node %q in %v", sync.nodeName, timeout)
	}
}

// Update causes an update operation on the given node. If node is nil, then
// the syncer will fetch the node spec from the API server before syncing.
//
// This method is safe to call from multiple goroutines.
func (sync *NodeSync) Update(node *v1.Node) {
	sync.opChan <- &updateOp{node}
}

// Delete performs the sync operations necessary to remove the node from the
// IPAM state.
//
// This method is safe to call from multiple goroutines.
func (sync *NodeSync) Delete(node *v1.Node) {
	sync.opChan <- &deleteOp{node}
	close(sync.opChan)
}

// syncOp is the interface for generic sync operation.
type syncOp interface {
	// run the requested sync operation.
	run(sync *NodeSync) error
}

// updateOp handles creation and updates of a node.
type updateOp struct {
	node *v1.Node
}

func (op *updateOp) String() string {
	if op.node == nil {
		return fmt.Sprintf("updateOp(nil)")
	}
	return fmt.Sprintf("updateOp(%q,%v)", op.node.Name, op.node.Spec.PodCIDR)
}

func (op *updateOp) run(sync *NodeSync) error {
	glog.V(3).Infof("Running updateOp %+v", op)

	ctx := context.Background()

	if op.node == nil {
		glog.V(3).Infof("Getting node spec for %q", sync.nodeName)
		node, err := sync.kubeAPI.Node(ctx, sync.nodeName)
		if err != nil {
			glog.Errorf("Error getting node %q spec: %v", sync.nodeName, err)
			return err
		}
		op.node = node
	}

	aliasRange, err := sync.cloudAlias.Alias(ctx, sync.nodeName)
	if err != nil {
		glog.Errorf("Error getting cloud alias for node %q: %v", sync.nodeName, err)
		return err
	}

	switch {
	case op.node.Spec.PodCIDR == "" && aliasRange == nil:
		err = op.allocateRange(ctx, sync, op.node)
	case op.node.Spec.PodCIDR == "" && aliasRange != nil:
		err = op.updateNodeFromAlias(ctx, sync, op.node, aliasRange)
	case op.node.Spec.PodCIDR != "" && aliasRange == nil:
		err = op.updateAliasFromNode(ctx, sync, op.node)
	case op.node.Spec.PodCIDR != "" && aliasRange != nil:
		err = op.validateRange(ctx, sync, op.node, aliasRange)
	}

	return err
}

// validateRange checks that the allocated range and the alias range
// match.
func (op *updateOp) validateRange(ctx context.Context, sync *NodeSync, node *v1.Node, aliasRange *net.IPNet) error {
	if node.Spec.PodCIDR != aliasRange.String() {
		glog.Errorf("Inconsistency detected between node PodCIDR and node alias (%v != %v)",
			node.Spec.PodCIDR, aliasRange)
		sync.kubeAPI.EmitNodeWarningEvent(node.Name, MismatchEvent,
			"Node.Spec.PodCIDR != cloud alias (%v != %v)", node.Spec.PodCIDR, aliasRange)
		// User intervention is required in this case, as this is most likely due
		// to the user mucking around with their VM aliases on the side.
	} else {
		glog.V(4).Infof("Node %q CIDR range %v is matches cloud assignment", node.Name, node.Spec.PodCIDR)
	}
	return nil
}

// updateNodeFromAlias updates the the node from the cloud allocated
// alias.
func (op *updateOp) updateNodeFromAlias(ctx context.Context, sync *NodeSync, node *v1.Node, aliasRange *net.IPNet) error {
	if sync.mode != SyncFromCloud {
		glog.Warningf("Detect mode %q while expect to sync from cloud", sync.mode
	}

	glog.V(2).Infof("Updating node spec with alias range, node.PodCIDR = %v", aliasRange)

	if err := sync.set.Occupy(aliasRange); err != nil {
		glog.Errorf("Error occupying range %v for node %v", aliasRange, sync.nodeName)
		return err
	}

	if err := sync.kubeAPI.UpdateNodePodCIDR(ctx, node, aliasRange); err != nil {
		glog.Errorf("Could not update node %q PodCIDR to %v: %v", node.Name, aliasRange, err)
		return err
	}

	glog.V(2).Infof("Node %q PodCIDR set to %v", node.Name, aliasRange)

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Updated node %q PodCIDR from cloud alias %v", node.Name, aliasRange)

	return nil
}

// updateAliasFromNode updates the cloud alias given the node allocation.
func (op *updateOp) updateAliasFromNode(ctx context.Context, sync *NodeSync, node *v1.Node) error {
	if sync.mode != SyncFromCluster {
		glog.Warningf("Detect mode %q while expect to sync from cluster", sync.mode)
	}

	_, aliasRange, err := net.ParseCIDR(node.Spec.PodCIDR)
	if err != nil {
		glog.Errorf("Could not parse PodCIDR (%q) for node %q: %v",
			node.Spec.PodCIDR, node.Name, err)
		return err
	}

	if err := sync.set.Occupy(aliasRange); err != nil {
		glog.Errorf("Error occupying range %v for node %v", aliasRange, sync.nodeName)
		return err
	}

	if err := sync.cloudAlias.AddAlias(ctx, node.Name, aliasRange); err != nil {
		glog.Errorf("Could not add alias %v for node %q: %v", aliasRange, node.Name, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Updated node %q cloud alias with node spec, node.PodCIDR = %v",
		node.Name, node.Spec.PodCIDR)

	return nil
}

// allocateRange allocates a new range and updates both the cloud
// platform and the node allocation.
func (op *updateOp) allocateRange(ctx context.Context, sync *NodeSync, node *v1.Node) error {
	if sync.mode != SyncFromCluster {
		sync.kubeAPI.EmitNodeWarningEvent(node.Name, InvalidModeEvent,
			"Cannot allocate CIDRs in mode %q", sync.mode)
		return fmt.Errorf("controller cannot allocate CIDRS in mode %q", sync.mode)
	}

	cidrRange, err := sync.set.AllocateNext()
	if err != nil {
		return err
	}
	// If addAlias returns a hard error, cidrRange will be leaked as there
	// is no durable record of the range. The missing space will be
	// recovered on the next restart of the controller.
	if err := sync.cloudAlias.AddAlias(ctx, node.Name, cidrRange); err != nil {
		glog.Errorf("Could not add alias %v for node %q: %v", cidrRange, node.Name, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodePodCIDR(ctx, node, cidrRange); err != nil {
		glog.Errorf("Could not update node %q PodCIDR to %v: %v", node.Name, cidrRange, err)
		return err
	}

	if err := sync.kubeAPI.UpdateNodeNetworkUnavailable(node.Name, false); err != nil {
		glog.Errorf("Could not update node NetworkUnavailable status to false: %v", err)
		return err
	}

	glog.V(2).Infof("Allocated PodCIDR %v for node %q", cidrRange, node.Name)

	return nil
}

// deleteOp handles deletion of a node.
type deleteOp struct {
	node *v1.Node
}

func (op *deleteOp) String() string {
	if op.node == nil {
		return fmt.Sprintf("deleteOp(nil)")
	}
	return fmt.Sprintf("deleteOp(%q,%v)", op.node.Name, op.node.Spec.PodCIDR)
}

func (op *deleteOp) run(sync *NodeSync) error {
	glog.V(3).Infof("Running deleteOp %+v", op)
	if op.node.Spec.PodCIDR == "" {
		glog.V(2).Infof("Node %q was deleted, node had no PodCIDR range assigned", op.node.Name)
		return nil
	}

	_, cidrRange, err := net.ParseCIDR(op.node.Spec.PodCIDR)
	if err != nil {
		glog.Errorf("Deleted node %q has an invalid podCIDR %q: %v",
			op.node.Name, op.node.Spec.PodCIDR, err)
		sync.kubeAPI.EmitNodeWarningEvent(op.node.Name, InvalidPodCIDR,
			"Node %q has an invalid PodCIDR: %q", op.node.Name, op.node.Spec.PodCIDR)
		return nil
	}

	sync.set.Release(cidrRange)
	glog.V(2).Infof("Node %q was deleted, releasing CIDR range %v",
		op.node.Name, op.node.Spec.PodCIDR)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Richard Lehane. 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 containermatcher

import (
	"strings"

	"github.com/richardlehane/siegfried/pkg/core"
	"github.com/richardlehane/siegfried/pkg/core/priority"
	"github.com/richardlehane/siegfried/pkg/core/siegreader"
)

func (m Matcher) Identify(n string, b siegreader.Buffer) (chan core.Result, error) {
	res := make(chan core.Result)
	// check trigger
	buf, err := b.Slice(0, 8)
	if err != nil {
		close(res)
		return res, nil
	}
	for _, c := range m {
		if c.trigger(buf) {
			rdr, err := c.rdr(b)
			if err != nil {
				close(res)
				return res, err
			}
			go c.identify(rdr, res)
			return res, nil
		}
	}
	// nothing ... move on
	close(res)
	return res, nil
}

type identifier struct {
	partsMatched [][]hit // hits for parts
	ruledOut     []bool  // mark additional signatures as negatively matched
	waitSet      *priority.WaitSet
	hits         []hit // shared buffer of hits used when matching
}

func (c *ContainerMatcher) newIdentifier(numParts int) *identifier {
	return &identifier{
		make([][]hit, numParts),
		make([]bool, numParts),
		c.Priorities.WaitSet(),
		make([]hit, 0, 1),
	}
}

func (c *ContainerMatcher) identify(rdr Reader, res chan core.Result) {
	// safe to call on a nil matcher (i.e. container matching switched off)
	if c == nil {
		close(res)
		return
	}
	id := c.newIdentifier(len(c.Parts))
	var err, hit error, bool
	for err = rdr.Next(); err == nil; err = rdr.Next() {
		ct, ok := c.NameCTest[rdr.Name()]
		if !ok {
			continue
		}
		// name has matched, lets test the CTests
		// ct.identify will generate a slice of hits which pass to
		// processHits which will return true if we can stop
		if c.processHits(ct.identify(c, id, rdr, rdr.Name()), id, ct, rdr.Name(), res) {
			hit = true
			break
		}
	}
	// if we have no hits and a default value for this matcher, send it
	if !hit && c.Default {
		// the default is a negative value calculated from the CType
		res <- defaultHit(-1 - int(c.CType))
	}
	close(res)
}

func (ct *CTest) identify(c *ContainerMatcher, id *identifier, rdr Reader, name string) []hit {
	// reset hits
	id.hits = id.hits[:0]
	for _, h := range ct.Satisfied {
		if id.waitSet.Check(h) {
			id.hits = append(id.hits, hit{h, name, "name only"})
		}
	}
	if ct.Unsatisfied != nil {
		buf, _ := rdr.SetSource(c.entryBufs) // NOTE: an error is ignored here.
		bmc, _ := ct.BM.Identify("", buf)
		for r := range bmc {
			h := ct.Unsatisfied[r.Index()]
			if id.waitSet.Check(h) && id.checkHits(h) {
				id.hits = append(id.hits, hit{h, name, r.Basis()})
			}
		}
		rdr.Close()
		c.entryBufs.Put(buf)
	}
	return id.hits
}

// process the hits from the ctest: adding hits to the parts matched, checking priorities
// return true if satisfied and can quit
func (c *ContainerMatcher) processHits(hits []hit, id *identifier, ct *CTest, name string, res chan core.Result) bool {
	// if there are no hits, rule out any sigs in the ctest
	if len(hits) == 0 {
		for _, v := range ct.Satisfied {
			id.ruledOut[v] = true
		}
		for _, v := range ct.Unsatisfied {
			id.ruledOut[v] = true
		}
		return false
	}
	for _, h := range hits {
		id.partsMatched[h.id] = append(id.partsMatched[h.id], h)
		if len(id.partsMatched[h.id]) == c.Parts[h.id] {
			if id.waitSet.Check(h.id) {
				idx, _ := c.Priorities.Index(h.id)
				res <- toResult(c.Sindexes[idx], id.partsMatched[h.id]) // send a Result here
				// set a priority list and return early if can
				if id.waitSet.Put(h.id) {
					return true
				}
			}
		}
	}
	// if nothing ruled out by this test, then we must continue
	if len(hits) == len(ct.Satisfied)+len(ct.Unsatisfied) {
		return false
	}
	// we can rule some possible matches out...
	for _, v := range ct.Satisfied {
		if len(id.partsMatched[v]) == 0 || id.partsMatched[v][len(id.partsMatched[v])-1].name != name {
			id.ruledOut[v] = true
		}
	}
	for _, v := range ct.Unsatisfied {
		if len(id.partsMatched[v]) == 0 || id.partsMatched[v][len(id.partsMatched[v])-1].name != name {
			id.ruledOut[v] = true
		}
	}
	// if we haven't got a waitList yet, then we should return false
	waitingOn := id.waitSet.WaitingOn()
	if waitingOn == nil {
		return false
	}
	// loop over the wait list, seeing if they are all ruled out
	for _, v := range waitingOn {
		if !id.ruledOut[v] {
			return false
		}
	}
	return true
}

// eliminate duplicate hits - must do this since rely on number of matches for each sig as test for full match
func (id *identifier) checkHits(i int) bool {
	for _, h := range id.hits {
		if i == h.id {
			return false
		}
	}
	return true
}

func toResult(i int, h []hit) result {
	if len(h) == 0 {
		return result(h)
	}
	h[0].id += i
	return result(h)
}

type result []hit

func (r result) Index() int {
	if len(r) == 0 {
		return -1
	}
	return r[0].id
}

func (r result) Basis() string {
	var basis string
	for i, v := range r {
		if i < 1 {
			basis += "container "
		} else {
			basis += "; "
		}
		basis += "name " + v.name
		if len(v.basis) > 0 {
			basis += " with " + v.basis
		}
	}
	return basis
}

type hit struct {
	id    int
	name  string
	basis string
}

type defaultHit int

func (d defaultHit) Index() int {
	return int(d)
}

func (d defaultHit) Basis() string {
	return "container match with trigger and default extension"
}
<commit_msg>whoops - why shouldn't code in github html<commit_after>// Copyright 2014 Richard Lehane. 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 containermatcher

import (
	"strings"

	"github.com/richardlehane/siegfried/pkg/core"
	"github.com/richardlehane/siegfried/pkg/core/priority"
	"github.com/richardlehane/siegfried/pkg/core/siegreader"
)

func (m Matcher) Identify(n string, b siegreader.Buffer) (chan core.Result, error) {
	res := make(chan core.Result)
	// check trigger
	buf, err := b.Slice(0, 8)
	if err != nil {
		close(res)
		return res, nil
	}
	for _, c := range m {
		if c.trigger(buf) {
			rdr, err := c.rdr(b)
			if err != nil {
				close(res)
				return res, err
			}
			go c.identify(rdr, res)
			return res, nil
		}
	}
	// nothing ... move on
	close(res)
	return res, nil
}

type identifier struct {
	partsMatched [][]hit // hits for parts
	ruledOut     []bool  // mark additional signatures as negatively matched
	waitSet      *priority.WaitSet
	hits         []hit // shared buffer of hits used when matching
}

func (c *ContainerMatcher) newIdentifier(numParts int) *identifier {
	return &identifier{
		make([][]hit, numParts),
		make([]bool, numParts),
		c.Priorities.WaitSet(),
		make([]hit, 0, 1),
	}
}

func (c *ContainerMatcher) identify(rdr Reader, res chan core.Result) {
	// safe to call on a nil matcher (i.e. container matching switched off)
	if c == nil {
		close(res)
		return
	}
	id := c.newIdentifier(len(c.Parts))
	var err error
	var hit bool
	for err = rdr.Next(); err == nil; err = rdr.Next() {
		ct, ok := c.NameCTest[rdr.Name()]
		if !ok {
			continue
		}
		// name has matched, lets test the CTests
		// ct.identify will generate a slice of hits which pass to
		// processHits which will return true if we can stop
		if c.processHits(ct.identify(c, id, rdr, rdr.Name()), id, ct, rdr.Name(), res) {
			hit = true
			break
		}
	}
	// if we have no hits and a default value for this matcher, send it
	if !hit && c.Default {
		// the default is a negative value calculated from the CType
		res <- defaultHit(-1 - int(c.CType))
	}
	close(res)
}

func (ct *CTest) identify(c *ContainerMatcher, id *identifier, rdr Reader, name string) []hit {
	// reset hits
	id.hits = id.hits[:0]
	for _, h := range ct.Satisfied {
		if id.waitSet.Check(h) {
			id.hits = append(id.hits, hit{h, name, "name only"})
		}
	}
	if ct.Unsatisfied != nil {
		buf, _ := rdr.SetSource(c.entryBufs) // NOTE: an error is ignored here.
		bmc, _ := ct.BM.Identify("", buf)
		for r := range bmc {
			h := ct.Unsatisfied[r.Index()]
			if id.waitSet.Check(h) && id.checkHits(h) {
				id.hits = append(id.hits, hit{h, name, r.Basis()})
			}
		}
		rdr.Close()
		c.entryBufs.Put(buf)
	}
	return id.hits
}

// process the hits from the ctest: adding hits to the parts matched, checking priorities
// return true if satisfied and can quit
func (c *ContainerMatcher) processHits(hits []hit, id *identifier, ct *CTest, name string, res chan core.Result) bool {
	// if there are no hits, rule out any sigs in the ctest
	if len(hits) == 0 {
		for _, v := range ct.Satisfied {
			id.ruledOut[v] = true
		}
		for _, v := range ct.Unsatisfied {
			id.ruledOut[v] = true
		}
		return false
	}
	for _, h := range hits {
		id.partsMatched[h.id] = append(id.partsMatched[h.id], h)
		if len(id.partsMatched[h.id]) == c.Parts[h.id] {
			if id.waitSet.Check(h.id) {
				idx, _ := c.Priorities.Index(h.id)
				res <- toResult(c.Sindexes[idx], id.partsMatched[h.id]) // send a Result here
				// set a priority list and return early if can
				if id.waitSet.Put(h.id) {
					return true
				}
			}
		}
	}
	// if nothing ruled out by this test, then we must continue
	if len(hits) == len(ct.Satisfied)+len(ct.Unsatisfied) {
		return false
	}
	// we can rule some possible matches out...
	for _, v := range ct.Satisfied {
		if len(id.partsMatched[v]) == 0 || id.partsMatched[v][len(id.partsMatched[v])-1].name != name {
			id.ruledOut[v] = true
		}
	}
	for _, v := range ct.Unsatisfied {
		if len(id.partsMatched[v]) == 0 || id.partsMatched[v][len(id.partsMatched[v])-1].name != name {
			id.ruledOut[v] = true
		}
	}
	// if we haven't got a waitList yet, then we should return false
	waitingOn := id.waitSet.WaitingOn()
	if waitingOn == nil {
		return false
	}
	// loop over the wait list, seeing if they are all ruled out
	for _, v := range waitingOn {
		if !id.ruledOut[v] {
			return false
		}
	}
	return true
}

// eliminate duplicate hits - must do this since rely on number of matches for each sig as test for full match
func (id *identifier) checkHits(i int) bool {
	for _, h := range id.hits {
		if i == h.id {
			return false
		}
	}
	return true
}

func toResult(i int, h []hit) result {
	if len(h) == 0 {
		return result(h)
	}
	h[0].id += i
	return result(h)
}

type result []hit

func (r result) Index() int {
	if len(r) == 0 {
		return -1
	}
	return r[0].id
}

func (r result) Basis() string {
	var basis string
	for i, v := range r {
		if i < 1 {
			basis += "container "
		} else {
			basis += "; "
		}
		basis += "name " + v.name
		if len(v.basis) > 0 {
			basis += " with " + v.basis
		}
	}
	return basis
}

type hit struct {
	id    int
	name  string
	basis string
}

type defaultHit int

func (d defaultHit) Index() int {
	return int(d)
}

func (d defaultHit) Basis() string {
	return "container match with trigger and default extension"
}
<|endoftext|>


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

package gcscaching_test

import (
	"testing"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	. "github.com/jacobsa/ogletest"
)

func TestStatCache(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking cache
////////////////////////////////////////////////////////////////////////

type invariantsCache struct {
	wrapped gcscaching.StatCache
}

func (c *invariantsCache) Insert(
	o *gcs.Object,
	expiration time.Time) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Insert(o, expiration)
	return
}

func (c *invariantsCache) Erase(name string) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Erase(name)
	return
}

func (c *invariantsCache) LookUp(name string, now time.Time) (o *gcs.Object) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	o = c.wrapped.LookUp(name, now)
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

var someTime = time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local)

type StatCacheTest struct {
	cache invariantsCache
}

func init() { RegisterTestSuite(&StatCacheTest{}) }

func (t *StatCacheTest) SetUp(ti *TestInfo) {
	const capacity = 100
	t.cache.wrapped = gcscaching.NewStatCache(capacity)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *StatCacheTest) LookUpInEmptyCache() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) LookUpUnknownKey() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) KeysPresentButEverythingIsExpired() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) FillUpToCapacity() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) ExpiresLeastRecentlyUsed() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_NewerGeneration() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_NewerMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_SameMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_OlderMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_OlderGeneration() {
	AssertFalse(true, "TODO")
}
<commit_msg>StatCacheTest.LookUpInEmptyCache<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcscaching_test

import (
	"testing"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	. "github.com/jacobsa/ogletest"
)

func TestStatCache(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking cache
////////////////////////////////////////////////////////////////////////

type invariantsCache struct {
	wrapped gcscaching.StatCache
}

func (c *invariantsCache) Insert(
	o *gcs.Object,
	expiration time.Time) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Insert(o, expiration)
	return
}

func (c *invariantsCache) Erase(name string) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Erase(name)
	return
}

func (c *invariantsCache) LookUp(name string, now time.Time) (o *gcs.Object) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	o = c.wrapped.LookUp(name, now)
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

var someTime = time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local)

type StatCacheTest struct {
	cache invariantsCache
}

func init() { RegisterTestSuite(&StatCacheTest{}) }

func (t *StatCacheTest) SetUp(ti *TestInfo) {
	const capacity = 100
	t.cache.wrapped = gcscaching.NewStatCache(capacity)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *StatCacheTest) LookUpInEmptyCache() {
	ExpectEq(nil, t.cache.LookUp("", someTime))
	ExpectEq(nil, t.cache.LookUp("taco", someTime))
}

func (t *StatCacheTest) LookUpUnknownKey() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) KeysPresentButEverythingIsExpired() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) FillUpToCapacity() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) ExpiresLeastRecentlyUsed() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_NewerGeneration() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_NewerMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_SameMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_SameGeneration_OlderMetadataGen() {
	AssertFalse(true, "TODO")
}

func (t *StatCacheTest) Overwrite_OlderGeneration() {
	AssertFalse(true, "TODO")
}
<|endoftext|>


<commit_before>package cbext

import (
	"encoding/json"
	"errors"
	"github.com/couchbaselabs/go-couchbase"
	"reflect"
	"strconv"
	"strings"
)

func GetCurrentDatacenter() string {
	return "ryd"
}

func GetDatacenters() []string {
	return []string{"ryd", "awsSwe", "awsNY"}
}

// TODO: make the "unique"-check field contain keys to their values in the array, for faster access

func SilentUniqueAppendToArray(bucket *couchbase.Bucket, key string, value interface{}, unique string) (string, error) {
	var arraykey string
	var err error
	if err = AssertNotExists(bucket, key+":"+unique); err == nil {
		arraykey, err = AppendToArray(bucket, key, value)
		if err == nil {
			err = bucket.Set(key+":"+unique, 0, arraykey)
		}
	}
	return arraykey, err
}

func AssertNotExists(bucket *couchbase.Bucket, key string) error {
	_, err := bucket.GetRaw(key)
	if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
		return nil
	} else {
		return errors.New("KEY_EXISTS")
	}
}

func AppendToArray(bucket *couchbase.Bucket, key string, value interface{}) (string, error) {
	var keyValue int
	err := bucket.Get(key+"_"+GetCurrentDatacenter(), &keyValue)
	if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
		return "", err
	}

	if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
		bucket.Set(key+"_"+GetCurrentDatacenter(), 0, 0)
		keyValue = 0
	}

	keyValue += 1
	err = bucket.Set(key+"_"+GetCurrentDatacenter()+"_"+strconv.Itoa(keyValue), 0, value)

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

	err = bucket.Set(key+"_"+GetCurrentDatacenter(), 0, keyValue)
	if err != nil {
		return "", err
	}

	return key + "_" + GetCurrentDatacenter() + "_" + strconv.Itoa(keyValue), nil
}

func FlushArray(bucket *couchbase.Bucket, key string, value interface{}) error {
	count := 0
	for _, dc := range GetDatacenters() {
		val := 0
		err := bucket.Get(key+"_"+dc, &val)
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
		count += val
	}

	for _, dc := range GetDatacenters() {
		var keyValue int
		err := bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		for i := 1; i <= keyValue; i += 1 {
			err = bucket.Delete(key + "_" + dc + "_" + strconv.Itoa(i))
			if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
				return err
			}
		}
	}

	for _, dc := range GetDatacenters() {
		err := bucket.Delete(key + "_" + dc)
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
	}

	return nil
}

func DeleteUniqueArrayObject(bucket *couchbase.Bucket, key string, unique string) error {
	var arraykey string
	err := bucket.Get(key+":"+unique, &arraykey)
	if err != nil {
		return err
	}
	err = bucket.Delete(arraykey)
	if err != nil {
		return err
	}
	err = bucket.Delete(key + ":" + unique)
	if err != nil {
		return err
	}
	return nil
}

func DeleteArrayObject(bucket *couchbase.Bucket, key string, value interface{}) error {
	b, err := json.Marshal(value)
	if err != nil {
		return err
	}

	for _, dc := range GetDatacenters() {
		var keyValue int
		err = bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		for i := 1; i <= keyValue; i += 1 {
			var v []byte
			v, err = bucket.GetRaw(key + "_" + dc + "_" + strconv.Itoa(i))
			if err == nil {
				if string(b) == string(v) {
					return bucket.Delete(key + "_" + dc + "_" + strconv.Itoa(i))
				}
			}
		}
	}

	return errors.New("OBJECT_NOT_FOUND_IN_ARRAY")
}

func GetArray(bucket *couchbase.Bucket, key string, rv interface{}) error {
	//fmt.Printf("GetArray(bucket *couchbase.Bucket, key %#v, rv %#v)+n", key, rv)
	count := 0
	for _, dc := range GetDatacenters() {
		val := 0
		err := bucket.Get(key+"_"+dc, &val)
		count += val
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
	}

	//fmt.Println("got keys @", key)
	//time.Sleep(3 * time.Second)

	slice := reflect.ValueOf(rv).Elem()
	slice.Set(reflect.MakeSlice(slice.Type(), 0, count))

	for _, dc := range GetDatacenters() {
		var keyValue int
		err := bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		//fmt.Println("sleeping between datacenters")
		//time.Sleep(3 * time.Second)
		for i := 1; i <= keyValue; i += 1 {
			v := reflect.New(slice.Type().Elem())
			err = bucket.Get(key+"_"+dc+"_"+strconv.Itoa(i), v.Interface())
			if err == nil {
				slice.Set(reflect.Append(slice, v.Elem()))
			}
		}
	}
	//fmt.Println("GetArray done")
	return nil
}
<commit_msg>bugfixing<commit_after>package cbext

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/couchbaselabs/go-couchbase"
	"reflect"
	"strconv"
	"strings"
)

func GetCurrentDatacenter() string {
	return "ryd"
}

func GetDatacenters() []string {
	return []string{"ryd", "awsSwe", "awsNY"}
}

// TODO: make the "unique"-check field contain keys to their values in the array, for faster access

func SilentUniqueAppendToArray(bucket *couchbase.Bucket, key string, value interface{}, unique string) (string, error) {
	var arraykey string
	var err error
	if err = AssertNotExists(bucket, key+":"+unique); err == nil {
		arraykey, err = AppendToArray(bucket, key, value)
		if err == nil {
			err = bucket.Set(key+":"+unique, 0, arraykey)
		}
	}
	return arraykey, err
}

func AssertNotExists(bucket *couchbase.Bucket, key string) error {
	_, err := bucket.GetRaw(key)
	if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
		return nil
	} else {
		fmt.Println("AssertNotExists()", err.Error())
		return errors.New("KEY_EXISTS")
	}
}

func AppendToArray(bucket *couchbase.Bucket, key string, value interface{}) (string, error) {
	var keyValue int
	err := bucket.Get(key+"_"+GetCurrentDatacenter(), &keyValue)
	if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
		return "", err
	}

	if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
		bucket.Set(key+"_"+GetCurrentDatacenter(), 0, 0)
		keyValue = 0
	}

	keyValue += 1
	err = bucket.Set(key+"_"+GetCurrentDatacenter()+"_"+strconv.Itoa(keyValue), 0, value)

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

	err = bucket.Set(key+"_"+GetCurrentDatacenter(), 0, keyValue)
	if err != nil {
		return "", err
	}

	return key + "_" + GetCurrentDatacenter() + "_" + strconv.Itoa(keyValue), nil
}

func FlushArray(bucket *couchbase.Bucket, key string, value interface{}) error {
	count := 0
	for _, dc := range GetDatacenters() {
		val := 0
		err := bucket.Get(key+"_"+dc, &val)
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
		count += val
	}

	for _, dc := range GetDatacenters() {
		var keyValue int
		err := bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		for i := 1; i <= keyValue; i += 1 {
			err = bucket.Delete(key + "_" + dc + "_" + strconv.Itoa(i))
			if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
				return err
			}
		}
	}

	for _, dc := range GetDatacenters() {
		err := bucket.Delete(key + "_" + dc)
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
	}

	return nil
}

func DeleteUniqueArrayObject(bucket *couchbase.Bucket, key string, unique string) error {
	var arraykey string
	err := bucket.Get(key+":"+unique, &arraykey)
	if err != nil {
		return err
	}
	err = bucket.Delete(arraykey)
	if err != nil {
		return err
	}
	err = bucket.Delete(key + ":" + unique)
	if err != nil {
		return err
	}
	return nil
}

func DeleteArrayObject(bucket *couchbase.Bucket, key string, value interface{}) error {
	b, err := json.Marshal(value)
	if err != nil {
		return err
	}

	for _, dc := range GetDatacenters() {
		var keyValue int
		err = bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		for i := 1; i <= keyValue; i += 1 {
			var v []byte
			v, err = bucket.GetRaw(key + "_" + dc + "_" + strconv.Itoa(i))
			if err == nil {
				if string(b) == string(v) {
					return bucket.Delete(key + "_" + dc + "_" + strconv.Itoa(i))
				}
			}
		}
	}

	return errors.New("OBJECT_NOT_FOUND_IN_ARRAY")
}

func GetArray(bucket *couchbase.Bucket, key string, rv interface{}) error {
	//fmt.Printf("GetArray(bucket *couchbase.Bucket, key %#v, rv %#v)+n", key, rv)
	count := 0
	for _, dc := range GetDatacenters() {
		val := 0
		err := bucket.Get(key+"_"+dc, &val)
		count += val
		if err != nil && !strings.Contains(err.Error(), "KEY_ENOENT") {
			return err
		}
	}

	//fmt.Println("got keys @", key)
	//time.Sleep(3 * time.Second)

	slice := reflect.ValueOf(rv).Elem()
	slice.Set(reflect.MakeSlice(slice.Type(), 0, count))

	for _, dc := range GetDatacenters() {
		var keyValue int
		err := bucket.Get(key+"_"+dc, &keyValue)
		if err != nil && strings.Contains(err.Error(), "KEY_ENOENT") {
			continue
		}

		//fmt.Println("sleeping between datacenters")
		//time.Sleep(3 * time.Second)
		for i := 1; i <= keyValue; i += 1 {
			v := reflect.New(slice.Type().Elem())
			err = bucket.Get(key+"_"+dc+"_"+strconv.Itoa(i), v.Interface())
			if err == nil {
				slice.Set(reflect.Append(slice, v.Elem()))
			}
		}
	}
	//fmt.Println("GetArray done")
	return nil
}
<|endoftext|>