<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)

type Client struct {
	Name            string            //Name of the miner
	IP              string            //Ip to the cgminer including port
	Conn            net.Conn          //Connection made with net.Dial
	RefreshInterval int               //Seconds between fetching information
	MinerInfo       *MinerInformation //Struc to put the answers for the webserver
}

//Main function for fetching information from one client
func rpcClient(name, ip string, refInt int, minerInfo *MinerInformation) {
	//Add everything except the connection
	c := Client{name, ip, nil, refInt, minerInfo}

	clientRequests := make(chan RpcRequest)

	//Start the thread the will keep doing summary requests
	go SummaryHandler(clientRequests, minerInfo, &c)
	//Start another thread the will ask the devs requests
	go DevsHandler(clientRequests, minerInfo, &c)

	//Wait for new requst to make from the clienReequest channel
	for r := range clientRequests {
		//Create a new connection
		c.Conn = createConnection(c.IP)

		//Send the request to the cgminer
		b := sendCommand(&c.Conn, r.Request)
		/*
		 * Note:
		 *
		 * It seems that cgminer close the tcp connection
		 * after each call so we need to reset it for
		 * the next rpc-call
		 */
		c.Conn.Close()
		//And send back the result
		r.ResultChan <- b
	}
}

//Making summary requests to the cgminer and parse the result.
func SummaryHandler(res chan<- RpcRequest, minerInfo *MinerInformation, c *Client) {
	request := RpcRequest{"{\"command\":\"summary\"}", make(chan []byte)}

	var response []byte
	var summary SummaryResponse

	for {
		res <- request
		response = <-request.ResultChan

		fmt.Printf("Response: %s\n", response)
		err := json.Unmarshal(response, &summary)
		//Check for errors
		if err != nil {
			fmt.Println(err.Error())
		}
		//Lock it
		minerInfo.Mu.Lock()
		//Save the summary
		minerInfo.Summary = summary
		//Now unlock
		minerInfo.Mu.Unlock()

		//Now sleep
		time.Sleep(time.Duration(c.RefreshInterval) * time.Second)
	}
}

//Making devs request to the cgminer and parse the result
func DevsHandler(res chan<- RpcRequest, minerInfo *MinerInformation, c *Client) {
	request := RpcRequest{"{\"command\":\"devs\"}", make(chan []byte)}

	var response []byte
	var devs DevsResponse

	for {
		res <- request
		response = <-request.ResultChan

		fmt.Printf("Response: %s\n", response)
		err := json.Unmarshal(response, &devs)
		//Check for errors
		if err != nil {
			fmt.Println(err.Error())
		}
		//Lock it
		minerInfo.Mu.Lock()
		//Save the summary
		//minerInfo.Summary = summary
		//Now unlock
		minerInfo.Mu.Unlock()

		//Now sleep
		time.Sleep(time.Duration(c.RefreshInterval) * time.Second)
	}
}

// Returns a TCP connection to the ip
func createConnection(ip string) net.Conn {
	conn, err := net.Dial("tcp", ip)

	//Check for errors
	if err != nil {
		log.Println(err)
		return nil
	}

	return conn
}

// Sends a json rpc command to threw the socket and return the answer
func sendCommand(conn *net.Conn, cmd string) []byte {
	//Write the command to the socket
	fmt.Fprintf(*conn, cmd)
	//Read the response
	response, err := bufio.NewReader(*conn).ReadString('\n')
	//Check for any errors
	if err != nil {
		//Check for errors
		if err == io.EOF {
			/*
			 * Cgminer sends out EOF after each call.
			 * Catch this error because it's not really
			 * an error that crash the program.
			 */

		} else {
			//If the error is not EOF then warn about it
			log.Println("Sending command error: ", err)
		}
	}
	//Create the byte array
	b := []byte(response)

	/*
	 * Check for \x00 to remove
	 */
	if b[len(b)-1] == '\x00' {
		b = b[0 : len(b)-1]
	}

	//Return the status we got from the server
	return b
}

type RpcRequest struct {
	Request    string
	ResultChan chan []byte
}

/*
 * Bellow here is only structs defined
 * for converting json responces to
 * structs.
 */

////////////
// Status //
////////////
type StatusObject struct {
	Status      string `json:"STATUS"`
	When        int    `json:"When"`
	Code        int    `json:"Code"`
	Msg         string `json:"Msg"`
	Description string `json:"Description"`
}

/////////////
// summary //
/////////////
type SummaryResponse struct {
	Status  []StatusObject  `json:"STATUS"`
	Summary []SummaryObject `json:"SUMMARY"`
	Id      int             `json:"id"`
}

type SummaryObject struct {
	Elapsed            int     `json:"Elapsed"`
	MHSAv              float64 `json:"MHS av"`
	FoundBlocks        int     `json:"Found blocks"`
	Getworks           int     `json:"Getworks"`
	Accepted           int     `json:"Accepted"`
	Rejected           int     `json:"Rejected"`
	HardwareErrors     int     `json:"Hardware Errors"`
	Utility            float64 `json:"Utility"`
	Discarded          int     `json:"Discarded"`
	Stale              int     `json:"Stale"`
	GetFailures        int     `json:"Get Failures"`
	LocalWork          int     `json:"Local Work"`
	RemoteFailures     int     `json:"Remote Failures"`
	NetworkBlocks      int     `json:"Network Blocks"`
	TotalMH            float64 `json:"TotalMH"`
	WorkUtility        float64 `json:"Work Utility"`
	DifficultyAccepted float64 `json:"Difficulty Accepted"`
	DifficultyRejected float64 `json:"Difficulty Rejected"`
	DifficultyStale    float64 `json:"Difficulty Stale"`
	BestShare          int     `json:"Best Share"`
}

//////////
// devs //
//////////
type DevsResponse struct {
	Status []StatusObject `json:"STATUS"`
	Devs   []DevObject    `json:"DEVS"`
	Id     int            `json:"id"`
}

type DevObject struct {
	GPU                 int     `json:"GPU"`
	Enabled             string  `json:"Enabled"`
	Status              string  `json:"Status"`
	Temperature         float64 `json:"Temperature"`
	FanSpeed            int     `json:"Fan Speed"`
	FanPercent          int     `json:"Fan Percent"`
	GPUClock            int     `json:"GPU Clock"`
	MemoryClock         int     `json:"Memory Clock"`
	GPUVoltage          float64 `json:"GPU Voltage"`
	GPUActivity         int     `json:"GPU Activity"`
	Powertune           int     `json:"Powertune"`
	MHSAv               float64 `json:"MHS av"`
	MHS5s               float64 `json:"MHS 5s"`
	Accepted            int     `json:"Accepted"`
	Rejected            int     `json:"Rejected"`
	HardwareErros       int     `json:"Hardware Errors"`
	Utility             float64 `json:"Utility"`
	Intensity           string  `json:"Intensity"`
	LastSharePool       int     `json:"Last Share Pool"`
	LastShareTime       int     `json:"Last Share Time"`
	TotalMH             float64 `json:"Total MH"`
	Diff1Work           int     `json:"Diff1 Work"`
	DifficultyAccepted  float64 `json:"Difficulty Accepted"`
	DifficultyRejected  float64 `json:"Difficulty Rejected"`
	LastShareDifficulty float64 `json:"Last Share Difficulty"`
	LastValidWork       int     `json:"Last Valid Work"`
}
<commit_msg>Devs rpc calls now workin as they should<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)

type Client struct {
	Name            string            //Name of the miner
	IP              string            //Ip to the cgminer including port
	Conn            net.Conn          //Connection made with net.Dial
	RefreshInterval int               //Seconds between fetching information
	MinerInfo       *MinerInformation //Struc to put the answers for the webserver
}

//Main function for fetching information from one client
func rpcClient(name, ip string, refInt int, minerInfo *MinerInformation) {
	//Add everything except the connection
	c := Client{name, ip, nil, refInt, minerInfo}

	clientRequests := make(chan RpcRequest)

	//Start the thread the will keep doing summary requests
	go SummaryHandler(clientRequests, minerInfo, &c)
	//Start another thread the will ask the devs requests
	go DevsHandler(clientRequests, minerInfo, &c)

	//Wait for new requst to make from the clienReequest channel
	for r := range clientRequests {
		//Create a new connection
		c.Conn = createConnection(c.IP)

		//Send the request to the cgminer
		b := sendCommand(&c.Conn, r.Request)
		/*
		 * Note:
		 *
		 * It seems that cgminer close the tcp connection
		 * after each call so we need to reset it for
		 * the next rpc-call
		 */
		c.Conn.Close()
		//And send back the result
		r.ResultChan <- b
	}
}

//Making summary requests to the cgminer and parse the result.
func SummaryHandler(res chan<- RpcRequest, minerInfo *MinerInformation, c *Client) {
	request := RpcRequest{"{\"command\":\"summary\"}", make(chan []byte)}

	var response []byte
	var summary SummaryResponse

	for {
		res <- request
		response = <-request.ResultChan

		fmt.Printf("Response: %s\n", response)
		err := json.Unmarshal(response, &summary)
		//Check for errors
		if err != nil {
			fmt.Println(err.Error())
		}
		//Lock it
		minerInfo.Mu.Lock()
		//Save the summary
		minerInfo.Summary = summary
		//Now unlock
		minerInfo.Mu.Unlock()

		//Now sleep
		time.Sleep(time.Duration(c.RefreshInterval) * time.Second)
	}
}

//Making devs request to the cgminer and parse the result
func DevsHandler(res chan<- RpcRequest, minerInfo *MinerInformation, c *Client) {
	request := RpcRequest{"{\"command\":\"devs\"}", make(chan []byte)}

	var response []byte
	var devs DevsResponse

	for {
		res <- request
		response = <-request.ResultChan

		fmt.Printf("Response: %s\n", response)
		err := json.Unmarshal(response, &devs)
		//Check for errors
		if err != nil {
			fmt.Println(err.Error())
		}
		//Lock it
		minerInfo.Mu.Lock()
		//Save the summary
		minerInfo.Devs = devs
		//Now unlock
		minerInfo.Mu.Unlock()

		//Now sleep
		time.Sleep(time.Duration(c.RefreshInterval) * time.Second)
	}
}

// Returns a TCP connection to the ip
func createConnection(ip string) net.Conn {
	conn, err := net.Dial("tcp", ip)

	//Check for errors
	if err != nil {
		log.Println(err)
		return nil
	}

	return conn
}

// Sends a json rpc command to threw the socket and return the answer
func sendCommand(conn *net.Conn, cmd string) []byte {
	//Write the command to the socket
	fmt.Fprintf(*conn, cmd)
	//Read the response
	response, err := bufio.NewReader(*conn).ReadString('\n')
	//Check for any errors
	if err != nil {
		//Check for errors
		if err == io.EOF {
			/*
			 * Cgminer sends out EOF after each call.
			 * Catch this error because it's not really
			 * an error that crash the program.
			 */

		} else {
			//If the error is not EOF then warn about it
			log.Println("Sending command error: ", err)
		}
	}
	//Create the byte array
	b := []byte(response)

	/*
	 * Check for \x00 to remove
	 */
	if b[len(b)-1] == '\x00' {
		b = b[0 : len(b)-1]
	}

	//Return the status we got from the server
	return b
}

type RpcRequest struct {
	Request    string
	ResultChan chan []byte
}

/*
 * Bellow here is only structs defined
 * for converting json responces to
 * structs.
 */

////////////
// Status //
////////////
type StatusObject struct {
	Status      string `json:"STATUS"`
	When        int    `json:"When"`
	Code        int    `json:"Code"`
	Msg         string `json:"Msg"`
	Description string `json:"Description"`
}

/////////////
// summary //
/////////////
type SummaryResponse struct {
	Status  []StatusObject  `json:"STATUS"`
	Summary []SummaryObject `json:"SUMMARY"`
	Id      int             `json:"id"`
}

type SummaryObject struct {
	Elapsed            int     `json:"Elapsed"`
	MHSAv              float64 `json:"MHS av"`
	FoundBlocks        int     `json:"Found blocks"`
	Getworks           int     `json:"Getworks"`
	Accepted           int     `json:"Accepted"`
	Rejected           int     `json:"Rejected"`
	HardwareErrors     int     `json:"Hardware Errors"`
	Utility            float64 `json:"Utility"`
	Discarded          int     `json:"Discarded"`
	Stale              int     `json:"Stale"`
	GetFailures        int     `json:"Get Failures"`
	LocalWork          int     `json:"Local Work"`
	RemoteFailures     int     `json:"Remote Failures"`
	NetworkBlocks      int     `json:"Network Blocks"`
	TotalMH            float64 `json:"TotalMH"`
	WorkUtility        float64 `json:"Work Utility"`
	DifficultyAccepted float64 `json:"Difficulty Accepted"`
	DifficultyRejected float64 `json:"Difficulty Rejected"`
	DifficultyStale    float64 `json:"Difficulty Stale"`
	BestShare          int     `json:"Best Share"`
}

//////////
// devs //
//////////
type DevsResponse struct {
	Status []StatusObject `json:"STATUS"`
	Devs   []DevObject    `json:"DEVS"`
	Id     int            `json:"id"`
}

type DevObject struct {
	GPU                 int     `json:"GPU"`
	Enabled             string  `json:"Enabled"`
	Status              string  `json:"Status"`
	Temperature         float64 `json:"Temperature"`
	FanSpeed            int     `json:"Fan Speed"`
	FanPercent          int     `json:"Fan Percent"`
	GPUClock            int     `json:"GPU Clock"`
	MemoryClock         int     `json:"Memory Clock"`
	GPUVoltage          float64 `json:"GPU Voltage"`
	GPUActivity         int     `json:"GPU Activity"`
	Powertune           int     `json:"Powertune"`
	MHSAv               float64 `json:"MHS av"`
	MHS5s               float64 `json:"MHS 5s"`
	Accepted            int     `json:"Accepted"`
	Rejected            int     `json:"Rejected"`
	HardwareErros       int     `json:"Hardware Errors"`
	Utility             float64 `json:"Utility"`
	Intensity           string  `json:"Intensity"`
	LastSharePool       int     `json:"Last Share Pool"`
	LastShareTime       int     `json:"Last Share Time"`
	TotalMH             float64 `json:"Total MH"`
	Diff1Work           int     `json:"Diff1 Work"`
	DifficultyAccepted  float64 `json:"Difficulty Accepted"`
	DifficultyRejected  float64 `json:"Difficulty Rejected"`
	LastShareDifficulty float64 `json:"Last Share Difficulty"`
	LastValidWork       int     `json:"Last Valid Work"`
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

/* This function takes a string and returns
   a (potentially nil) error object */
func TTWS(filename string) error {
	/* Open the input file */
	inf, err := os.Open(filename);
	/* In case this function generates a "panic", be sure to close this file */
	defer inf.Close();
	/* Did we open it successfully?  If not, close and return. */
	if (err!=nil) { inf.Close(); return err; }

	/* Open the output file in system temp dir*/
	outf, err := ioutil.TempFile("","");
	/* In case this function generates a "panic", be sure to close this file */
	defer outf.Close();
	/* Did we open it succesfully?  If not, close all and return. */
	if (err!=nil) { inf.Close(); outf.Close(); return err; }
	/* Create a scanner object to break this in to lines */
	scanner := bufio.NewScanner(inf);
	/* Declare a variable for the line */
	var line string;
	/* Loop over lines */
	for scanner.Scan() {
		/* Trim right space and then add the \n back on the end before writing */
		line = strings.TrimRight(scanner.Text(), " \t")+"\n"
		outf.Write([]byte(line));
	}
	/* Close all open files */
	inf.Close();
	outf.Close();
	/* Replace the source file by the trimmed file */
	os.Rename(outf.Name(), filename);

	/* No errors, so we return nil */
	return nil;
}

func WalkFunc(path string, fi os.FileInfo, err error) error {
	/* list of directories to ignore */
	blacklist := []string{".bzr", ".cvs", ".git", ".hg", ".svn"}
	if contains(path, blacklist){
		fmt.Printf("Skipping version control dir: %s\n", path)
		return filepath.SkipDir
	} else {
		inf, err := os.Open(path)
		defer inf.Close();
		if (err!=nil) { inf.Close(); return err; }
		readStart := io.LimitReader(inf, 512);
		data, err := ioutil.ReadAll(readStart);
		/* Close all open files */
		inf.Close();
		/* Determine file type */
		fileType := http.DetectContentType(data);
		/* only act on text files */
		if (strings.Contains(fileType, "text/plain") && !fi.IsDir()){
			fmt.Printf("Trimming: %v\n", path);
			TTWS(path);
		} else if !fi.IsDir() {
			fmt.Printf("Skipping file of type '%v': %v\n", fileType, path)
		}
	}
	return nil
}

func contains(x string, a []string) bool {
	for _, e := range(a) {
		if (x==e) { return true; }
	}
	return false;
}

func main() {
	flag.Parse()
	root := flag.Arg(0)
	err := filepath.Walk(root, WalkFunc)
	fmt.Printf("filepath.Walk() returned %v\n", err)
}
<commit_msg>I don't think you need to close the file if the open failed...it was never open I suspect<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

/* This function takes a string and returns
   a (potentially nil) error object */
func TTWS(filename string) error {
	/* Open the input file */
	inf, err := os.Open(filename);
	/* In case this function generates a "panic", be sure to close this file */
	defer inf.Close();
	/* Did we open it successfully?  If not, close and return. */
	if (err!=nil) { return err; }

	/* Open the output file in system temp dir*/
	outf, err := ioutil.TempFile("","");
	/* In case this function generates a "panic", be sure to close this file */
	defer outf.Close();
	/* Did we open it succesfully?  If not, close all and return. */
	if (err!=nil) { inf.Close(); outf.Close(); return err; }
	/* Create a scanner object to break this in to lines */
	scanner := bufio.NewScanner(inf);
	/* Declare a variable for the line */
	var line string;
	/* Loop over lines */
	for scanner.Scan() {
		/* Trim right space and then add the \n back on the end before writing */
		line = strings.TrimRight(scanner.Text(), " \t")+"\n"
		outf.Write([]byte(line));
	}
	/* Close all open files */
	inf.Close();
	outf.Close();
	/* Replace the source file by the trimmed file */
	os.Rename(outf.Name(), filename);

	/* No errors, so we return nil */
	return nil;
}

func WalkFunc(path string, fi os.FileInfo, err error) error {
	/* list of directories to ignore */
	blacklist := []string{".bzr", ".cvs", ".git", ".hg", ".svn"}
	if contains(path, blacklist){
		fmt.Printf("Skipping version control dir: %s\n", path)
		return filepath.SkipDir
	} else {
		inf, err := os.Open(path)
		defer inf.Close();
		if (err!=nil) { return err; }
		readStart := io.LimitReader(inf, 512);
		data, err := ioutil.ReadAll(readStart);
		/* Close all open files */
		inf.Close();
		/* Determine file type */
		fileType := http.DetectContentType(data);
		/* only act on text files */
		if (strings.Contains(fileType, "text/plain") && !fi.IsDir()){
			fmt.Printf("Trimming: %v\n", path);
			TTWS(path);
		} else if !fi.IsDir() {
			fmt.Printf("Skipping file of type '%v': %v\n", fileType, path)
		}
	}
	return nil
}

func contains(x string, a []string) bool {
	for _, e := range(a) {
		if (x==e) { return true; }
	}
	return false;
}

func main() {
	flag.Parse()
	root := flag.Arg(0)
	err := filepath.Walk(root, WalkFunc)
	fmt.Printf("filepath.Walk() returned %v\n", err)
}
<|endoftext|>


<commit_before>// auth_verify project auth_verify.go
package auth_verify

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/ianmcmahon/encoding_ssh"
)

// Parse string Comma key value and return map
// key=value,key2=value2
func ParseCommaKeyValue(entireString string) (map[string]string, error) {
	m := make(map[string]string)
	var keyValueSlice []string
	for _, keyValueString := range strings.Split(entireString, ",") {
		keyValueSlice = strings.Split(keyValueString, "=")
		// strip "
		m[keyValueSlice[0]] = strings.Trim(keyValueSlice[1], "\"")
	}
	return m, nil
}

// Takes
func convertPkix(key interface{}) (*rsa.PublicKey, error) {
	// Marshal to ASN.1 DER encoding
	pkix, err := x509.MarshalPKIXPublicKey(key)
	if err != nil {
		return nil, fmt.Errorf("%v", err)
	}

	re, err := x509.ParsePKIXPublicKey([]byte(pkix))
	if err != nil {
		return nil, fmt.Errorf("Public key error in parsing :'%s'\n", err)
	}
	return re.(*rsa.PublicKey), nil
}

func ReadPublicKey(base_path string, path string) (*rsa.PublicKey, error) {
	//TODO: Must santize input for path

	bytes, err := ioutil.ReadFile(base_path + path)
	if err != nil {
		return nil, fmt.Errorf("Failed to read public key :'%v'", err)
	}
	// decode string ssh-rsa format to native type
	pub_key, err := ssh.DecodePublicKey(string(bytes))
	if err != nil {
		return nil, fmt.Errorf("Decoding public key failed :'%v'", err)
	}
	public_key, err := convertPkix(pub_key)
	if err != nil {
		return nil, fmt.Errorf("convert public key failed :'%v'", err)
	}
	return public_key, nil
}

// The CreateAuthorizationHeader returns the Authorization header for the give request.
// Only support SDCsignature for now
func ParseAuthorizationHeader(headers http.Header, isMantaRequest bool) (map[string]string, error) {
	//"Signature keyId='/user_test/keys/user_test',algorithm='rsa-sha256' FOUmNhldoFHsit6QkTedZDeOUbIcIY+1cgZAm7HYjx3B1r/r9826j0r18v1kW874uX0oLNhh33r1+pXlUgAZ+xkmelaFhh9fk8tsv3JIJGKZnF0pJjDs0oQ5mYT0W9TmEF6WHE3bhO2ipM1m1pCdLyFjTe0LTDJs4VPs0q+3u4MD4TUZq24TF+9XlHeEkVkUHAqhXqSTw2FXi9XheQonns3V0BQbitulkcIOkjHlp+IHedCbaD7l6tLawkiJaPIKZUWH4ugvnPwUhVAQDDxkJ9KGlCb2JWJArspCcI/dHqOwKDn1O+4s0t+pQqKlKl93YQSEaerZosaXdT8ux3vVXg=="
	// Check Authorization syntax based on SDC signature "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
	if authorization_header, ok := headers["Authorization"]; ok {
		authorization_header_slice := strings.Fields(authorization_header[0])
		if len(authorization_header_slice) != 3 {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Length is not correct")
		}
		if authorization_header_slice[0] != "Signature" {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Incorrect signature")
		}
		m, err := ParseCommaKeyValue(authorization_header_slice[1])
		if err != nil {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Key value parse error: %s", err)
		}
		// Add signature
		m["sig"] = authorization_header_slice[2]

		return m, nil
	} else {
		return make(map[string]string), fmt.Errorf("No Authorization header")
	}
}

func Verify(base_key_dir string, headers http.Header, isMantaRequest bool) (bool, error) {
	// Parse header
	m, err := ParseAuthorizationHeader(headers, false)
	if err != nil {
		return false, fmt.Errorf("%v", err)
	}
	my_key, err := ReadPublicKey(base_key_dir, m["keyId"])
	if err != nil {
		return false, fmt.Errorf("%v", err)
	}
	// Try to get the right hash if not will fall to default
	hashFunc := getHashFunction(m["algorithm"])

	if date, ok := headers["Date"]; ok {
		// TODO: Verify date formant and that its lagging or passing by 300 sec
		access, err := VerifySignature(my_key, hashFunc, date[0], m["sig"])
		if err != nil {
			return false, fmt.Errorf("%v", err)
		}
		return access, nil
	} else {
		return false, fmt.Errorf("No Date header")
	}
}

// sig is the signature
// signing String that will be hashed
func VerifySignature(public_key *rsa.PublicKey, hashFunc crypto.Hash, signing string, sig string) (bool, error) {
	hash := hashFunc.New()
	hash.Write([]byte(signing))
	digest := hash.Sum(nil)

	decoded_sign, err := base64.StdEncoding.DecodeString(sig)
	if err != nil {
		return false, fmt.Errorf("decode signed signature failed :'%s'\n", err)
	}
	err = rsa.VerifyPKCS1v15(public_key, hashFunc, digest, []byte(decoded_sign))
	if err != nil {
		return false, fmt.Errorf("An error occurred while signing the key: %s", err)
	} else {
		return true, nil
	}
}

// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
	switch strings.ToLower(algorithm) {
	case "rsa-sha1":
		hashFunc = crypto.SHA1
	case "rsa-sha224", "rsa-sha256":
		hashFunc = crypto.SHA256
	case "rsa-sha384", "rsa-sha512":
		hashFunc = crypto.SHA512
	default:
		hashFunc = crypto.SHA256
	}
	return
}
<commit_msg>Use some checks for parsing key value string<commit_after>// auth_verify project auth_verify.go
package auth_verify

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/ianmcmahon/encoding_ssh"
)

// Parse string Comma key value and return map
// key=value,key2=value2
func ParseCommaKeyValue(entireString string) (map[string]string, error) {
	m := make(map[string]string)
	if len(entireString) > 1 {
		var keyValueSlice []string
		for _, keyValueString := range strings.Split(entireString, ",") {

			keyValueSlice = strings.Split(keyValueString, "=")
			if len(keyValueSlice) != 2 {
				return m, nil
			} else {
				// strip "
				m[keyValueSlice[0]] = strings.Trim(keyValueSlice[1], "\"")
			}
		}
		return m, nil
	} else {
		return m, nil
	}
}

// Takes
func convertPkix(key interface{}) (*rsa.PublicKey, error) {
	// Marshal to ASN.1 DER encoding
	pkix, err := x509.MarshalPKIXPublicKey(key)
	if err != nil {
		return nil, fmt.Errorf("%v", err)
	}

	re, err := x509.ParsePKIXPublicKey([]byte(pkix))
	if err != nil {
		return nil, fmt.Errorf("Public key error in parsing :'%s'\n", err)
	}
	return re.(*rsa.PublicKey), nil
}

func ReadPublicKey(base_path string, path string) (*rsa.PublicKey, error) {
	//TODO: Must santize input for path

	bytes, err := ioutil.ReadFile(base_path + path)
	if err != nil {
		return nil, fmt.Errorf("Failed to read public key :'%v'", err)
	}
	// decode string ssh-rsa format to native type
	pub_key, err := ssh.DecodePublicKey(string(bytes))
	if err != nil {
		return nil, fmt.Errorf("Decoding public key failed :'%v'", err)
	}
	public_key, err := convertPkix(pub_key)
	if err != nil {
		return nil, fmt.Errorf("convert public key failed :'%v'", err)
	}
	return public_key, nil
}

// The CreateAuthorizationHeader returns the Authorization header for the give request.
// Only support SDCsignature for now
func ParseAuthorizationHeader(headers http.Header, isMantaRequest bool) (map[string]string, error) {
	//"Signature keyId='/user_test/keys/user_test',algorithm='rsa-sha256' FOUmNhldoFHsit6QkTedZDeOUbIcIY+1cgZAm7HYjx3B1r/r9826j0r18v1kW874uX0oLNhh33r1+pXlUgAZ+xkmelaFhh9fk8tsv3JIJGKZnF0pJjDs0oQ5mYT0W9TmEF6WHE3bhO2ipM1m1pCdLyFjTe0LTDJs4VPs0q+3u4MD4TUZq24TF+9XlHeEkVkUHAqhXqSTw2FXi9XheQonns3V0BQbitulkcIOkjHlp+IHedCbaD7l6tLawkiJaPIKZUWH4ugvnPwUhVAQDDxkJ9KGlCb2JWJArspCcI/dHqOwKDn1O+4s0t+pQqKlKl93YQSEaerZosaXdT8ux3vVXg=="
	// Check Authorization syntax based on SDC signature "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
	if authorization_header, ok := headers["Authorization"]; ok {
		authorization_header_slice := strings.Fields(authorization_header[0])
		if len(authorization_header_slice) != 3 {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Length is not correct")
		}
		if authorization_header_slice[0] != "Signature" {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Incorrect signature")
		}
		m, err := ParseCommaKeyValue(authorization_header_slice[1])
		if err != nil {
			return make(map[string]string), fmt.Errorf("Authorization header malformed. Key value parse error: %s", err)
		}
		// Add signature
		m["sig"] = authorization_header_slice[2]

		return m, nil
	} else {
		return make(map[string]string), fmt.Errorf("No Authorization header")
	}
}

func Verify(base_key_dir string, headers http.Header, isMantaRequest bool) (bool, error) {
	// Parse header
	m, err := ParseAuthorizationHeader(headers, false)
	if err != nil {
		return false, fmt.Errorf("%v", err)
	}
	my_key, err := ReadPublicKey(base_key_dir, m["keyId"])
	if err != nil {
		return false, fmt.Errorf("%v", err)
	}
	// Try to get the right hash if not will fall to default
	hashFunc := getHashFunction(m["algorithm"])

	if date, ok := headers["Date"]; ok {
		// TODO: Verify date formant and that its lagging or passing by 300 sec
		access, err := VerifySignature(my_key, hashFunc, date[0], m["sig"])
		if err != nil {
			return false, fmt.Errorf("%v", err)
		}
		return access, nil
	} else {
		return false, fmt.Errorf("No Date header")
	}
}

// sig is the signature
// signing String that will be hashed
func VerifySignature(public_key *rsa.PublicKey, hashFunc crypto.Hash, signing string, sig string) (bool, error) {
	hash := hashFunc.New()
	hash.Write([]byte(signing))
	digest := hash.Sum(nil)

	decoded_sign, err := base64.StdEncoding.DecodeString(sig)
	if err != nil {
		return false, fmt.Errorf("decode signed signature failed :'%s'\n", err)
	}
	err = rsa.VerifyPKCS1v15(public_key, hashFunc, digest, []byte(decoded_sign))
	if err != nil {
		return false, fmt.Errorf("An error occurred while signing the key: %s", err)
	} else {
		return true, nil
	}
}

// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
	switch strings.ToLower(algorithm) {
	case "rsa-sha1":
		hashFunc = crypto.SHA1
	case "rsa-sha224", "rsa-sha256":
		hashFunc = crypto.SHA256
	case "rsa-sha384", "rsa-sha512":
		hashFunc = crypto.SHA512
	default:
		hashFunc = crypto.SHA256
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/Shopify/sarama"
	"github.com/bsm/sarama-cluster"
	"github.com/golang/snappy"
	"github.com/jpillora/backoff"
	"github.com/raintank/worldping-api/pkg/log"
	"gopkg.in/raintank/schema.v1"
	"gopkg.in/raintank/schema.v1/msg"
)

var (
	producerBatchSize = flag.Int("batch-size", 10000, "number of metrics to send in each batch.")
	destinationURL    = flag.String("destination-url", "http://localhost/metrics", "tsdb-gw address to send metrics to")
	destinationKey    = flag.String("destination-key", "admin-key", "admin-key of destination tsdb-gw server")

	group                = flag.String("group", "mt-replicator", "Kafka consumer group")
	clientID             = flag.String("client-id", "$HOSTNAME", "Kafka consumer group client id")
	srcTopic             = flag.String("src-topic", "mdm", "metrics topic name on source cluster")
	initialOffset        = flag.Int("initial-offset", -2, "initial offset to consume from. (-2=oldest, -1=newest)")
	srcBrokerStr         = flag.String("src-brokers", "localhost:9092", "tcp address of source kafka cluster (may be be given multiple times as a comma-separated list)")
	consumerFetchDefault = flag.Int("consumer-fetch-default", 32768, "number of bytes to try and fetch from consumer")
)

type writeRequest struct {
	data      []byte
	count     int
	offset    int64
	topic     string
	partition int32
}

type MetricsReplicator struct {
	consumer    *cluster.Consumer
	tsdbClient  *http.Client
	tsdbUrl     string
	tsdbKey     string
	writeQueue  chan *writeRequest
	wg          sync.WaitGroup
	shutdown    chan struct{}
	flushBuffer chan []*schema.MetricData
}

func NewMetricsReplicator() (*MetricsReplicator, error) {
	if *group == "" {
		log.Fatal(4, "--group is required")
	}

	if *srcBrokerStr == "" {
		log.Fatal(4, "--src-brokers required")
	}
	if *srcTopic == "" {
		log.Fatal(4, "--src-topic is required")
	}

	if *clientID == "$HOSTNAME" {
		*clientID, _ = os.Hostname()
	}

	srcBrokers := strings.Split(*srcBrokerStr, ",")

	config := cluster.NewConfig()
	config.Consumer.Offsets.Initial = int64(*initialOffset)
	config.ClientID = *clientID
	config.Group.Return.Notifications = true
	config.ChannelBufferSize = 1000
	config.Consumer.Fetch.Min = 1
	config.Consumer.Fetch.Default = int32(*consumerFetchDefault)
	config.Consumer.MaxWaitTime = time.Second
	config.Consumer.MaxProcessingTime = time.Second * time.Duration(5)
	config.Config.Version = sarama.V0_10_0_0

	err := config.Validate()
	if err != nil {
		return nil, err
	}
	consumer, err := cluster.NewConsumer(srcBrokers, *group, []string{*srcTopic}, config)
	if err != nil {
		log.Error(3, "failed to connect to source brokers %v.", srcBrokers)
		return nil, err
	}
	tsdbClient := &http.Client{
		Timeout: time.Duration(10) * time.Second,
	}

	return &MetricsReplicator{
		consumer:   consumer,
		tsdbClient: tsdbClient,
		tsdbKey:    *destinationKey,
		tsdbUrl:    *destinationURL,

		shutdown:   make(chan struct{}),
		writeQueue: make(chan *writeRequest, 10),
	}, nil
}

func (r *MetricsReplicator) Start() {
	r.wg.Add(1)
	go r.consume()
	r.wg.Add(1)
	go r.flush()
}

func (r *MetricsReplicator) Stop() {
	r.consumer.Close()
	close(r.shutdown)
	log.Info("Consumer closed.")
	done := make(chan struct{})
	go func() {
		r.wg.Wait()
		close(done)
	}()

	select {
	case <-time.After(time.Minute):
		log.Info("shutdown not complete after 1 minute. Abandoning inflight data.")
	case <-done:
		log.Info("shutdown complete.")
	}
	return
}

func (r *MetricsReplicator) consume() {
	buf := make([]*schema.MetricData, 0)
	accountingTicker := time.NewTicker(time.Second * 10)
	flushTicker := time.NewTicker(time.Second)
	counter := 0
	counterTs := time.Now()
	msgChan := r.consumer.Messages()

	flush := func(topic string, partition int32, offset int64) {
		mda := schema.MetricDataArray(buf)
		data, err := msg.CreateMsg(mda, 0, msg.FormatMetricDataArrayMsgp)
		if err != nil {
			panic(err)
		}
		// this will block when the writeQueue fills up. This will happen if
		// we are consuming at a faster rate then we can publish, or if publishing
		// is failing for some reason.
		r.writeQueue <- &writeRequest{
			data:      data,
			topic:     topic,
			partition: partition,
			offset:    offset,
			count:     len(buf),
		}
		counter += len(buf)
		buf = buf[:0]
	}

	var m *sarama.ConsumerMessage
	var ok bool
	defer func() {
		close(r.writeQueue)
		r.wg.Done()
	}()
	for {
		select {
		case m, ok = <-msgChan:
			if !ok {
				if len(buf) != 0 {
					flush(m.Topic, m.Partition, m.Offset)
				}
				return
			}
			md := &schema.MetricData{}
			_, err := md.UnmarshalMsg(m.Value)
			if err != nil {
				log.Error(3, "kafka-mdm decode error, skipping message. %s", err)
				continue
			}

			buf = append(buf, md)
			if len(buf) > *producerBatchSize {
				flush(m.Topic, m.Partition, m.Offset)
				// reset our ticker
				flushTicker.Stop()
				flushTicker = time.NewTicker(time.Second)
			}
		case <-flushTicker.C:
			if len(buf) == 0 {
				continue
			}
			flush(m.Topic, m.Partition, m.Offset)
		case t := <-accountingTicker.C:
			log.Info("%d metrics processed in last %.1fseconds.", counter, t.Sub(counterTs).Seconds())
			counter = 0
			counterTs = t
		case <-r.shutdown:
			if len(buf) > 0 {
				flush(m.Topic, m.Partition, m.Offset)
			}
			return
		}
	}
}

func (r *MetricsReplicator) flush() {
	b := &backoff.Backoff{
		Min:    100 * time.Millisecond,
		Max:    time.Minute,
		Factor: 1.5,
		Jitter: true,
	}
	body := new(bytes.Buffer)
	defer r.wg.Done()
	for wr := range r.writeQueue {
		for {
			pre := time.Now()
			body.Reset()
			snappyBody := snappy.NewWriter(body)
			snappyBody.Write(wr.data)
			req, err := http.NewRequest("POST", r.tsdbUrl, body)
			if err != nil {
				panic(err)
			}
			req.Header.Add("Authorization", "Bearer "+r.tsdbKey)
			req.Header.Add("Content-Type", "rt-metric-binary-snappy")
			resp, err := r.tsdbClient.Do(req)
			diff := time.Since(pre)
			if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
				// the payload has been successfully sent so lets mark our offset
				r.consumer.MarkPartitionOffset(wr.topic, wr.partition, wr.offset, "")

				b.Reset()
				log.Info("GrafanaNet sent %d metrics in %s -msg size %d", wr.count, diff, body.Len())
				resp.Body.Close()
				ioutil.ReadAll(resp.Body)
				break
			}
			dur := b.Duration()
			if err != nil {
				log.Warn("GrafanaNet failed to submit data: %s will try again in %s (this attempt took %s)", err, dur, diff)
			} else {
				buf := make([]byte, 300)
				n, _ := resp.Body.Read(buf)
				log.Warn("GrafanaNet failed to submit data: http %d - %s will try again in %s (this attempt took %s)", resp.StatusCode, buf[:n], dur, diff)
				resp.Body.Close()
				ioutil.ReadAll(resp.Body)
			}

			time.Sleep(dur)
		}
	}
}
<commit_msg>log kafka  consumer notifications<commit_after>package main

import (
	"bytes"
	"flag"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/Shopify/sarama"
	"github.com/bsm/sarama-cluster"
	"github.com/golang/snappy"
	"github.com/jpillora/backoff"
	"github.com/raintank/worldping-api/pkg/log"
	"gopkg.in/raintank/schema.v1"
	"gopkg.in/raintank/schema.v1/msg"
)

var (
	producerBatchSize = flag.Int("batch-size", 10000, "number of metrics to send in each batch.")
	destinationURL    = flag.String("destination-url", "http://localhost/metrics", "tsdb-gw address to send metrics to")
	destinationKey    = flag.String("destination-key", "admin-key", "admin-key of destination tsdb-gw server")

	group                = flag.String("group", "mt-replicator", "Kafka consumer group")
	clientID             = flag.String("client-id", "$HOSTNAME", "Kafka consumer group client id")
	srcTopic             = flag.String("src-topic", "mdm", "metrics topic name on source cluster")
	initialOffset        = flag.Int("initial-offset", -2, "initial offset to consume from. (-2=oldest, -1=newest)")
	srcBrokerStr         = flag.String("src-brokers", "localhost:9092", "tcp address of source kafka cluster (may be be given multiple times as a comma-separated list)")
	consumerFetchDefault = flag.Int("consumer-fetch-default", 32768, "number of bytes to try and fetch from consumer")
)

type writeRequest struct {
	data      []byte
	count     int
	offset    int64
	topic     string
	partition int32
}

type MetricsReplicator struct {
	consumer    *cluster.Consumer
	tsdbClient  *http.Client
	tsdbUrl     string
	tsdbKey     string
	writeQueue  chan *writeRequest
	wg          sync.WaitGroup
	shutdown    chan struct{}
	flushBuffer chan []*schema.MetricData
}

func NewMetricsReplicator() (*MetricsReplicator, error) {
	if *group == "" {
		log.Fatal(4, "--group is required")
	}

	if *srcBrokerStr == "" {
		log.Fatal(4, "--src-brokers required")
	}
	if *srcTopic == "" {
		log.Fatal(4, "--src-topic is required")
	}

	if *clientID == "$HOSTNAME" {
		*clientID, _ = os.Hostname()
	}

	srcBrokers := strings.Split(*srcBrokerStr, ",")

	config := cluster.NewConfig()
	config.Consumer.Offsets.Initial = int64(*initialOffset)
	config.ClientID = *clientID
	config.Group.Return.Notifications = true
	config.ChannelBufferSize = 1000
	config.Consumer.Fetch.Min = 1
	config.Consumer.Fetch.Default = int32(*consumerFetchDefault)
	config.Consumer.MaxWaitTime = time.Second
	config.Consumer.MaxProcessingTime = time.Second * time.Duration(5)
	config.Config.Version = sarama.V0_10_0_0

	err := config.Validate()
	if err != nil {
		return nil, err
	}
	consumer, err := cluster.NewConsumer(srcBrokers, *group, []string{*srcTopic}, config)
	if err != nil {
		log.Error(3, "failed to connect to source brokers %v.", srcBrokers)
		return nil, err
	}
	tsdbClient := &http.Client{
		Timeout: time.Duration(10) * time.Second,
	}

	return &MetricsReplicator{
		consumer:   consumer,
		tsdbClient: tsdbClient,
		tsdbKey:    *destinationKey,
		tsdbUrl:    *destinationURL,

		shutdown:   make(chan struct{}),
		writeQueue: make(chan *writeRequest, 10),
	}, nil
}

func (r *MetricsReplicator) Start() {
	r.wg.Add(1)
	go r.consume()
	r.wg.Add(1)
	go r.flush()
}

func (r *MetricsReplicator) Stop() {
	r.consumer.Close()
	close(r.shutdown)
	log.Info("Consumer closed.")
	done := make(chan struct{})
	go func() {
		r.wg.Wait()
		close(done)
	}()

	select {
	case <-time.After(time.Minute):
		log.Info("shutdown not complete after 1 minute. Abandoning inflight data.")
	case <-done:
		log.Info("shutdown complete.")
	}
	return
}

func (r *MetricsReplicator) consume() {
	buf := make([]*schema.MetricData, 0)
	accountingTicker := time.NewTicker(time.Second * 10)
	flushTicker := time.NewTicker(time.Second)
	counter := 0
	counterTs := time.Now()
	msgChan := r.consumer.Messages()
	notificationsChan := r.consumer.Notifications()

	flush := func(topic string, partition int32, offset int64) {
		mda := schema.MetricDataArray(buf)
		data, err := msg.CreateMsg(mda, 0, msg.FormatMetricDataArrayMsgp)
		if err != nil {
			panic(err)
		}
		// this will block when the writeQueue fills up. This will happen if
		// we are consuming at a faster rate then we can publish, or if publishing
		// is failing for some reason.
		r.writeQueue <- &writeRequest{
			data:      data,
			topic:     topic,
			partition: partition,
			offset:    offset,
			count:     len(buf),
		}
		counter += len(buf)
		buf = buf[:0]
	}

	var m *sarama.ConsumerMessage
	var ok bool
	defer func() {
		close(r.writeQueue)
		r.wg.Done()
	}()
	for {
		select {
		case m, ok = <-msgChan:
			if !ok {
				if len(buf) != 0 {
					flush(m.Topic, m.Partition, m.Offset)
				}
				return
			}
			md := &schema.MetricData{}
			_, err := md.UnmarshalMsg(m.Value)
			if err != nil {
				log.Error(3, "kafka-mdm decode error, skipping message. %s", err)
				continue
			}

			buf = append(buf, md)
			if len(buf) > *producerBatchSize {
				flush(m.Topic, m.Partition, m.Offset)
				// reset our ticker
				flushTicker.Stop()
				flushTicker = time.NewTicker(time.Second)
			}
		case <-flushTicker.C:
			if len(buf) == 0 {
				continue
			}
			flush(m.Topic, m.Partition, m.Offset)
		case t := <-accountingTicker.C:
			log.Info("%d metrics processed in last %.1fseconds.", counter, t.Sub(counterTs).Seconds())
			counter = 0
			counterTs = t
		case <-r.shutdown:
			if len(buf) > 0 {
				flush(m.Topic, m.Partition, m.Offset)
			}
			return
		case msg := <-notificationsChan:
			if len(msg.Claimed) > 0 {
				for topic, partitions := range msg.Claimed {
					log.Info("kafka consumer claimed %d partitions on topic: %s", len(partitions), topic)
				}
			}
			if len(msg.Released) > 0 {
				for topic, partitions := range msg.Released {
					log.Info("kafka consumer released %d partitions on topic: %s", len(partitions), topic)
				}
			}

			if len(msg.Current) == 0 {
				log.Info("kafka consumer is no longer consuming from any partitions.")
			} else {
				log.Info("kafka Current partitions:")
				for topic, partitions := range msg.Current {
					log.Info("kafka Current partitions: %s: %v", topic, partitions)
				}
			}
		}
	}
}

func (r *MetricsReplicator) flush() {
	b := &backoff.Backoff{
		Min:    100 * time.Millisecond,
		Max:    time.Minute,
		Factor: 1.5,
		Jitter: true,
	}
	body := new(bytes.Buffer)
	defer r.wg.Done()
	for wr := range r.writeQueue {
		for {
			pre := time.Now()
			body.Reset()
			snappyBody := snappy.NewWriter(body)
			snappyBody.Write(wr.data)
			req, err := http.NewRequest("POST", r.tsdbUrl, body)
			if err != nil {
				panic(err)
			}
			req.Header.Add("Authorization", "Bearer "+r.tsdbKey)
			req.Header.Add("Content-Type", "rt-metric-binary-snappy")
			resp, err := r.tsdbClient.Do(req)
			diff := time.Since(pre)
			if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
				// the payload has been successfully sent so lets mark our offset
				r.consumer.MarkPartitionOffset(wr.topic, wr.partition, wr.offset, "")

				b.Reset()
				log.Info("GrafanaNet sent %d metrics in %s -msg size %d", wr.count, diff, body.Len())
				resp.Body.Close()
				ioutil.ReadAll(resp.Body)
				break
			}
			dur := b.Duration()
			if err != nil {
				log.Warn("GrafanaNet failed to submit data: %s will try again in %s (this attempt took %s)", err, dur, diff)
			} else {
				buf := make([]byte, 300)
				n, _ := resp.Body.Read(buf)
				log.Warn("GrafanaNet failed to submit data: http %d - %s will try again in %s (this attempt took %s)", resp.StatusCode, buf[:n], dur, diff)
				resp.Body.Close()
				ioutil.ReadAll(resp.Body)
			}

			time.Sleep(dur)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017-2018 Authors of Cilium
//
// 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 utils

import (
	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy/api"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// subsysK8s is the value for logfields.LogSubsys
	subsysK8s = "k8s"
	// podPrefixLbl is the value the prefix used in the label selector to
	// represent pods on the default namespace.
	podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel

	// podAnyPrefixLbl is the value of the prefix used in the label selector to
	// represent pods in the default namespace for any source type.
	podAnyPrefixLbl = labels.LabelSourceAnyKeyPrefix + k8sConst.PodNamespaceLabel

	// podInitLbl is the label used in a label selector to match on
	// initializing pods.
	podInitLbl = labels.LabelSourceReservedKeyPrefix + labels.IDNameInit

	// ResourceTypeCiliumNetworkPolicy is the resource type used for the
	// PolicyLabelDerivedFrom label
	ResourceTypeCiliumNetworkPolicy = "CiliumNetworkPolicy"
)

var (
	// log is the k8s package logger object.
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s)
)

// GetPolicyLabels returns a LabelArray for the given namespace and name.
func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray {
	return labels.LabelArray{
		labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelUID, string(uid), labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelNamespace, ns, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelDerivedFrom, derivedFrom, labels.LabelSourceK8s),
	}
}

// getEndpointSelector converts the provided labelSelector into an EndpointSelector,
// adding the relevant matches for namespaces based on the provided options.
func getEndpointSelector(namespace string, labelSelector *metav1.LabelSelector, addK8sPrefix, matchesInit bool) api.EndpointSelector {
	es := api.NewESFromK8sLabelSelector("", labelSelector)

	// There's no need to prefixed K8s
	// prefix for reserved labels
	if addK8sPrefix && es.HasKeyPrefix(labels.LabelSourceReservedKeyPrefix) {
		return es
	}

	// The user can explicitly specify the namespace in the
	// FromEndpoints selector. If omitted, we limit the
	// scope to the namespace the policy lives in.
	//
	// Policies applying on initializing pods are a special case.
	// Those pods don't have any labels, so they don't have a namespace label either.
	// Don't add a namespace label to those endpoint selectors, or we wouldn't be
	// able to match on those pods.
	if !matchesInit && !es.HasKey(podPrefixLbl) && !es.HasKey(podAnyPrefixLbl) {
		es.AddMatch(podPrefixLbl, namespace)
	}

	return es
}

func parseToCiliumIngressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Ingress != nil {
		retRule.Ingress = make([]api.IngressRule, len(inRule.Ingress))
		for i, ing := range inRule.Ingress {
			if ing.FromEndpoints != nil {
				retRule.Ingress[i].FromEndpoints = make([]api.EndpointSelector, len(ing.FromEndpoints))
				for j, ep := range ing.FromEndpoints {
					retRule.Ingress[i].FromEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if ing.ToPorts != nil {
				retRule.Ingress[i].ToPorts = make([]api.PortRule, len(ing.ToPorts))
				copy(retRule.Ingress[i].ToPorts, ing.ToPorts)
			}
			if ing.FromCIDR != nil {
				retRule.Ingress[i].FromCIDR = make([]api.CIDR, len(ing.FromCIDR))
				copy(retRule.Ingress[i].FromCIDR, ing.FromCIDR)
			}

			if ing.FromCIDRSet != nil {
				retRule.Ingress[i].FromCIDRSet = make([]api.CIDRRule, len(ing.FromCIDRSet))
				copy(retRule.Ingress[i].FromCIDRSet, ing.FromCIDRSet)
			}

			if ing.FromRequires != nil {
				retRule.Ingress[i].FromRequires = make([]api.EndpointSelector, len(ing.FromRequires))
				for j, ep := range ing.FromRequires {
					retRule.Ingress[i].FromRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if ing.FromEntities != nil {
				retRule.Ingress[i].FromEntities = make([]api.Entity, len(ing.FromEntities))
				copy(retRule.Ingress[i].FromEntities, ing.FromEntities)
			}
		}
	}
}

func parseToCiliumEgressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Egress != nil {
		retRule.Egress = make([]api.EgressRule, len(inRule.Egress))

		for i, egr := range inRule.Egress {
			if egr.ToEndpoints != nil {
				retRule.Egress[i].ToEndpoints = make([]api.EndpointSelector, len(egr.ToEndpoints))
				for j, ep := range egr.ToEndpoints {
					retRule.Egress[i].ToEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if egr.ToPorts != nil {
				retRule.Egress[i].ToPorts = make([]api.PortRule, len(egr.ToPorts))
				copy(retRule.Egress[i].ToPorts, egr.ToPorts)
			}
			if egr.ToCIDR != nil {
				retRule.Egress[i].ToCIDR = make([]api.CIDR, len(egr.ToCIDR))
				copy(retRule.Egress[i].ToCIDR, egr.ToCIDR)
			}

			if egr.ToCIDRSet != nil {
				retRule.Egress[i].ToCIDRSet = make(api.CIDRRuleSlice, len(egr.ToCIDRSet))
				copy(retRule.Egress[i].ToCIDRSet, egr.ToCIDRSet)
			}

			if egr.ToRequires != nil {
				retRule.Egress[i].ToRequires = make([]api.EndpointSelector, len(egr.ToRequires))
				for j, ep := range egr.ToRequires {
					retRule.Egress[i].ToRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if egr.ToServices != nil {
				retRule.Egress[i].ToServices = make([]api.Service, len(egr.ToServices))
				copy(retRule.Egress[i].ToServices, egr.ToServices)
			}

			if egr.ToEntities != nil {
				retRule.Egress[i].ToEntities = make([]api.Entity, len(egr.ToEntities))
				copy(retRule.Egress[i].ToEntities, egr.ToEntities)
			}

			if egr.ToFQDNs != nil {
				retRule.Egress[i].ToFQDNs = make([]api.FQDNSelector, len(egr.ToFQDNs))
				copy(retRule.Egress[i].ToFQDNs, egr.ToFQDNs)
			}

			if egr.ToGroups != nil {
				retRule.Egress[i].ToGroups = make([]api.ToGroups, len(egr.ToGroups))
				copy(retRule.Egress[i].ToGroups, egr.ToGroups)
			}
		}
	}
}

// namespacesAreValid checks the set of namespaces from a rule returns true if
// they are not specified, or if they are specified and match the namespace
// where the rule is being inserted.
func namespacesAreValid(namespace string, userNamespaces []string) bool {
	return len(userNamespaces) == 0 ||
		(len(userNamespaces) == 1 && userNamespaces[0] == namespace)
}

// ParseToCiliumRule returns an api.Rule with all the labels parsed into cilium
// labels.
func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule {
	retRule := &api.Rule{}
	if r.EndpointSelector.LabelSelector != nil {
		retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector)
		// The PodSelector should only reflect to the same namespace
		// the policy is being stored, thus we add the namespace to
		// the MatchLabels map.
		//
		// Policies applying on initializing pods are a special case.
		// Those pods don't have any labels, so they don't have a namespace label either.
		// Don't add a namespace label to those endpoint selectors, or we wouldn't be
		// able to match on those pods.
		if !retRule.EndpointSelector.HasKey(podInitLbl) {
			userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl)
			if present && !namespacesAreValid(namespace, userNamespace) {
				log.WithFields(logrus.Fields{
					logfields.K8sNamespace:              namespace,
					logfields.CiliumNetworkPolicyName:   name,
					logfields.K8sNamespace + ".illegal": userNamespace,
				}).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." +
					" EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.")
			}
			retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace)
		}
	}

	parseToCiliumIngressRule(namespace, r, retRule)
	parseToCiliumEgressRule(namespace, r, retRule)

	retRule.Labels = ParseToCiliumLabels(namespace, name, uid, r.Labels)

	retRule.Description = r.Description

	return retRule
}

// ParseToCiliumLabels returns all ruleLbls appended with a specific label that
// represents the given namespace and name along with a label that specifies
// these labels were derived from a CiliumNetworkPolicy.
func ParseToCiliumLabels(namespace, name string, uid types.UID, ruleLbs labels.LabelArray) labels.LabelArray {
	policyLbls := GetPolicyLabels(namespace, name, uid, ResourceTypeCiliumNetworkPolicy)
	return append(policyLbls, ruleLbs...)
}
<commit_msg>k8s: Fix comment in getEndpointSelector<commit_after>// Copyright 2017-2018 Authors of Cilium
//
// 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 utils

import (
	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy/api"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// subsysK8s is the value for logfields.LogSubsys
	subsysK8s = "k8s"
	// podPrefixLbl is the value the prefix used in the label selector to
	// represent pods on the default namespace.
	podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel

	// podAnyPrefixLbl is the value of the prefix used in the label selector to
	// represent pods in the default namespace for any source type.
	podAnyPrefixLbl = labels.LabelSourceAnyKeyPrefix + k8sConst.PodNamespaceLabel

	// podInitLbl is the label used in a label selector to match on
	// initializing pods.
	podInitLbl = labels.LabelSourceReservedKeyPrefix + labels.IDNameInit

	// ResourceTypeCiliumNetworkPolicy is the resource type used for the
	// PolicyLabelDerivedFrom label
	ResourceTypeCiliumNetworkPolicy = "CiliumNetworkPolicy"
)

var (
	// log is the k8s package logger object.
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s)
)

// GetPolicyLabels returns a LabelArray for the given namespace and name.
func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray {
	return labels.LabelArray{
		labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelUID, string(uid), labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelNamespace, ns, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelDerivedFrom, derivedFrom, labels.LabelSourceK8s),
	}
}

// getEndpointSelector converts the provided labelSelector into an EndpointSelector,
// adding the relevant matches for namespaces based on the provided options.
func getEndpointSelector(namespace string, labelSelector *metav1.LabelSelector, addK8sPrefix, matchesInit bool) api.EndpointSelector {
	es := api.NewESFromK8sLabelSelector("", labelSelector)

	// The k8s prefix must not be added to reserved labels.
	if addK8sPrefix && es.HasKeyPrefix(labels.LabelSourceReservedKeyPrefix) {
		return es
	}

	// The user can explicitly specify the namespace in the
	// FromEndpoints selector. If omitted, we limit the
	// scope to the namespace the policy lives in.
	//
	// Policies applying on initializing pods are a special case.
	// Those pods don't have any labels, so they don't have a namespace label either.
	// Don't add a namespace label to those endpoint selectors, or we wouldn't be
	// able to match on those pods.
	if !matchesInit && !es.HasKey(podPrefixLbl) && !es.HasKey(podAnyPrefixLbl) {
		es.AddMatch(podPrefixLbl, namespace)
	}

	return es
}

func parseToCiliumIngressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Ingress != nil {
		retRule.Ingress = make([]api.IngressRule, len(inRule.Ingress))
		for i, ing := range inRule.Ingress {
			if ing.FromEndpoints != nil {
				retRule.Ingress[i].FromEndpoints = make([]api.EndpointSelector, len(ing.FromEndpoints))
				for j, ep := range ing.FromEndpoints {
					retRule.Ingress[i].FromEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if ing.ToPorts != nil {
				retRule.Ingress[i].ToPorts = make([]api.PortRule, len(ing.ToPorts))
				copy(retRule.Ingress[i].ToPorts, ing.ToPorts)
			}
			if ing.FromCIDR != nil {
				retRule.Ingress[i].FromCIDR = make([]api.CIDR, len(ing.FromCIDR))
				copy(retRule.Ingress[i].FromCIDR, ing.FromCIDR)
			}

			if ing.FromCIDRSet != nil {
				retRule.Ingress[i].FromCIDRSet = make([]api.CIDRRule, len(ing.FromCIDRSet))
				copy(retRule.Ingress[i].FromCIDRSet, ing.FromCIDRSet)
			}

			if ing.FromRequires != nil {
				retRule.Ingress[i].FromRequires = make([]api.EndpointSelector, len(ing.FromRequires))
				for j, ep := range ing.FromRequires {
					retRule.Ingress[i].FromRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if ing.FromEntities != nil {
				retRule.Ingress[i].FromEntities = make([]api.Entity, len(ing.FromEntities))
				copy(retRule.Ingress[i].FromEntities, ing.FromEntities)
			}
		}
	}
}

func parseToCiliumEgressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Egress != nil {
		retRule.Egress = make([]api.EgressRule, len(inRule.Egress))

		for i, egr := range inRule.Egress {
			if egr.ToEndpoints != nil {
				retRule.Egress[i].ToEndpoints = make([]api.EndpointSelector, len(egr.ToEndpoints))
				for j, ep := range egr.ToEndpoints {
					retRule.Egress[i].ToEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if egr.ToPorts != nil {
				retRule.Egress[i].ToPorts = make([]api.PortRule, len(egr.ToPorts))
				copy(retRule.Egress[i].ToPorts, egr.ToPorts)
			}
			if egr.ToCIDR != nil {
				retRule.Egress[i].ToCIDR = make([]api.CIDR, len(egr.ToCIDR))
				copy(retRule.Egress[i].ToCIDR, egr.ToCIDR)
			}

			if egr.ToCIDRSet != nil {
				retRule.Egress[i].ToCIDRSet = make(api.CIDRRuleSlice, len(egr.ToCIDRSet))
				copy(retRule.Egress[i].ToCIDRSet, egr.ToCIDRSet)
			}

			if egr.ToRequires != nil {
				retRule.Egress[i].ToRequires = make([]api.EndpointSelector, len(egr.ToRequires))
				for j, ep := range egr.ToRequires {
					retRule.Egress[i].ToRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if egr.ToServices != nil {
				retRule.Egress[i].ToServices = make([]api.Service, len(egr.ToServices))
				copy(retRule.Egress[i].ToServices, egr.ToServices)
			}

			if egr.ToEntities != nil {
				retRule.Egress[i].ToEntities = make([]api.Entity, len(egr.ToEntities))
				copy(retRule.Egress[i].ToEntities, egr.ToEntities)
			}

			if egr.ToFQDNs != nil {
				retRule.Egress[i].ToFQDNs = make([]api.FQDNSelector, len(egr.ToFQDNs))
				copy(retRule.Egress[i].ToFQDNs, egr.ToFQDNs)
			}

			if egr.ToGroups != nil {
				retRule.Egress[i].ToGroups = make([]api.ToGroups, len(egr.ToGroups))
				copy(retRule.Egress[i].ToGroups, egr.ToGroups)
			}
		}
	}
}

// namespacesAreValid checks the set of namespaces from a rule returns true if
// they are not specified, or if they are specified and match the namespace
// where the rule is being inserted.
func namespacesAreValid(namespace string, userNamespaces []string) bool {
	return len(userNamespaces) == 0 ||
		(len(userNamespaces) == 1 && userNamespaces[0] == namespace)
}

// ParseToCiliumRule returns an api.Rule with all the labels parsed into cilium
// labels.
func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule {
	retRule := &api.Rule{}
	if r.EndpointSelector.LabelSelector != nil {
		retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector)
		// The PodSelector should only reflect to the same namespace
		// the policy is being stored, thus we add the namespace to
		// the MatchLabels map.
		//
		// Policies applying on initializing pods are a special case.
		// Those pods don't have any labels, so they don't have a namespace label either.
		// Don't add a namespace label to those endpoint selectors, or we wouldn't be
		// able to match on those pods.
		if !retRule.EndpointSelector.HasKey(podInitLbl) {
			userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl)
			if present && !namespacesAreValid(namespace, userNamespace) {
				log.WithFields(logrus.Fields{
					logfields.K8sNamespace:              namespace,
					logfields.CiliumNetworkPolicyName:   name,
					logfields.K8sNamespace + ".illegal": userNamespace,
				}).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." +
					" EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.")
			}
			retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace)
		}
	}

	parseToCiliumIngressRule(namespace, r, retRule)
	parseToCiliumEgressRule(namespace, r, retRule)

	retRule.Labels = ParseToCiliumLabels(namespace, name, uid, r.Labels)

	retRule.Description = r.Description

	return retRule
}

// ParseToCiliumLabels returns all ruleLbls appended with a specific label that
// represents the given namespace and name along with a label that specifies
// these labels were derived from a CiliumNetworkPolicy.
func ParseToCiliumLabels(namespace, name string, uid types.UID, ruleLbs labels.LabelArray) labels.LabelArray {
	policyLbls := GetPolicyLabels(namespace, name, uid, ResourceTypeCiliumNetworkPolicy)
	return append(policyLbls, ruleLbs...)
}
<|endoftext|>


<commit_before>package devices

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/opencontainers/runc/libcontainer/configs"

	"golang.org/x/sys/unix"
)

var (
	ErrNotADevice = errors.New("not a device node")
)

// Testing dependencies
var (
	unixLstat     = unix.Lstat
	ioutilReadDir = ioutil.ReadDir
)

// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
func DeviceFromPath(path, permissions string) (*configs.Device, error) {
	var stat unix.Stat_t
	err := unixLstat(path, &stat)
	if err != nil {
		return nil, err
	}
	var (
		devType rune
		mode    = stat.Mode
	)
	switch {
	case mode&unix.S_IFBLK != 0:
		devType = 'b'
	case mode&unix.S_IFCHR != 0:
		devType = 'c'
	default:
		return nil, ErrNotADevice
	}
	devNumber := int(stat.Rdev)
	uid := stat.Uid
	gid := stat.Gid
	return &configs.Device{
		Type:        devType,
		Path:        path,
		Major:       Major(devNumber),
		Minor:       Minor(devNumber),
		Permissions: permissions,
		FileMode:    os.FileMode(mode),
		Uid:         uid,
		Gid:         gid,
	}, nil
}

func HostDevices() ([]*configs.Device, error) {
	return getDevices("/dev")
}

func getDevices(path string) ([]*configs.Device, error) {
	files, err := ioutilReadDir(path)
	if err != nil {
		return nil, err
	}
	out := []*configs.Device{}
	for _, f := range files {
		switch {
		case f.IsDir():
			switch f.Name() {
			// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
			case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
				continue
			default:
				sub, err := getDevices(filepath.Join(path, f.Name()))
				if err != nil {
					return nil, err
				}

				out = append(out, sub...)
				continue
			}
		case f.Name() == "console":
			continue
		}
		device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
		if err != nil {
			if err == ErrNotADevice {
				continue
			}
			if os.IsNotExist(err) {
				continue
			}
			return nil, err
		}
		out = append(out, device)
	}
	return out, nil
}
<commit_msg>Fix condition to detect device type in DeviceFromPath<commit_after>package devices

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/opencontainers/runc/libcontainer/configs"

	"golang.org/x/sys/unix"
)

var (
	ErrNotADevice = errors.New("not a device node")
)

// Testing dependencies
var (
	unixLstat     = unix.Lstat
	ioutilReadDir = ioutil.ReadDir
)

// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
func DeviceFromPath(path, permissions string) (*configs.Device, error) {
	var stat unix.Stat_t
	err := unixLstat(path, &stat)
	if err != nil {
		return nil, err
	}
	var (
		devType rune
		mode    = stat.Mode
	)
	switch {
	case mode&unix.S_IFBLK == unix.S_IFBLK:
		devType = 'b'
	case mode&unix.S_IFCHR == unix.S_IFCHR:
		devType = 'c'
	default:
		return nil, ErrNotADevice
	}
	devNumber := int(stat.Rdev)
	uid := stat.Uid
	gid := stat.Gid
	return &configs.Device{
		Type:        devType,
		Path:        path,
		Major:       Major(devNumber),
		Minor:       Minor(devNumber),
		Permissions: permissions,
		FileMode:    os.FileMode(mode),
		Uid:         uid,
		Gid:         gid,
	}, nil
}

func HostDevices() ([]*configs.Device, error) {
	return getDevices("/dev")
}

func getDevices(path string) ([]*configs.Device, error) {
	files, err := ioutilReadDir(path)
	if err != nil {
		return nil, err
	}
	out := []*configs.Device{}
	for _, f := range files {
		switch {
		case f.IsDir():
			switch f.Name() {
			// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
			case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
				continue
			default:
				sub, err := getDevices(filepath.Join(path, f.Name()))
				if err != nil {
					return nil, err
				}

				out = append(out, sub...)
				continue
			}
		case f.Name() == "console":
			continue
		}
		device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
		if err != nil {
			if err == ErrNotADevice {
				continue
			}
			if os.IsNotExist(err) {
				continue
			}
			return nil, err
		}
		out = append(out, device)
	}
	return out, nil
}
<|endoftext|>


<commit_before>// Package Router, returns instance for express Router
// Functions defined here are extended by express.go itself
//
// Express Router takes the url regex as similar to the js one
// Router.Get("/:param") will return the param in Response.Params["param"]
package router

import (
	"regexp"
	"github.com/DronRathore/goexpress/request"
	"github.com/DronRathore/goexpress/response"
)
// An extension type to help loop of lookup in express.go
type NextFunc func(NextFunc)
// Middleware function singature type
type Middleware func(request *request.Request, response *response.Response, next func())

// A Route contains a regexp and a Router.Middleware type handler
type Route struct{
	regex *regexp.Regexp
	handler Middleware
	isMiddleware bool
}

// Collection of all method types routers
type Router struct {
	routes map[string][]*Route
}

// Intialise the Router defaults
func (r *Router) Init(){
	r.routes = make(map[string][]*Route)
	r.routes["get"] = []*Route{}
	r.routes["post"] = []*Route{}
	r.routes["put"] = []*Route{}
	r.routes["delete"] = []*Route{}
	r.routes["patch"] = []*Route{}
}

func (r* Router) addHandler(method string, isMiddleware bool, url *regexp.Regexp, middleware Middleware){
	var route = &Route{}
	route.regex = url
	route.handler = middleware
	route.isMiddleware = isMiddleware
	r.routes[method] = append(r.routes[method], route)
}

// Router functions are extended by express itself

func (r* Router) Get(url string, middleware Middleware) *Router{
	r.addHandler("get", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Post(url string, middleware Middleware) *Router{
	r.addHandler("post", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Put(url string, middleware Middleware) *Router{
	r.addHandler("put", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Patch(url string, middleware Middleware) *Router{
	r.addHandler("patch", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Delete(url string, middleware Middleware) *Router{
	r.addHandler("delete", false, CompileRegex(url), middleware)
	return r
}
// Router.Use can take a function or a new express.Router() instance as argument
func (r* Router) Use(middleware interface{}) *Router{
	router, ok := middleware.(Router)
	if ok {
		r.useRouter(router)
	} else {
		mware, ok := middleware.(func(request *request.Request, response *response.Response, next func()))
		if ok {
			var regex = CompileRegex("(.*)")
			// A middleware is for all type of routes
			r.addHandler("get", true, regex, mware)
			r.addHandler("post", true, regex, mware)
			r.addHandler("put", true, regex, mware)
			r.addHandler("patch", true, regex, mware)
			r.addHandler("delete", true, regex, mware)
		} else {
			panic("express.Router.Use can only take a function or a Router instance")
		}
	}
	return r
}

func (r* Router) useRouter(router Router) *Router {
	routes := router.getRoutes()
	for route_type, list := range routes {
		if r.routes[route_type] == nil {
			r.routes[route_type] = []*Route{}
		}
		r.routes[route_type] = append(r.routes[route_type], list...)
	}
	return r;
}

func (r* Router) getRoutes() map[string][]*Route {
	return r.routes
}
// Finds the suitable router for given url and method
// It returns the middleware if found and a cursor index of array
func (r* Router) FindNext(index int, method string, url string, request *request.Request) (Middleware, int, bool){
	var i = index
	for i < len(r.routes[method]){
		var route = r.routes[method][i]
		if route.regex.MatchString(url){
			var regex = route.regex.FindStringSubmatch(url)
			for i, name := range route.regex.SubexpNames() {
				if name != "" {
					request.Params[name] = regex[i]
				}
			}
			return route.handler, i, route.isMiddleware
		}
		i++
	}
	return nil, -1, false
}

// Helper which returns a golang RegExp for a given express route string
func CompileRegex(url string) *regexp.Regexp {
	var i = 0
	var buffer = "/"
	var regexStr = "^"
	var endVariable = ">(?:[A-Za-z0-9\\-\\_\\$\\.\\+\\!\\*\\'\\(\\)\\,]+))"
	if url[0] == '/' {
		i++
	}
	for i < len(url) {
		if url[i] == '/' {
			// this is a new group parse the last part
			regexStr += buffer + "/"
			buffer = ""
			i++
		} else {
			if url[i] == ':' && ( (i-1 >=0 && url[i-1] == '/') || (i-1 == -1)) {
				// a variable found, lets read it
				var tempbuffer = "(?P<"
				var variableName = ""
				var variableNameDone = false
				var done = false
				var hasRegex = false
				var innerGroup = 0
				// lets branch in to look deeper
				i++
				for done != true && i < len(url) {
					if url[i] == '/' {
						if variableName != "" {
							if innerGroup == 0 {
								if hasRegex == false {
									tempbuffer += endVariable
								}
								done = true
								break
							}
						}
						tempbuffer = ""
						break;
					} else if url[i] == '(' {
						if variableNameDone == false {
							variableNameDone = true
							tempbuffer += ">"
							hasRegex = true
						}
						tempbuffer += string(url[i])
						if url[i - 1] != '\\' {
							innerGroup++
						}
					} else if url[i] == ')' {
						tempbuffer += string(url[i])
						if url[i - 1] != '\\' {
							innerGroup--
						}
					} else {
						if variableNameDone == false {
							variableName += string(url[i])
						}
						tempbuffer += string(url[i])
					}
					i++
				}
				if tempbuffer != "" {
					if hasRegex == false && done == false {
						tempbuffer += endVariable
					} else if hasRegex {
						tempbuffer += ")"
					}
					buffer += tempbuffer
				} else {
					panic("Invalid Route regex")
				}
			} else {
				buffer += string(url[i])
				i++
			}
		}
	}
	if buffer != "" {
		regexStr += buffer
	}
	return regexp.MustCompile(regexStr + "(?:[\\/]{0,1})$")
}
<commit_msg>[Major Bug] Allow route variables to be placed anywhere<commit_after>// Package Router, returns instance for express Router
// Functions defined here are extended by express.go itself
//
// Express Router takes the url regex as similar to the js one
// Router.Get("/:param") will return the param in Response.Params["param"]
package router

import (
	"regexp"
	"github.com/DronRathore/goexpress/request"
	"github.com/DronRathore/goexpress/response"
)
// An extension type to help loop of lookup in express.go
type NextFunc func(NextFunc)
// Middleware function singature type
type Middleware func(request *request.Request, response *response.Response, next func())

// A Route contains a regexp and a Router.Middleware type handler
type Route struct{
	regex *regexp.Regexp
	handler Middleware
	isMiddleware bool
}

// Collection of all method types routers
type Router struct {
	routes map[string][]*Route
}

// Intialise the Router defaults
func (r *Router) Init(){
	r.routes = make(map[string][]*Route)
	r.routes["get"] = []*Route{}
	r.routes["post"] = []*Route{}
	r.routes["put"] = []*Route{}
	r.routes["delete"] = []*Route{}
	r.routes["patch"] = []*Route{}
}

func (r* Router) addHandler(method string, isMiddleware bool, url *regexp.Regexp, middleware Middleware){
	var route = &Route{}
	route.regex = url
	route.handler = middleware
	route.isMiddleware = isMiddleware
	r.routes[method] = append(r.routes[method], route)
}

// Router functions are extended by express itself

func (r* Router) Get(url string, middleware Middleware) *Router{
	r.addHandler("get", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Post(url string, middleware Middleware) *Router{
	r.addHandler("post", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Put(url string, middleware Middleware) *Router{
	r.addHandler("put", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Patch(url string, middleware Middleware) *Router{
	r.addHandler("patch", false, CompileRegex(url), middleware)
	return r
}

func (r* Router) Delete(url string, middleware Middleware) *Router{
	r.addHandler("delete", false, CompileRegex(url), middleware)
	return r
}
// Router.Use can take a function or a new express.Router() instance as argument
func (r* Router) Use(middleware interface{}) *Router{
	router, ok := middleware.(Router)
	if ok {
		r.useRouter(router)
	} else {
		mware, ok := middleware.(func(request *request.Request, response *response.Response, next func()))
		if ok {
			var regex = CompileRegex("(.*)")
			// A middleware is for all type of routes
			r.addHandler("get", true, regex, mware)
			r.addHandler("post", true, regex, mware)
			r.addHandler("put", true, regex, mware)
			r.addHandler("patch", true, regex, mware)
			r.addHandler("delete", true, regex, mware)
		} else {
			panic("express.Router.Use can only take a function or a Router instance")
		}
	}
	return r
}

func (r* Router) useRouter(router Router) *Router {
	routes := router.getRoutes()
	for route_type, list := range routes {
		if r.routes[route_type] == nil {
			r.routes[route_type] = []*Route{}
		}
		r.routes[route_type] = append(r.routes[route_type], list...)
	}
	return r;
}

func (r* Router) getRoutes() map[string][]*Route {
	return r.routes
}
// Finds the suitable router for given url and method
// It returns the middleware if found and a cursor index of array
func (r* Router) FindNext(index int, method string, url string, request *request.Request) (Middleware, int, bool){
	var i = index
	for i < len(r.routes[method]){
		var route = r.routes[method][i]
		if route.regex.MatchString(url){
			var regex = route.regex.FindStringSubmatch(url)
			for i, name := range route.regex.SubexpNames() {
				if name != "" {
					request.Params[name] = regex[i]
				}
			}
			return route.handler, i, route.isMiddleware
		}
		i++
	}
	return nil, -1, false
}

// Helper which returns a golang RegExp for a given express route string
func CompileRegex(url string) *regexp.Regexp {
	var i = 0
	var buffer = "/"
	var regexStr = "^"
	var endVariable = ">(?:[A-Za-z0-9\\-\\_\\$\\.\\+\\!\\*\\'\\(\\)\\,]+))"
	if url[0] == '/' {
		i++
	}
	for i < len(url) {
		if url[i] == '/' {
			// this is a new group parse the last part
			regexStr += buffer + "/"
			buffer = ""
			i++
		} else {
			if url[i] == ':' && ( (i-1 > 0 && url[i-1] == '/') || (i-1 == -1) || (i-1 > 0)) {
				// a variable found, lets read it
				var tempbuffer = "(?P<"
				var variableName = ""
				var variableNameDone = false
				var done = false
				var hasRegex = false
				var innerGroup = 0
				// lets branch in to look deeper
				i++
				for done != true && i < len(url) {
					if url[i] == '/' {
						if variableName != "" {
							if innerGroup == 0 {
								if hasRegex == false {
									tempbuffer += endVariable
								}
								done = true
								break
							}
						}
						tempbuffer = ""
						break;
					} else if url[i] == '(' {
						if variableNameDone == false {
							variableNameDone = true
							tempbuffer += ">"
							hasRegex = true
						}
						tempbuffer += string(url[i])
						if url[i - 1] != '\\' {
							innerGroup++
						}
					} else if url[i] == ')' {
						tempbuffer += string(url[i])
						if url[i - 1] != '\\' {
							innerGroup--
						}
					} else {
						if variableNameDone == false {
							variableName += string(url[i])
						}
						tempbuffer += string(url[i])
					}
					i++
				}
				if tempbuffer != "" {
					if hasRegex == false && done == false {
						tempbuffer += endVariable
					} else if hasRegex {
						tempbuffer += ")"
					}
					buffer += tempbuffer
				} else {
					panic("Invalid Route regex")
				}
			} else {
				buffer += string(url[i])
				i++
			}
		}
	}
	if buffer != "" {
		regexStr += buffer
	}
	return regexp.MustCompile(regexStr + "(?:[\\/]{0,1})$")
}
<|endoftext|>


<commit_before>// +build recover

package nsmd_integration_tests

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/nsm"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/registry"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/nsmd"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest/pods"
)

func TestNSMHealRemoteDieNSMD_NSE(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())
	defer kubetest.MakeLogsSnapshot(k8s, t)
	// Deploy open tracing to see what happening.
	nodes_setup, err := kubetest.SetupNodesConfig(k8s, 2, defaultTimeout, []*pods.NSMgrPodConfig{
		{
			Variables: map[string]string{
				nsm.NsmdHealDSTWaitTimeout:   "20", // 20 second delay, since we know both NSM and NSE will die and we need to go with different code branch.
				nsmd.NsmdDeleteLocalRegistry: "true",
			},
			Namespace:          k8s.GetK8sNamespace(),
			DataplaneVariables: kubetest.DefaultDataplaneVariables(k8s.GetForwardingPlane()),
		},
		{
			Namespace:          k8s.GetK8sNamespace(),
			Variables:          pods.DefaultNSMD(),
			DataplaneVariables: kubetest.DefaultDataplaneVariables(k8s.GetForwardingPlane()),
		},
	}, k8s.GetK8sNamespace())
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMPWithConfig(k8s, nodes_setup[1].Node, "icmp-responder-nse-1", defaultTimeout, 30)

	nscPodNode := kubetest.DeployNSC(k8s, nodes_setup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	logrus.Infof("Delete Remote NSMD/ICMP responder NSE")
	k8s.DeletePods(nodes_setup[1].Nsmd)
	k8s.DeletePods(icmpPod)
	//k8s.DeletePods(nodes_setup[1].Nsmd, icmpPod)
	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", time.Minute)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodes_setup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodes_setup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	_ = k8s.WaitLogsContainsRegex(nodes_setup[1].Nsmd, "nsmd", "NSM gRPC API Server: .* is operational", defaultTimeout)
	k8s.WaitLogsContains(nodes_setup[1].Nsmd, "nsmdp", "nsmdp: successfully started", defaultTimeout)
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodes_setup[1].Node.Name)

	// Restore ICMP responder pod.
	icmpPod = kubetest.DeployICMP(k8s, nodes_setup[1].Node, "icmp-responder-nse-2", defaultTimeout)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Heal: Connection recovered:", time.Minute)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}

func TestNSMHealRemoteDieNSMD(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())

	// Deploy open tracing to see what happening.
	nodes_setup, err := kubetest.SetupNodes(k8s, 2, defaultTimeout)
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMP(k8s, nodes_setup[1].Node, "icmp-responder-nse-1", defaultTimeout)
	g.Expect(icmpPod).ToNot(BeNil())

	nscPodNode := kubetest.DeployNSC(k8s, nodes_setup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	logrus.Infof("Delete Remote NSMD")
	k8s.DeletePods(nodes_setup[1].Nsmd)

	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", defaultTimeout)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodes_setup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodes_setup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodes_setup[1].Node.Name)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Heal: Connection recovered:", defaultTimeout)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}

func TestNSMHealRemoteDieNSMDFakeEndpoint(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())

	// Deploy open tracing to see what happening.
	nodesSetup, err := kubetest.SetupNodes(k8s, 2, defaultTimeout)
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMP(k8s, nodesSetup[1].Node, "icmp-responder-nse-1", defaultTimeout)
	g.Expect(icmpPod).ToNot(BeNil())

	nscPodNode := kubetest.DeployNSC(k8s, nodesSetup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	// Remember nse name
	_, nsm1RegistryClient, fwd1Close := kubetest.PrepareRegistryClients(k8s, nodesSetup[1].Nsmd)
	nseList, err := nsm1RegistryClient.GetEndpoints(context.Background(), &empty.Empty{})
	fwd1Close()

	g.Expect(err).To(BeNil())
	g.Expect(len(nseList.NetworkServiceEndpoints)).To(Equal(1))
	nseName := nseList.NetworkServiceEndpoints[0].GetName()

	logrus.Infof("Delete Remote NSMD")
	k8s.DeletePods(nodesSetup[1].Nsmd)

	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodesSetup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", defaultTimeout)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Cleanup Endpoints CRDs...")
	k8s.CleanupEndpointsCRDs()

	nse2RegistryClient, nsm2RegistryClient, fwd2Close := kubetest.PrepareRegistryClients(k8s, nodesSetup[0].Nsmd)
	defer fwd2Close()

	_, err = nse2RegistryClient.RegisterNSE(context.Background(), &registry.NSERegistration{
		NetworkService: &registry.NetworkService{
			Name:    "icmp-responder",
			Payload: "IP",
		},
		NetworkServiceEndpoint: &registry.NetworkServiceEndpoint{
			Name:               nseName,
			NetworkServiceName: "icmp-responder",
		},
	})
	g.Expect(err).To(BeNil())
	nseList, err = nsm2RegistryClient.GetEndpoints(context.Background(), &empty.Empty{})
	g.Expect(err).To(BeNil())
	g.Expect(len(nseList.NetworkServiceEndpoints)).To(Equal(1))

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodesSetup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodesSetup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodesSetup[1].Node.Name)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodesSetup[0].Nsmd, "nsmd", "Heal: Connection recovered:", defaultTimeout)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}
<commit_msg>Add log harvesting to TestNSMHealRemoteDieNSMDFakeEndpoint (#1667)<commit_after>// +build recover

package nsmd_integration_tests

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"

	"github.com/networkservicemesh/networkservicemesh/controlplane/api/nsm"
	"github.com/networkservicemesh/networkservicemesh/controlplane/api/registry"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/nsmd"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest"
	"github.com/networkservicemesh/networkservicemesh/test/kubetest/pods"
)

func TestNSMHealRemoteDieNSMD_NSE(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())
	defer kubetest.MakeLogsSnapshot(k8s, t)
	// Deploy open tracing to see what happening.
	nodes_setup, err := kubetest.SetupNodesConfig(k8s, 2, defaultTimeout, []*pods.NSMgrPodConfig{
		{
			Variables: map[string]string{
				nsm.NsmdHealDSTWaitTimeout:   "20", // 20 second delay, since we know both NSM and NSE will die and we need to go with different code branch.
				nsmd.NsmdDeleteLocalRegistry: "true",
			},
			Namespace:          k8s.GetK8sNamespace(),
			DataplaneVariables: kubetest.DefaultDataplaneVariables(k8s.GetForwardingPlane()),
		},
		{
			Namespace:          k8s.GetK8sNamespace(),
			Variables:          pods.DefaultNSMD(),
			DataplaneVariables: kubetest.DefaultDataplaneVariables(k8s.GetForwardingPlane()),
		},
	}, k8s.GetK8sNamespace())
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMPWithConfig(k8s, nodes_setup[1].Node, "icmp-responder-nse-1", defaultTimeout, 30)

	nscPodNode := kubetest.DeployNSC(k8s, nodes_setup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	logrus.Infof("Delete Remote NSMD/ICMP responder NSE")
	k8s.DeletePods(nodes_setup[1].Nsmd)
	k8s.DeletePods(icmpPod)
	//k8s.DeletePods(nodes_setup[1].Nsmd, icmpPod)
	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", time.Minute)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodes_setup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodes_setup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	_ = k8s.WaitLogsContainsRegex(nodes_setup[1].Nsmd, "nsmd", "NSM gRPC API Server: .* is operational", defaultTimeout)
	k8s.WaitLogsContains(nodes_setup[1].Nsmd, "nsmdp", "nsmdp: successfully started", defaultTimeout)
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodes_setup[1].Node.Name)

	// Restore ICMP responder pod.
	icmpPod = kubetest.DeployICMP(k8s, nodes_setup[1].Node, "icmp-responder-nse-2", defaultTimeout)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Heal: Connection recovered:", time.Minute)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}

func TestNSMHealRemoteDieNSMD(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())

	// Deploy open tracing to see what happening.
	nodes_setup, err := kubetest.SetupNodes(k8s, 2, defaultTimeout)
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMP(k8s, nodes_setup[1].Node, "icmp-responder-nse-1", defaultTimeout)
	g.Expect(icmpPod).ToNot(BeNil())

	nscPodNode := kubetest.DeployNSC(k8s, nodes_setup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	logrus.Infof("Delete Remote NSMD")
	k8s.DeletePods(nodes_setup[1].Nsmd)

	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", defaultTimeout)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodes_setup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodes_setup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodes_setup[1].Node.Name)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodes_setup[0].Nsmd, "nsmd", "Heal: Connection recovered:", defaultTimeout)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}

func TestNSMHealRemoteDieNSMDFakeEndpoint(t *testing.T) {
	g := NewWithT(t)

	if testing.Short() {
		t.Skip("Skip, please run without -short")
		return
	}

	k8s, err := kubetest.NewK8s(g, true)
	defer k8s.Cleanup()

	g.Expect(err).To(BeNil())
	defer kubetest.MakeLogsSnapshot(k8s, t)

	// Deploy open tracing to see what happening.
	nodesSetup, err := kubetest.SetupNodes(k8s, 2, defaultTimeout)
	g.Expect(err).To(BeNil())

	// Run ICMP on latest node
	icmpPod := kubetest.DeployICMP(k8s, nodesSetup[1].Node, "icmp-responder-nse-1", defaultTimeout)
	g.Expect(icmpPod).ToNot(BeNil())

	nscPodNode := kubetest.DeployNSC(k8s, nodesSetup[0].Node, "nsc-1", defaultTimeout)
	kubetest.CheckNSC(k8s, nscPodNode)

	// Remember nse name
	_, nsm1RegistryClient, fwd1Close := kubetest.PrepareRegistryClients(k8s, nodesSetup[1].Nsmd)
	nseList, err := nsm1RegistryClient.GetEndpoints(context.Background(), &empty.Empty{})
	fwd1Close()

	g.Expect(err).To(BeNil())
	g.Expect(len(nseList.NetworkServiceEndpoints)).To(Equal(1))
	nseName := nseList.NetworkServiceEndpoints[0].GetName()

	logrus.Infof("Delete Remote NSMD")
	k8s.DeletePods(nodesSetup[1].Nsmd)

	logrus.Infof("Waiting for NSE with network service")
	k8s.WaitLogsContains(nodesSetup[0].Nsmd, "nsmd", "Waiting for NSE with network service icmp-responder", defaultTimeout)
	// Now are are in dataplane dead state, and in Heal procedure waiting for dataplane.
	nsmdName := fmt.Sprintf("nsmd-worker-recovered-%d", 1)

	logrus.Infof("Cleanup Endpoints CRDs...")
	k8s.CleanupEndpointsCRDs()

	nse2RegistryClient, nsm2RegistryClient, fwd2Close := kubetest.PrepareRegistryClients(k8s, nodesSetup[0].Nsmd)
	defer fwd2Close()

	_, err = nse2RegistryClient.RegisterNSE(context.Background(), &registry.NSERegistration{
		NetworkService: &registry.NetworkService{
			Name:    "icmp-responder",
			Payload: "IP",
		},
		NetworkServiceEndpoint: &registry.NetworkServiceEndpoint{
			Name:               nseName,
			NetworkServiceName: "icmp-responder",
		},
	})
	g.Expect(err).To(BeNil())
	nseList, err = nsm2RegistryClient.GetEndpoints(context.Background(), &empty.Empty{})
	g.Expect(err).To(BeNil())
	g.Expect(len(nseList.NetworkServiceEndpoints)).To(Equal(1))

	logrus.Infof("Starting recovered NSMD...")
	startTime := time.Now()
	nodesSetup[1].Nsmd = k8s.CreatePod(pods.NSMgrPodWithConfig(nsmdName, nodesSetup[1].Node, &pods.NSMgrPodConfig{Namespace: k8s.GetK8sNamespace()})) // Recovery NSEs
	logrus.Printf("Started new NSMD: %v on node %s", time.Since(startTime), nodesSetup[1].Node.Name)

	logrus.Infof("Waiting for connection recovery...")
	k8s.WaitLogsContains(nodesSetup[0].Nsmd, "nsmd", "Heal: Connection recovered:", defaultTimeout)
	logrus.Infof("Waiting for connection recovery Done...")

	kubetest.HealNscChecker(k8s, nscPodNode)
}
<|endoftext|>