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

package iscsi

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/mitchellh/mapstructure"
	"github.com/opensds/opensds/contrib/connector"
)

// IscsiConnectorInfo define
type IscsiConnectorInfo struct {
	AccessMode string   `mapstructure:"accessMode"`
	AuthUser   string   `mapstructure:"authUserName"`
	AuthPass   string   `mapstructure:"authPassword"`
	AuthMethod string   `mapstructure:"authMethod"`
	TgtDisco   bool     `mapstructure:"targetDiscovered"`
	TgtIQN     []string `mapstructure:"targetIQN"`
	TgtPortal  []string `mapstructure:"targetPortal"`
	VolumeID   string   `mapstructure:"volumeId"`
	TgtLun     int      `mapstructure:"targetLun"`
	Encrypted  bool     `mapstructure:"encrypted"`
}

////////////////////////////////////////////////////////////////////////////////
//   Refer some codes from: https://github.com/j-griffith/csi-cinder       //
//   Refer some codes from: https://github.com/kubernetes/kubernetes       //
////////////////////////////////////////////////////////////////////////////////

const (
	//ISCSITranslateTCP tcp
	ISCSITranslateTCP = "tcp"
)

// statFunc define
type statFunc func(string) (os.FileInfo, error)

// globFunc define
type globFunc func(string) ([]string, error)

// waitForPathToExist scan the device path
func waitForPathToExist(devicePath *string, maxRetries int, deviceTransport string) bool {
	// This makes unit testing a lot easier
	return waitForPathToExistInternal(devicePath, maxRetries, deviceTransport, os.Stat, filepath.Glob)
}

// waitForPathToExistInternal scan the device path
func waitForPathToExistInternal(devicePath *string, maxRetries int, deviceTransport string, osStat statFunc, filepathGlob globFunc) bool {
	if devicePath == nil {
		return false
	}

	for i := 0; i < maxRetries; i++ {
		var err error
		if deviceTransport == ISCSITranslateTCP {
			_, err = osStat(*devicePath)
		} else {
			fpath, _ := filepathGlob(*devicePath)
			if fpath == nil {
				err = os.ErrNotExist
			} else {
				// There might be a case that fpath contains multiple device paths if
				// multiple PCI devices connect to same iscsi target. We handle this
				// case at subsequent logic. Pick up only first path here.
				*devicePath = fpath[0]
			}
		}
		if err == nil {
			return true
		}
		if !os.IsNotExist(err) {
			return false
		}
		if i == maxRetries-1 {
			break
		}
		time.Sleep(time.Second)
	}
	return false
}

// GetInitiator returns all the ISCSI Initiator Name
func getInitiator() ([]string, error) {
	res, err := connector.ExecCmd("cat", "/etc/iscsi/initiatorname.iscsi")
	iqns := []string{}
	if err != nil {
		log.Printf("Error encountered gathering initiator names: %v\n", err)
		return iqns, nil
	}

	lines := strings.Split(string(res), "\n")
	for _, l := range lines {
		if strings.Contains(l, "InitiatorName=") {
			iqns = append(iqns, strings.Split(l, "=")[1])
		}
	}

	log.Printf("Found the following iqns: %s\n", iqns)
	return iqns, nil
}

// Discovery ISCSI Target
func discovery(portal string) (string, error) {
	log.Printf("Discovery portal: %s\n", portal)
	result, err := connector.ExecCmd("iscsiadm", "-m", "discovery", "-t", "sendtargets", "-p", portal)
	if err != nil {
		log.Printf("Error encountered in sendtargets: %v\n", err)
		return "", err
	}
	return strings.Replace(result, "\n", "", -1), nil
}

// Login ISCSI Target
func setAuth(portal string, targetiqn string, name string, passwd string) error {
	// Set UserName
	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn,
		"--op=update", "--name", "node.session.auth.username", "--value", name)
	if err != nil {
		log.Printf("Received error on set income username: %v, %v\n", err, info)
		return err
	}
	// Set Password
	info, err = connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn,
		"--op=update", "--name", "node.session.auth.password", "--value", passwd)
	if err != nil {
		log.Printf("Received error on set income password: %v, %v\n", err, info)
		return err
	}
	return nil
}

// Login ISCSI Target
func login(portal string, targetiqn string) error {
	log.Printf("Login portal: %s targetiqn: %s\n", portal, targetiqn)
	// Do not login again if there is an active session.
	cmd := "iscsiadm -m session |grep -w " + portal + "|grep -w " + targetiqn
	_, err := connector.ExecCmd("/bin/bash", "-c", cmd)
	if err == nil {
		log.Printf("there is an active session\n")
		_, err := connector.ExecCmd("iscsiadm", "-m", "session", "-R")
		if err == nil {
			log.Printf("rescan iscsi session success.\n")
		}
		return nil
	}

	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn, "--login")
	if err != nil {
		log.Printf("Received error on login attempt: %v, %s\n", err, info)
		return err
	}
	return nil
}

// Logout ISCSI Target
func logout(portal string, targetiqn string) error {
	log.Printf("Logout portal: %s targetiqn: %s\n", portal, targetiqn)
	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn, "--logout")
	if err != nil {
		log.Println("Received error on logout attempt", err, info)
		return err
	}
	return nil
}

// Delete ISCSI Node
func delete(targetiqn string) (err error) {
	log.Printf("Delete targetiqn: %s\n", targetiqn)
	_, err = connector.ExecCmd("iscsiadm", "-m", "node", "-o", "delete", "-T", targetiqn)
	if err != nil {
		log.Printf("Received error on Delete attempt: %v\n", err)
		return err
	}
	return nil
}

// ParseIscsiConnectInfo decode
func parseIscsiConnectInfo(connectInfo map[string]interface{}) (*IscsiConnectorInfo, int, error) {
	var con IscsiConnectorInfo
	mapstructure.Decode(connectInfo, &con)

	fmt.Printf("iscsi target portal: %s, target iqn: %s, target lun: %d\n", con.TgtPortal, con.TgtIQN, con.TgtLun)
	if len(con.TgtPortal) == 0 || con.TgtLun == 0 {
		return nil, -1, errors.New("iscsi connection data invalid.")
	}

	var index int

	log.Printf("TgtPortal:%v\n", con.TgtPortal)
	for i, portal := range con.TgtPortal {
		strs := strings.Split(portal, ":")
		ip := strs[0]
		cmd := "ping -c 2 " + ip
		res, err := connector.ExecCmd("/bin/bash", "-c", cmd)
		log.Printf("ping result:%v\n", res)
		if err != nil {
			log.Printf("ping error:%v\n", err)
			if i == len(con.TgtPortal)-1 {
				return nil, -1, errors.New("no available iscsi portal.")
			}
			continue
		}
		index = i
		break
	}

	return &con, index, nil
}

// Connect ISCSI Target
func connect(connMap map[string]interface{}) (string, error) {
	conn, index, err := parseIscsiConnectInfo(connMap)
	if err != nil {
		return "", err
	}
	log.Println("connmap info: ", connMap)
	log.Println("conn info is: ", conn)
	portal := conn.TgtPortal[index]

	var targetiqn string
	var targetiqnIdx = 1
	if len(conn.TgtIQN) == 0 {
		content, _ := discovery(portal)
		targetiqn = strings.Split(content, " ")[targetiqnIdx]
	} else {
		targetiqn = conn.TgtIQN[index]
	}

	targetlun := strconv.Itoa(conn.TgtLun)

	cmd := "pgrep -f /sbin/iscsid"
	_, err = connector.ExecCmd("/bin/bash", "-c", cmd)

	if err != nil {
		cmd = "/sbin/iscsid"
		_, errExec := connector.ExecCmd("/bin/bash", "-c", cmd)
		if errExec != nil {
			return "", fmt.Errorf("Please stop the iscsi process outside the container first: %v", errExec)
		}
	}

	log.Printf("Connect portal: %s targetiqn: %s targetlun: %s\n", portal, targetiqn, targetlun)
	devicePath := strings.Join([]string{
		"/dev/disk/by-path/ip",
		portal,
		"iscsi",
		targetiqn,
		"lun",
		targetlun}, "-")

	log.Println("devicepath is ", devicePath)

	// Discovery
	_, err = discovery(portal)
	if err != nil {
		return "", err
	}
	if len(conn.AuthMethod) != 0 {
		setAuth(portal, targetiqn, conn.AuthUser, conn.AuthPass)
	}
	//Login
	err = login(portal, targetiqn)
	if err != nil {
		return "", err
	}

	isexist := waitForPathToExist(&devicePath, 10, ISCSITranslateTCP)

	if !isexist {
		return "", errors.New("Could not connect volume: Timeout after 10s")
	}

	return devicePath, nil
}

// Disconnect ISCSI Target
func disconnect(conn map[string]interface{}) error {
	iscsiCon, index, err := parseIscsiConnectInfo(conn)
	if err != nil {
		return err
	}
	portal := iscsiCon.TgtPortal[index]

	var targetiqn string
	if len(iscsiCon.TgtIQN) == 0 {
		content, _ := discovery(portal)
		targetiqn = strings.Split(content, " ")[1]
	} else {
		targetiqn = iscsiCon.TgtIQN[index]
	}

	cmd := "ls /dev/disk/by-path/ |grep -w " + portal + "|grep -w " + targetiqn + "|wc -l |awk '{if($1>1) print 1; else print 0}'"
	logoutFlag, err := connector.ExecCmd("/bin/bash", "-c", cmd)
	if err != nil {
		log.Printf("Disconnect iscsi target failed, %v\n", err)
		return err
	}

	logoutFlag = strings.Replace(logoutFlag, "\n", "", -1)
	if logoutFlag == "0" {
		log.Printf("Disconnect portal: %s targetiqn: %s\n", portal, targetiqn)
		// Logout
		err = logout(portal, targetiqn)
		if err != nil {
			return err
		}

		//Delete
		err = delete(targetiqn)
		if err != nil {
			return err
		}

		return nil
	}
	log.Println("logoutFlag: ", logoutFlag)
	return nil
}

func getTgtPortalAndTgtIQN() (string, string, error) {
	log.Println("GetTgtPortalAndTgtIQN")
	var targetiqn, targetportal string
	out, err := connector.ExecCmd("iscsiadm", "-m", "session")
	if err != nil {
		errGetPortalAndIQN := fmt.Errorf("Get targetportal And targetiqn failed: %v", err)
		log.Println("Get targetportal And targetiqn failed: ", errGetPortalAndIQN)
		return "", "", errGetPortalAndIQN
	}

	lines := strings.Split(string(out), "\n")

	for _, line := range lines {
		if strings.Contains(line, "tcp") {
			lineSplit := strings.Split(line, " ")
			targetportalTemp := lineSplit[2]
			targetportal = strings.Split(targetportalTemp, ",")[0]
			targetiqn = lineSplit[3]
		}
	}

	if targetiqn != "" && targetportal != "" {
		return targetiqn, targetportal, nil
	}

	msg := "targetportal And targetiqn not found"
	log.Println(msg)
	return "", "", errors.New(msg)

}

func getInitiatorInfo() (string, error) {
	initiators, err := getInitiator()
	if err != nil {
		return "", err
	}

	if len(initiators) == 0 {
		return "", errors.New("No iqn found")
	}

	if len(initiators) > 1 {
		return "", errors.New("the number of iqn is wrong")
	}

	return initiators[0], nil
}
<commit_msg>Bug Fix: For Volume Attachment to OpenSDS CSI plugin<commit_after>// Copyright 2018 The OpenSDS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package iscsi

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/mitchellh/mapstructure"
	"github.com/opensds/opensds/contrib/connector"
)

// IscsiConnectorInfo define
type IscsiConnectorInfo struct {
	AccessMode string   `mapstructure:"accessMode"`
	AuthUser   string   `mapstructure:"authUserName"`
	AuthPass   string   `mapstructure:"authPassword"`
	AuthMethod string   `mapstructure:"authMethod"`
	TgtDisco   bool     `mapstructure:"targetDiscovered"`
	TgtIQN     []string `mapstructure:"targetIQN"`
	TgtPortal  []string `mapstructure:"targetPortal"`
	VolumeID   string   `mapstructure:"volumeId"`
	TgtLun     int      `mapstructure:"targetLun"`
	Encrypted  bool     `mapstructure:"encrypted"`
}

////////////////////////////////////////////////////////////////////////////////
//   Refer some codes from: https://github.com/j-griffith/csi-cinder       //
//   Refer some codes from: https://github.com/kubernetes/kubernetes       //
////////////////////////////////////////////////////////////////////////////////

const (
	//ISCSITranslateTCP tcp
	ISCSITranslateTCP = "tcp"
)

// statFunc define
type statFunc func(string) (os.FileInfo, error)

// globFunc define
type globFunc func(string) ([]string, error)

// waitForPathToExist scan the device path
func waitForPathToExist(devicePath *string, maxRetries int, deviceTransport string) bool {
	// This makes unit testing a lot easier
	return waitForPathToExistInternal(devicePath, maxRetries, deviceTransport, os.Stat, filepath.Glob)
}

// waitForPathToExistInternal scan the device path
func waitForPathToExistInternal(devicePath *string, maxRetries int, deviceTransport string, osStat statFunc, filepathGlob globFunc) bool {
	if devicePath == nil {
		return false
	}

	for i := 0; i < maxRetries; i++ {
		var err error
		if deviceTransport == ISCSITranslateTCP {
			_, err = osStat(*devicePath)
		} else {
			fpath, _ := filepathGlob(*devicePath)
			if fpath == nil {
				err = os.ErrNotExist
			} else {
				// There might be a case that fpath contains multiple device paths if
				// multiple PCI devices connect to same iscsi target. We handle this
				// case at subsequent logic. Pick up only first path here.
				*devicePath = fpath[0]
			}
		}
		if err == nil {
			return true
		}
		if !os.IsNotExist(err) {
			return false
		}
		if i == maxRetries-1 {
			break
		}
		time.Sleep(time.Second)
	}
	return false
}

// GetInitiator returns all the ISCSI Initiator Name
func getInitiator() ([]string, error) {
	res, err := connector.ExecCmd("cat", "/etc/iscsi/initiatorname.iscsi")
	iqns := []string{}
	if err != nil {
		log.Printf("Error encountered gathering initiator names: %v\n", err)
		return iqns, nil
	}

	lines := strings.Split(string(res), "\n")
	for _, l := range lines {
		if strings.Contains(l, "InitiatorName=") {
			iqns = append(iqns, strings.Split(l, "=")[1])
		}
	}

	log.Printf("Found the following iqns: %s\n", iqns)
	return iqns, nil
}

// Discovery ISCSI Target
func discovery(portal string) (string, error) {
	log.Printf("Discovery portal: %s\n", portal)
	result, err := connector.ExecCmd("iscsiadm", "-m", "discovery", "-t", "sendtargets", "-p", portal)
	if err != nil {
		log.Printf("Error encountered in sendtargets: %v\n", err)
		return "", err
	}
	return strings.Replace(result, "\n", "", -1), nil
}

// Login ISCSI Target
func setAuth(portal string, targetiqn string, name string, passwd string) error {
	// Set UserName
	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn,
		"--op=update", "--name", "node.session.auth.username", "--value", name)
	if err != nil {
		log.Printf("Received error on set income username: %v, %v\n", err, info)
		return err
	}
	// Set Password
	info, err = connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn,
		"--op=update", "--name", "node.session.auth.password", "--value", passwd)
	if err != nil {
		log.Printf("Received error on set income password: %v, %v\n", err, info)
		return err
	}
	return nil
}

// Login ISCSI Target
func login(portal string, targetiqn string) error {
	log.Printf("Login portal: %s targetiqn: %s\n", portal, targetiqn)
	// Do not login again if there is an active session.
	cmd := "iscsiadm -m session |grep -w " + portal + "|grep -w " + targetiqn
	_, err := connector.ExecCmd("/bin/bash", "-c", cmd)
	if err == nil {
		log.Printf("there is an active session\n")
		_, err := connector.ExecCmd("iscsiadm", "-m", "session", "-R")
		if err == nil {
			log.Printf("rescan iscsi session success.\n")
		}
		return nil
	}

	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn, "--login")
	if err != nil {
		log.Printf("Received error on login attempt: %v, %s\n", err, info)
		return err
	}
	return nil
}

// Logout ISCSI Target
func logout(portal string, targetiqn string) error {
	log.Printf("Logout portal: %s targetiqn: %s\n", portal, targetiqn)
	info, err := connector.ExecCmd("iscsiadm", "-m", "node", "-p", portal, "-T", targetiqn, "--logout")
	if err != nil {
		log.Println("Received error on logout attempt", err, info)
		return err
	}
	return nil
}

// Delete ISCSI Node
func delete(targetiqn string) (err error) {
	log.Printf("Delete targetiqn: %s\n", targetiqn)
	_, err = connector.ExecCmd("iscsiadm", "-m", "node", "-o", "delete", "-T", targetiqn)
	if err != nil {
		log.Printf("Received error on Delete attempt: %v\n", err)
		return err
	}
	return nil
}

// ParseIscsiConnectInfo decode
func parseIscsiConnectInfo(connectInfo map[string]interface{}) (*IscsiConnectorInfo, int, error) {
	var con IscsiConnectorInfo
	mapstructure.Decode(connectInfo, &con)

	fmt.Printf("iscsi target portal: %s, target iqn: %s, target lun: %d\n", con.TgtPortal, con.TgtIQN, con.TgtLun)
	if len(con.TgtPortal) == 0 || con.TgtLun == 0 {
		return nil, -1, errors.New("iscsi connection data invalid.")
	}

	var index int

	log.Printf("TgtPortal:%v\n", con.TgtPortal)
	for i, portal := range con.TgtPortal {
		strs := strings.Split(portal, ":")
		ip := strs[0]
		cmd := "ping -c 2 " + ip
		res, err := connector.ExecCmd("/bin/bash", "-c", cmd)
		log.Printf("ping result:%v\n", res)
		if err != nil {
			log.Printf("ping error:%v\n", err)
			if i == len(con.TgtPortal)-1 {
				return nil, -1, errors.New("no available iscsi portal.")
			}
			continue
		}
		index = i
		break
	}

	return &con, index, nil
}

// Connect ISCSI Target
func connect(connMap map[string]interface{}) (string, error) {
	conn, index, err := parseIscsiConnectInfo(connMap)
	if err != nil {
		return "", err
	}
	log.Println("connmap info: ", connMap)
	log.Println("conn info is: ", conn)
	portal := conn.TgtPortal[index]

	var targetiqn string
	var targetiqnIdx = 1
	if len(conn.TgtIQN) == 0 {
		content, _ := discovery(portal)
		targetiqn = strings.Split(content, " ")[targetiqnIdx]
	} else {
		targetiqn = conn.TgtIQN[index]
	}

	targetlun := strconv.Itoa(conn.TgtLun)

    cmd := "ls -ali / | sed '2!d' |awk {'print $1'}"
    INODE_NUM, err := connector.ExecCmd("/bin/bash", "-c", cmd)
    if err != nil && INODE_NUM == "2" {
		cmd = "\"pgrep -f /sbin/iscsid\""
		_, err = connector.ExecCmd("/bin/bash", "-c", cmd)

		if err != nil {
			cmd = "/sbin/iscsid"
			_, errExec := connector.ExecCmd("/bin/bash", "-c", cmd)
			if errExec != nil {
				return "", fmt.Errorf("Please stop the iscsi process: %v", errExec)
			}
		}
	}

	log.Printf("Connect portal: %s targetiqn: %s targetlun: %s\n", portal, targetiqn, targetlun)
	devicePath := strings.Join([]string{
		"/dev/disk/by-path/ip",
		portal,
		"iscsi",
		targetiqn,
		"lun",
		targetlun}, "-")

	log.Println("devicepath is ", devicePath)

	// Discovery
	_, err = discovery(portal)
	if err != nil {
		return "", err
	}
	if len(conn.AuthMethod) != 0 {
		setAuth(portal, targetiqn, conn.AuthUser, conn.AuthPass)
	}
	//Login
	err = login(portal, targetiqn)
	if err != nil {
		return "", err
	}

	isexist := waitForPathToExist(&devicePath, 10, ISCSITranslateTCP)

	if !isexist {
		return "", errors.New("Could not connect volume: Timeout after 10s")
	}

	return devicePath, nil
}

// Disconnect ISCSI Target
func disconnect(conn map[string]interface{}) error {
	iscsiCon, index, err := parseIscsiConnectInfo(conn)
	if err != nil {
		return err
	}
	portal := iscsiCon.TgtPortal[index]

	var targetiqn string
	if len(iscsiCon.TgtIQN) == 0 {
		content, _ := discovery(portal)
		targetiqn = strings.Split(content, " ")[1]
	} else {
		targetiqn = iscsiCon.TgtIQN[index]
	}

	cmd := "ls /dev/disk/by-path/ |grep -w " + portal + "|grep -w " + targetiqn + "|wc -l |awk '{if($1>1) print 1; else print 0}'"
	logoutFlag, err := connector.ExecCmd("/bin/bash", "-c", cmd)
	if err != nil {
		log.Printf("Disconnect iscsi target failed, %v\n", err)
		return err
	}

	logoutFlag = strings.Replace(logoutFlag, "\n", "", -1)
	if logoutFlag == "0" {
		log.Printf("Disconnect portal: %s targetiqn: %s\n", portal, targetiqn)
		// Logout
		err = logout(portal, targetiqn)
		if err != nil {
			return err
		}

		//Delete
		err = delete(targetiqn)
		if err != nil {
			return err
		}

		return nil
	}
	log.Println("logoutFlag: ", logoutFlag)
	return nil
}

func getTgtPortalAndTgtIQN() (string, string, error) {
	log.Println("GetTgtPortalAndTgtIQN")
	var targetiqn, targetportal string
	out, err := connector.ExecCmd("iscsiadm", "-m", "session")
	if err != nil {
		errGetPortalAndIQN := fmt.Errorf("Get targetportal And targetiqn failed: %v", err)
		log.Println("Get targetportal And targetiqn failed: ", errGetPortalAndIQN)
		return "", "", errGetPortalAndIQN
	}

	lines := strings.Split(string(out), "\n")

	for _, line := range lines {
		if strings.Contains(line, "tcp") {
			lineSplit := strings.Split(line, " ")
			targetportalTemp := lineSplit[2]
			targetportal = strings.Split(targetportalTemp, ",")[0]
			targetiqn = lineSplit[3]
		}
	}

	if targetiqn != "" && targetportal != "" {
		return targetiqn, targetportal, nil
	}

	msg := "targetportal And targetiqn not found"
	log.Println(msg)
	return "", "", errors.New(msg)

}

func getInitiatorInfo() (string, error) {
	initiators, err := getInitiator()
	if err != nil {
		return "", err
	}

	if len(initiators) == 0 {
		return "", errors.New("No iqn found")
	}

	if len(initiators) > 1 {
		return "", errors.New("the number of iqn is wrong")
	}

	return initiators[0], nil
}
<|endoftext|>


<commit_before>package quaternion

/*
Quaternion array package
A Quaternion array is a struct with 4 columns defined as x y z w
it can be created simply using slice.
*/

import "math"

//Inverse of quaternion array q
func Inverse() {

}

func Amplitude(){

}

//Normalize quaternion array q or array list to unit quaternions
func Norm(){

}

//
func Division(){

}

//
func Mul(){

}

//Exponential of a quaternion array
func Exp(){

}

//Neprien logarithm of a quaternion array
func Ln(){

}

//Real power of a quaternion array
func Pow(){

}

//Rotate vector or array of vectors v by quaternion q
func Rotate(q []float64) {

}

//
func Toaxisangle(){

}

//
<commit_msg>add struct<commit_after>package quaternion

/*
Quaternion array package
A Quaternion array is a struct with 4 columns defined as x y z w
it can be created simply using slice.
*/

import "math"

type qMatrix struct{

}

//Inverse of quaternion array q
func Inverse() {

}

func Amplitude(){

}

//Normalize quaternion array q or array list to unit quaternions
func Norm(){

}

//
func Division(){

}

//
func Mul(){

}

//Exponential of a quaternion array
func Exp(){

}

//Neprien logarithm of a quaternion array
func Ln(){

}

//Real power of a quaternion array
func Pow(){

}

//Rotate vector or array of vectors v by quaternion q
func Rotate(q []float64) {

}

//
func Toaxisangle(){

}

//
<|endoftext|>


<commit_before>// Package bloomfilter provides a bloom filter.
// A Bloom filter provides a quick and memory-efficient, probabilistic check for
// the existence of a member in a set.
// The efficiency comes at the speed of certainty.
// False positives are possible, wrongly indicating that an element is in the set,
// ie that the element is possibly in the set.
// False negatives are not possible - if the filter indicates that an element is
// not in the set, then it is definitely not in the set.
package bloomfilter

import (
	"fmt"
	"hash/fnv"
	"math"
)

// Hash32Fn is a function type for 32 bit hashing functions.
type Hash32Fn func(string) uint32

// BloomFilter is the public struct.
type BloomFilter struct {
	// TODO: use bits, not bools.
	Filter       []bool
	Size         uint32
	HashFuncs    []Hash32Fn
	TotalFlipped uint32
}

// NewBloomFilter creates a new BloomFilter with the specified number of switches,
// and a list of the hash functions to use when adding elements to the set, and
// when checking for existence.
func NewBloomFilter(filterSize uint32) *BloomFilter {
	b := BloomFilter{}
	b.Filter = make([]bool, filterSize)
	b.Size = filterSize
	// TODO: inject preferred choice of hashers.
	b.HashFuncs = []Hash32Fn{hashFnv1, hashFnv1a}
	return &b
}

// hashFnv1 puts a string through the golang stdlib 32-bit FNV-1 hash.
// A string is reduced to an int.
func hashFnv1(s string) uint32 {
	h := fnv.New32()
	h.Write([]byte(s))
	return h.Sum32()
}

// hashFnv1a puts a string through the golang stdlib 32-bit FNV-1a hash.
// A string is reduced to an int.
func hashFnv1a(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// getIndex32 hashes a string, then reduces that hash to an index within the bounds of the filter.
func (b *BloomFilter) getIndex32(s string, hashFn Hash32Fn) uint32 {
	h := hashFn(s)
	return h % b.Size
}

// Add a string to the Bloom filter.
func (b *BloomFilter) Add(s string) {
	fmt.Println("\nAdding:", s)
	// Iterate over the list of hash functions, using each to reduce the string
	// to a single index in the filter, which is then flipped on.
	for i, j := range b.HashFuncs {
		idx := b.getIndex32(s, j)
		fmt.Println("Hash function #", i, "hashed to index:", idx)
		b.Filter[idx] = true
		fmt.Println("Updated filter to:", b.Filter)
		b.TotalFlipped++
	}
}

// Exists checks if the given string is in the Bloom Filter.
func (b *BloomFilter) Exists(s string) bool {
	// Put the candidate string through each of the hash functions, and for each
	// index returned get the value at that index in the bloom filter, and put
	// those values into the results slice.
	results := make([]bool, len(b.HashFuncs))
	for i, j := range b.HashFuncs {
		idx := b.getIndex32(s, j)
		val := b.Filter[idx]
		results[i] = val
	}
	allTrue := true
	// Iterate over the switch values retrieved from the bloom filter.
	for _, j := range results {
		// If the switch values retrieved are all true we'll end up returning true.
		allTrue = allTrue && j
	}
	return allTrue
}

// False positive probability:
// The number of true cells in the filter,
// divided by the length of the filter,
// to the power of the number of hash functions.
func (b *BloomFilter) GetFalsePositiveProbability() float64 {
	x := float64(b.TotalFlipped) / float64(b.Size)
	y := float64(len(b.HashFuncs))
	z := math.Pow(x, y) * 100
	p := math.Floor(z + .5)
	return p
}
<commit_msg>Replace bools with bits.<commit_after>// Package bloomfilter provides a bloom filter.
// A Bloom filter provides a quick and memory-efficient, probabilistic check for
// the existence of a member in a set.
// The efficiency comes at the speed of certainty.
// False positives are possible, wrongly indicating that an element is in the set,
// ie that the element is possibly in the set.
// False negatives are not possible - if the filter indicates that an element is
// not in the set, then it is definitely not in the set.
package bloomfilter

import (
	"fmt"
	"hash/fnv"
	"math"
)

// Hash32Fn is a function type for 32 bit hashing functions.
type Hash32Fn func(string) uint32

// BloomFilter is the public struct.
type BloomFilter struct {
	Filter       []byte
	Size         uint32
	HashFuncs    []Hash32Fn
	TotalFlipped uint32
}

func (b *BloomFilter) SetTrue(i uint32) {
	bitByte := byteFromBitIndex(i)
	i = offsetBitIndex(i)
	b.Filter[bitByte] |= 1 << i
}

func (b *BloomFilter) Get(i uint32) bool {
	bitByte := byteFromBitIndex(i)
	r := b.Filter[bitByte]

	i = offsetBitIndex(i)
	isSet := r & (1 << i)

	if isSet != 0 {
		return true
	}
	return false
}

// byteFromBitIndex returns the index in the []byte of the byte
// which contains the specified bit.
func byteFromBitIndex(i uint32) uint32 {
	relevantByte := i / 8
	return relevantByte
}

// offsetBitIndex returns the index of the specified bit in its byte of the []byte.
func offsetBitIndex(i uint32) uint32 {
	if i >= 8 {
		i = i % 8
	}
	return i
}

// NewBloomFilter creates a new BloomFilter with the specified number of switches,
// and a list of the hash functions to use when adding elements to the set, and
// when checking for existence.
func NewBloomFilter(filterSize uint32) *BloomFilter {
	b := BloomFilter{}
	b.Filter = make([]byte, filterSize/8+1)
	b.Size = filterSize
	// TODO: inject preferred choice of hashers.
	b.HashFuncs = []Hash32Fn{hashFnv1, hashFnv1a}
	return &b
}

// hashFnv1 puts a string through the golang stdlib 32-bit FNV-1 hash.
// A string is reduced to an int.
func hashFnv1(s string) uint32 {
	h := fnv.New32()
	h.Write([]byte(s))
	return h.Sum32()
}

// hashFnv1a puts a string through the golang stdlib 32-bit FNV-1a hash.
// A string is reduced to an int.
func hashFnv1a(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// getIndex32 hashes a string, then reduces that hash to an index within the bounds of the filter.
func (b *BloomFilter) getIndex32(s string, hashFn Hash32Fn) uint32 {
	h := hashFn(s)
	return h % b.Size
}

// Add a string to the Bloom filter.
func (b *BloomFilter) Add(s string) {
	fmt.Println("\nAdding:", s)
	// Iterate over the list of hash functions, using each to reduce the string
	// to a single index in the filter, which is then flipped on.
	for i, j := range b.HashFuncs {
		idx := b.getIndex32(s, j)
		fmt.Println("Hash function #", i, "hashed to index:", idx)
		b.SetTrue(idx)

		fmt.Println("Updated filter to:", b.Filter)
		b.TotalFlipped++
	}
}

// Exists checks if the given string is in the Bloom Filter.
func (b *BloomFilter) Exists(s string) bool {
	// Put the candidate string through each of the hash functions, and for each
	// index returned get the value at that index in the bloom filter, and put
	// those values into the results slice.
	results := make([]bool, len(b.HashFuncs))
	for i, j := range b.HashFuncs {
		idx := b.getIndex32(s, j)
		results[i] = b.Get(idx)
	}

	allTrue := true
	// Iterate over the switch values retrieved from the bloom filter.
	for _, j := range results {
		// If the switch values retrieved are all true we'll end up returning true.
		allTrue = allTrue && j
	}
	return allTrue
}

// False positive probability:
// The number of true cells in the filter,
// divided by the length of the filter,
// to the power of the number of hash functions.
func (b *BloomFilter) GetFalsePositiveProbability() float64 {
	x := float64(b.TotalFlipped) / float64(b.Size)
	y := float64(len(b.HashFuncs))
	z := math.Pow(x, y) * 100
	p := math.Floor(z + .5)
	return p
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Zhen, LLC. http://zhen.io. All rights reserved.
 * Use of this source code is governed by the Apache 2.0 license.
 *
 */

package bp32

import (
	"testing"
	"reflect"
	"fmt"
	"github.com/reducedb/encoding"
)

var (
	codec encoding.Integer
	data []uint32
)

func init() {
	codec = NewIntegratedBP32()
	log.Printf("bp32/init: generating %d uint32s\n", size)
	data = encoding.GenerateClustered(size, size*2)
	log.Printf("bp32/init: generated %d integers for test", size)
}

func runCompression(data []uint32, length int, codec encoding.Integer) []uint32 {
	compressed := make([]uint32, length*2)
	inpos := encoding.NewCursor()
	outpos := encoding.NewCursor()
	codec.Compress(data, inpos, length, compressed, outpos)
	compressed = compressed[:outpos.Get()]
	return compressed
}

func runDecompression(data []uint32, length int, codec encoding.Integer) []uint32 {
	recovered := make([]uint32, length)
	rinpos := encoding.NewCursor()
	routpos := encoding.NewCursor()
	codec.Uncompress(data, rinpos, len(data), recovered, routpos)
	recovered = recovered[:routpos.Get()]
	return recovered
}

func TestBasicExample(t *testing.T) {
	for _, k := range []int{128, 128*10, 128*100, 128*1000, 128*10000} {
		fmt.Printf("bp32/TestBasicExample: Testing with %d integers\n", k)

		compressed := runCompression(data, k, codec)
		fmt.Printf("bp32/TestBasicExample: Compressed from %d bytes to %d bytes\n", k*4, len(compressed)*4)

		recovered := runDecompression(compressed, k, codec)
		fmt.Printf("bp32/TestBasicExample: Decompressed from %d bytes to %d bytes\n", len(compressed)*4, len(recovered)*4)

		if !reflect.DeepEqual(data[:k], recovered) {
			t.Fatalf("bp32/TestBasicExample: Problem recovering. Original length = %d, recovered length = %d\n", k, len(recovered))
		}
	}
}

func BenchmarkCompress(b *testing.B) {
	k := int(encoding.CeilBy(uint32(b.N), 128))
	//data := generateData(int(N))

	b.ResetTimer()
	compressed := runCompression(data, k, codec)
	b.StopTimer()

	fmt.Printf("bp32/BenchmarkCompress: Compressed from %d bytes to %d bytes\n", k*4, len(compressed)*4)
}

func BenchmarkDecompress(b *testing.B) {
	k := int(encoding.CeilBy(uint32(b.N), 128))
	//data := generateData(int(N))
	compressed := runCompression(data, k, codec)

	b.ResetTimer()
	recovered := runDecompression(compressed, len(compressed), codec)
	b.StopTimer()

	fmt.Printf("bp32/BenchmarkDecompress: Decompressed from %d bytes to %d bytes\n", len(compressed)*4, len(recovered)*4)
}
<commit_msg>fixed missing import<commit_after>/*
 * Copyright (c) 2013 Zhen, LLC. http://zhen.io. All rights reserved.
 * Use of this source code is governed by the Apache 2.0 license.
 *
 */

package bp32

import (
	"testing"
	"reflect"
	"fmt"
	"log"
	"github.com/reducedb/encoding"
)

var (
	codec encoding.Integer
	data []uint32
)

func init() {
	codec = NewIntegratedBP32()
	log.Printf("bp32/init: generating %d uint32s\n", size)
	data = encoding.GenerateClustered(size, size*2)
	log.Printf("bp32/init: generated %d integers for test", size)
}

func runCompression(data []uint32, length int, codec encoding.Integer) []uint32 {
	compressed := make([]uint32, length*2)
	inpos := encoding.NewCursor()
	outpos := encoding.NewCursor()
	codec.Compress(data, inpos, length, compressed, outpos)
	compressed = compressed[:outpos.Get()]
	return compressed
}

func runDecompression(data []uint32, length int, codec encoding.Integer) []uint32 {
	recovered := make([]uint32, length)
	rinpos := encoding.NewCursor()
	routpos := encoding.NewCursor()
	codec.Uncompress(data, rinpos, len(data), recovered, routpos)
	recovered = recovered[:routpos.Get()]
	return recovered
}

func TestBasicExample(t *testing.T) {
	for _, k := range []int{128, 128*10, 128*100, 128*1000, 128*10000} {
		fmt.Printf("bp32/TestBasicExample: Testing with %d integers\n", k)

		compressed := runCompression(data, k, codec)
		fmt.Printf("bp32/TestBasicExample: Compressed from %d bytes to %d bytes\n", k*4, len(compressed)*4)

		recovered := runDecompression(compressed, k, codec)
		fmt.Printf("bp32/TestBasicExample: Decompressed from %d bytes to %d bytes\n", len(compressed)*4, len(recovered)*4)

		if !reflect.DeepEqual(data[:k], recovered) {
			t.Fatalf("bp32/TestBasicExample: Problem recovering. Original length = %d, recovered length = %d\n", k, len(recovered))
		}
	}
}

func BenchmarkCompress(b *testing.B) {
	k := int(encoding.CeilBy(uint32(b.N), 128))
	//data := generateData(int(N))

	b.ResetTimer()
	compressed := runCompression(data, k, codec)
	b.StopTimer()

	fmt.Printf("bp32/BenchmarkCompress: Compressed from %d bytes to %d bytes\n", k*4, len(compressed)*4)
}

func BenchmarkDecompress(b *testing.B) {
	k := int(encoding.CeilBy(uint32(b.N), 128))
	//data := generateData(int(N))
	compressed := runCompression(data, k, codec)

	b.ResetTimer()
	recovered := runDecompression(compressed, len(compressed), codec)
	b.StopTimer()

	fmt.Printf("bp32/BenchmarkDecompress: Decompressed from %d bytes to %d bytes\n", len(compressed)*4, len(recovered)*4)
}
<|endoftext|>


<commit_before>package boleto

import (
	"bytes"
	"encoding/base64"
	"errors"

	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/twooffive"

	"image/png"

	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/tmpl"
)

const templateBoleto = `

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">

    {{if eq .Format "html"}}

            <button class="no-print btnDefault print" onclick="window.print()">

                &nbspImprimir
            </button>
            <button class="no-print btnDefault print" onclick="window.location='./boleto?fmt=pdf&id={{.View.ID}}'">

                &nbspGerar PDF
            </button>

    {{end}}
    {{template "boletoForm" .}}

    {{template "boletoForm" .}}

`

const boletoForm = `
{{define "boletoForm"}}

                    {{.ConfigBank.Logo}}

                {{.View.BankNumber}}

                    Local de Pagamento

                    ATÉ O VENCIMENTO EM QUALQUER BANCO OU CORRESPONDENTE NÃO BANCÁRIO

                    Data de Vencimento

{{.View.Boleto.Title.ExpireDateTime | brdate}}

                    Nome do Beneficiário / CNPJ / CPF / Endereço:

                            {{.View.Boleto.Recipient.Name}}
                            {{.View.Boleto.Recipient.Document.Type}} {{fmtDoc .View.Boleto.Recipient.Document}}

                    {{.View.Boleto.Recipient.Address.Street}},
                    {{.View.Boleto.Recipient.Address.Number}} -
                    {{.View.Boleto.Recipient.Address.District}},
                    {{.View.Boleto.Recipient.Address.StateCode}} -
                    {{.View.Boleto.Recipient.Address.ZipCode}}

                    Agência/Código Beneficiário

{{.View.Boleto.Agreement.Agency}}/{{if eq .View.BankNumber "033-7"}}{{.View.Boleto.Agreement.AgreementNumber}}{{else}}{{.View.Boleto.Agreement.Account}}{{end}}

                    Data do Documento

{{.View.Boleto.Title.CreateDate | brdate}}

                    Num. do Documento

{{.View.Boleto.Title.DocumentNumber}}

                    Espécie doc

{{.ConfigBank.EspecieDoc}}

                    Aceite

{{.ConfigBank.Aceite}}

                    Data Processamento

{{.View.Boleto.Title.CreateDate | brdate}}

                    Carteira/Nosso Número

{{.View.Boleto.Agreement.Wallet}}/{{.View.Boleto.Title.OurNumber}}

                {{if eq .View.BankNumber "033-7"}}

                                Carteira

COBRANCA SIMPLES RCR

                {{else}}

                    Uso do Banco

                            {{if eq .View.BankNumber "237-2"}}

                                    Cip

865

                            {{end}}

                                Carteira

{{.View.Boleto.Agreement.Wallet}}

                {{end}}

                    Espécie

{{.ConfigBank.Moeda}}

                    Quantidade

{{.ConfigBank.Quantidade}}

                    Valor

{{.ConfigBank.ValorCotacao}}

                    (=) Valor do Documento

{{fmtNumber .View.Boleto.Title.AmountInCents}}

                    Instruções de responsabilidade do BENEFICIÁRIO. Qualquer dúvida sobre este boleto contate o beneficiário.

{{.View.Boleto.Title.Instructions }}

                    (-) Descontos/Abatimento

                    (+) Juros/Multa

                    (=) Valor Pago

                            Nome do Pagador:  {{.View.Boleto.Buyer.Name}}
                            CNPJ/CPF:  {{fmtDoc .View.Boleto.Buyer.Document}}

                            Endereço:  {{.View.Boleto.Buyer.Address.Street}} {{.View.Boleto.Buyer.Address.Number}}, {{.View.Boleto.Buyer.Address.District}} - {{.View.Boleto.Buyer.Address.City}}, {{.View.Boleto.Buyer.Address.StateCode}} - {{.View.Boleto.Buyer.Address.ZipCode}}

                            Sacador/Avalista:
                            CNPJ/CPF:

	{{end}}
`

type HTMLBoleto struct {
	View          models.BoletoView
	ConfigBank    ConfigBank
	Barcode64     string
	Format        string
	DigitableLine string
}

//HTML renderiza HTML do boleto
func HTML(boletoView models.BoletoView, format string) (string, error) {

	if boletoView.Barcode == "" {
		return "", errors.New("boleto not found")
	}
	b := tmpl.New()
	html := HTMLBoleto{
		View:       boletoView,
		ConfigBank: GetConfig(boletoView.Boleto),
		Format:     format,
	}
	bcode, _ := twooffive.Encode(boletoView.Barcode, true)
	orgBounds := bcode.Bounds()
	orgWidth := orgBounds.Max.X - orgBounds.Min.X
	img, _ := barcode.Scale(bcode, orgWidth, 50)
	buf := new(bytes.Buffer)
	err := png.Encode(buf, img)
	html.Barcode64 = base64.StdEncoding.EncodeToString(buf.Bytes())
	html.DigitableLine = textToImage(boletoView.DigitableLine)
	s, err := b.From(html).To(templateBoleto).Transform(boletoForm)
	if err != nil {
		return "", err
	}
	return s, nil
}
<commit_msg>display account digit on boleto<commit_after>package boleto

import (
	"bytes"
	"encoding/base64"
	"errors"

	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/twooffive"

	"image/png"

	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/tmpl"
)

const templateBoleto = `

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">

    {{if eq .Format "html"}}

            <button class="no-print btnDefault print" onclick="window.print()">

                &nbspImprimir
            </button>
            <button class="no-print btnDefault print" onclick="window.location='./boleto?fmt=pdf&id={{.View.ID}}'">

                &nbspGerar PDF
            </button>

    {{end}}
    {{template "boletoForm" .}}

    {{template "boletoForm" .}}

`

const boletoForm = `
{{define "boletoForm"}}

                    {{.ConfigBank.Logo}}

                {{.View.BankNumber}}

                    Local de Pagamento

                    ATÉ O VENCIMENTO EM QUALQUER BANCO OU CORRESPONDENTE NÃO BANCÁRIO

                    Data de Vencimento

{{.View.Boleto.Title.ExpireDateTime | brdate}}

                    Nome do Beneficiário / CNPJ / CPF / Endereço:

                            {{.View.Boleto.Recipient.Name}}
                            {{.View.Boleto.Recipient.Document.Type}} {{fmtDoc .View.Boleto.Recipient.Document}}

                    {{.View.Boleto.Recipient.Address.Street}},
                    {{.View.Boleto.Recipient.Address.Number}} -
                    {{.View.Boleto.Recipient.Address.District}},
                    {{.View.Boleto.Recipient.Address.StateCode}} -
                    {{.View.Boleto.Recipient.Address.ZipCode}}

                    Agência/Código Beneficiário

                        {{.View.Boleto.Agreement.Agency}} /{{if eq .View.BankNumber "033-7"}}
                            {{.View.Boleto.Agreement.AgreementNumber}}
                        {{else if eq .View.BankNumber "341-7" }}
                            {{.View.Boleto.Agreement.Account}}-{{.View.Boleto.Agreement.AccountDigit}}
                        {{else}}
                            {{.View.Boleto.Agreement.Account}}
                        {{end}}

                    Data do Documento

{{.View.Boleto.Title.CreateDate | brdate}}

                    Num. do Documento

{{.View.Boleto.Title.DocumentNumber}}

                    Espécie doc

{{.ConfigBank.EspecieDoc}}

                    Aceite

{{.ConfigBank.Aceite}}

                    Data Processamento

{{.View.Boleto.Title.CreateDate | brdate}}

                    Carteira/Nosso Número

{{.View.Boleto.Agreement.Wallet}}/{{.View.Boleto.Title.OurNumber}}

                {{if eq .View.BankNumber "033-7"}}

                                Carteira

COBRANCA SIMPLES RCR

                {{else}}

                    Uso do Banco

                            {{if eq .View.BankNumber "237-2"}}

                                    Cip

865

                            {{end}}

                                Carteira

{{.View.Boleto.Agreement.Wallet}}

                {{end}}

                    Espécie

{{.ConfigBank.Moeda}}

                    Quantidade

{{.ConfigBank.Quantidade}}

                    Valor

{{.ConfigBank.ValorCotacao}}

                    (=) Valor do Documento

{{fmtNumber .View.Boleto.Title.AmountInCents}}

                    Instruções de responsabilidade do BENEFICIÁRIO. Qualquer dúvida sobre este boleto contate o beneficiário.

{{.View.Boleto.Title.Instructions }}

                    (-) Descontos/Abatimento

                    (+) Juros/Multa

                    (=) Valor Pago

                            Nome do Pagador:  {{.View.Boleto.Buyer.Name}}
                            CNPJ/CPF:  {{fmtDoc .View.Boleto.Buyer.Document}}

                            Endereço:  {{.View.Boleto.Buyer.Address.Street}} {{.View.Boleto.Buyer.Address.Number}}, {{.View.Boleto.Buyer.Address.District}} - {{.View.Boleto.Buyer.Address.City}}, {{.View.Boleto.Buyer.Address.StateCode}} - {{.View.Boleto.Buyer.Address.ZipCode}}

                            Sacador/Avalista:
                            CNPJ/CPF:

	{{end}}
`

type HTMLBoleto struct {
	View          models.BoletoView
	ConfigBank    ConfigBank
	Barcode64     string
	Format        string
	DigitableLine string
}

//HTML renderiza HTML do boleto
func HTML(boletoView models.BoletoView, format string) (string, error) {

	if boletoView.Barcode == "" {
		return "", errors.New("boleto not found")
	}
	b := tmpl.New()
	html := HTMLBoleto{
		View:       boletoView,
		ConfigBank: GetConfig(boletoView.Boleto),
		Format:     format,
	}
	bcode, _ := twooffive.Encode(boletoView.Barcode, true)
	orgBounds := bcode.Bounds()
	orgWidth := orgBounds.Max.X - orgBounds.Min.X
	img, _ := barcode.Scale(bcode, orgWidth, 50)
	buf := new(bytes.Buffer)
	err := png.Encode(buf, img)
	html.Barcode64 = base64.StdEncoding.EncodeToString(buf.Bytes())
	html.DigitableLine = textToImage(boletoView.DigitableLine)
	s, err := b.From(html).To(templateBoleto).Transform(boletoForm)
	if err != nil {
		return "", err
	}
	return s, nil
}
<|endoftext|>


<commit_before>package store_test

import (
	"bytes"
	. "launchpad.net/gocheck"
	"launchpad.net/mgo"
	"os/exec"
	"time"
)

// ----------------------------------------------------------------------------
// The mgo test suite

type MgoSuite struct {
	Addr    string
	Session *mgo.Session
	output  bytes.Buffer
	server  *exec.Cmd
}

func (s *MgoSuite) SetUpSuite(c *C) {
	mgo.SetDebug(true)
	mgo.SetStats(true)
	dbdir := c.MkDir()
	args := []string{
		"--dbpath", dbdir,
		"--bind_ip", "127.0.0.1",
		"--port", "50017",
		"--nssize", "1",
		"--noprealloc",
		"--smallfiles",
	}
	s.server = exec.Command("mongod", args...)
	s.server.Stdout = &s.output
	s.server.Stderr = &s.output
	err := s.server.Start()
	if err != nil {
		panic(err)
	}
}

func (s *MgoSuite) TearDownSuite(c *C) {
	s.server.Process.Kill()
	s.server.Process.Wait()
}

func (s *MgoSuite) SetUpTest(c *C) {
	err := DropAll("localhost:50017")
	if err != nil {
		panic(err)
	}
	mgo.SetLogger(c)
	mgo.ResetStats()
	s.Addr = "127.0.0.1:50017"
	s.Session, err = mgo.Dial(s.Addr)
	if err != nil {
		panic(err)
	}
}

func (s *MgoSuite) TearDownTest(c *C) {
	s.Session.Close()
	for i := 0; ; i++ {
		stats := mgo.GetStats()
		if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 {
			break
		}
		if i == 20 {
			c.Fatal("Test left sockets in a dirty state")
		}
		c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive)
		time.Sleep(5e8)
	}
}

func DropAll(mongourl string) (err error) {
	session, err := mgo.DialWithTimeout(mongourl, 30*time.Second)
	if err != nil {
		return err
	}
	defer session.Close()

	names, err := session.DatabaseNames()
	if err != nil {
		return err
	}
	for _, name := range names {
		switch name {
		case "admin", "local", "config":
		default:
			err = session.DB(name).DropDatabase()
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>better solution<commit_after>package store_test

import (
	"bytes"
	. "launchpad.net/gocheck"
	"launchpad.net/mgo"
	"os/exec"
	"time"
)

// ----------------------------------------------------------------------------
// The mgo test suite

type MgoSuite struct {
	Addr    string
	Session *mgo.Session
	output  bytes.Buffer
	server  *exec.Cmd
}

func (s *MgoSuite) SetUpSuite(c *C) {
	mgo.SetDebug(true)
	mgo.SetStats(true)
	dbdir := c.MkDir()
	args := []string{
		"--dbpath", dbdir,
		"--bind_ip", "127.0.0.1",
		"--port", "50017",
		"--nssize", "1",
		"--noprealloc",
		"--smallfiles",
		"--nojournal",
	}
	s.server = exec.Command("mongod", args...)
	s.server.Stdout = &s.output
	s.server.Stderr = &s.output
	err := s.server.Start()
	if err != nil {
		panic(err)
	}
}

func (s *MgoSuite) TearDownSuite(c *C) {
	s.server.Process.Kill()
	s.server.Process.Wait()
}

func (s *MgoSuite) SetUpTest(c *C) {
	err := DropAll("localhost:50017")
	if err != nil {
		panic(err)
	}
	mgo.SetLogger(c)
	mgo.ResetStats()
	s.Addr = "127.0.0.1:50017"
	s.Session, err = mgo.Dial(s.Addr)
	if err != nil {
		panic(err)
	}
}

func (s *MgoSuite) TearDownTest(c *C) {
	s.Session.Close()
	for i := 0; ; i++ {
		stats := mgo.GetStats()
		if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 {
			break
		}
		if i == 20 {
			c.Fatal("Test left sockets in a dirty state")
		}
		c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive)
		time.Sleep(5e8)
	}
}

func DropAll(mongourl string) (err error) {
	session, err := mgo.Dial(mongourl)
	if err != nil {
		return err
	}
	defer session.Close()

	names, err := session.DatabaseNames()
	if err != nil {
		return err
	}
	for _, name := range names {
		switch name {
		case "admin", "local", "config":
		default:
			err = session.DB(name).DropDatabase()
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
)

var chrome_applescript = `
  tell application "Google Chrome"
    activate
    set theUrl to "` + *url + `"

    if (count every window) = 0 then
      make new window
    end if

    set found to false
    set theTabIndex to -1
    repeat with theWindow in every window
      set theTabIndex to 0
      repeat with theTab in every tab of theWindow
        set theTabIndex to theTabIndex + 1
        if theTab's URL = theUrl then
          set found to true
          exit
        end if
      end repeat

      if found then
        exit repeat
      end if
    end repeat

    if found then
      tell theTab to reload
      set theWindow's active tab index to theTabIndex
      set index of theWindow to 1
    else
      tell window 1 to make new tab with properties {URL:theUrl}
    end if
  end tell
`

func openUrl() {
	if *url == "" {
		return
	}

	talk("Opening url (macosx/chrome): ", *url)

	cmd := exec.Command("osascript")

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

	in.Write([]byte(chrome_applescript))
	in.Close()

	output, err := cmd.CombinedOutput()

	if err != nil {
		Err("AppleScript Error: ", string(output))
	}
}
<commit_msg>cleaned up logging<commit_after>package main

import (
	"os/exec"
)

var chrome_applescript = `
  tell application "Google Chrome"
    activate
    set theUrl to "` + *url + `"

    if (count every window) = 0 then
      make new window
    end if

    set found to false
    set theTabIndex to -1
    repeat with theWindow in every window
      set theTabIndex to 0
      repeat with theTab in every tab of theWindow
        set theTabIndex to theTabIndex + 1
        if theTab's URL = theUrl then
          set found to true
          exit
        end if
      end repeat

      if found then
        exit repeat
      end if
    end repeat

    if found then
      tell theTab to reload
      set theWindow's active tab index to theTabIndex
      set index of theWindow to 1
    else
      tell window 1 to make new tab with properties {URL:theUrl}
    end if
  end tell
`

func openUrl() {
	if *url == "" {
		return
	}

	Talk("Opening url (macosx/chrome): ", *url)

	cmd := exec.Command("osascript")

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

	in.Write([]byte(chrome_applescript))
	in.Close()

	output, err := cmd.CombinedOutput()

	if err != nil {
		Err("AppleScript Error: ", string(output))
	}
}
<|endoftext|>


<commit_before>package gol_goquery

import (
	"log"

	"github.com/PuerkitoBio/goquery"
)

type GoqueryResults struct {
	Results []string
}

func GoqueryDocument(url string) *goquery.Document {
	doc, err := goquery.NewDocument(url)
	if err != nil {
		log.Fatal(err)
	}
	return doc
}

func Goquery(url string, goquerySelector string) (domNode *goquery.Selection) {
	doc := GoqueryDocument(url)

	domNode = doc.Find(goquerySelector)
	return
}

func (results *GoqueryResults) GoqueryAttrsFrom(domNodes *goquery.Selection, attr string) {
	results.Results = make([]string, domNodes.Size())
	domNodes.Each(func(i int, s *goquery.Selection) {
		var attrValue string
		var attrPresent bool
		if attr == "text" {
			attrValue = s.Text()
			attrPresent = (attrValue != "")
		} else {
			attrValue, attrPresent = s.Attr(attr)
		}
		if !attrPresent {
			s_html, _ := s.Html()
			log.Printf("[warn] %s\n", s_html)
		}

		results.Results[i] = attrValue
	})
	return
}

func GoqueryHrefsFrom(url string, goquerySelector string) (results GoqueryResults) {
	results.GoqueryAttrsFrom(Goquery(url, goquerySelector), "href")
	return
}

func GoqueryTextFrom(url string, goquerySelector string) (results GoqueryResults) {
	results.GoqueryAttrsFrom(Goquery(url, goquerySelector), "text")
	return
}

func GoqueryHrefsFromParents(url string, selectors []string) (results GoqueryResults) {
	var domNodes *goquery.Selection
	last_idx := len(selectors) - 1
	for idx, selector := range selectors {
		if selector == ".." {
			domNodes = domNodes.Parent()
		} else {
			domNodes = Goquery(url, selector)
		}
		if idx == last_idx {
			results.GoqueryAttrsFrom(domNodes, "href")
		}
	}
	return
}
<commit_msg>gol_goquery package name changed to golgoquery to match others<commit_after>package golgoquery

import (
	"log"

	"github.com/PuerkitoBio/goquery"
)

type GoqueryResults struct {
	Results []string
}

func GoqueryDocument(url string) *goquery.Document {
	doc, err := goquery.NewDocument(url)
	if err != nil {
		log.Fatal(err)
	}
	return doc
}

func Goquery(url string, goquerySelector string) (domNode *goquery.Selection) {
	doc := GoqueryDocument(url)

	domNode = doc.Find(goquerySelector)
	return
}

func (results *GoqueryResults) GoqueryAttrsFrom(domNodes *goquery.Selection, attr string) {
	results.Results = make([]string, domNodes.Size())
	domNodes.Each(func(i int, s *goquery.Selection) {
		var attrValue string
		var attrPresent bool
		if attr == "text" {
			attrValue = s.Text()
			attrPresent = (attrValue != "")
		} else {
			attrValue, attrPresent = s.Attr(attr)
		}
		if !attrPresent {
			s_html, _ := s.Html()
			log.Printf("[warn] %s\n", s_html)
		}

		results.Results[i] = attrValue
	})
	return
}

func GoqueryHrefsFrom(url string, goquerySelector string) (results GoqueryResults) {
	results.GoqueryAttrsFrom(Goquery(url, goquerySelector), "href")
	return
}

func GoqueryTextFrom(url string, goquerySelector string) (results GoqueryResults) {
	results.GoqueryAttrsFrom(Goquery(url, goquerySelector), "text")
	return
}

func GoqueryHrefsFromParents(url string, selectors []string) (results GoqueryResults) {
	var domNodes *goquery.Selection
	last_idx := len(selectors) - 1
	for idx, selector := range selectors {
		if selector == ".." {
			domNodes = domNodes.Parent()
		} else {
			domNodes = Goquery(url, selector)
		}
		if idx == last_idx {
			results.GoqueryAttrsFrom(domNodes, "href")
		}
	}
	return
}
<|endoftext|>


<commit_before>package gopherjs_http

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

	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"
	"github.com/go-on/gopherjslib"
)

// HtmlFile returns a handler that serves the given .html file, with the "text/go" scripts compiled to JavaScript via GopherJS.
func HtmlFile(name string) http.Handler {
	return &htmlFile{name: name}
}

type htmlFile struct {
	name string
}

func (this *htmlFile) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	file, err := os.Open(this.name)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	ProcessHtml(file).WriteTo(w)
}

// TODO: Write into writer, no need for buffer (unless want to be able to abort on error?). Or, alternatively, parse html and serve minified version?
func ProcessHtml(r io.Reader) *bytes.Buffer {
	insideTextGo := false
	tokenizer := html.NewTokenizer(r)
	var buf bytes.Buffer

	for {
		if tokenizer.Next() == html.ErrorToken {
			err := tokenizer.Err()
			if err == io.EOF {
				return &buf
			}

			return &bytes.Buffer{}
		}

		token := tokenizer.Token()
		switch token.Type {
		case html.DoctypeToken:
			buf.WriteString(token.String())
		case html.CommentToken:
			buf.WriteString(token.String())
		case html.StartTagToken:
			if token.DataAtom == atom.Script && getType(token.Attr) == "text/go" {
				insideTextGo = true

				buf.WriteString(`<script type="text/javascript">`)

				if src := getSrc(token.Attr); src != "" {
					if data, err := ioutil.ReadFile(src); err == nil {
						buf.WriteString(goToJs(string(data)))
					}
				}
			} else {
				buf.WriteString(token.String())
			}
		case html.EndTagToken:
			if token.DataAtom == atom.Script && insideTextGo {
				insideTextGo = false
			}
			buf.WriteString(token.String())
		case html.SelfClosingTagToken:
			// TODO: Support <script type="text/go" src="..." />.
			buf.WriteString(token.String())
		case html.TextToken:
			if insideTextGo {
				buf.WriteString(goToJs(token.Data))
			} else {
				buf.WriteString(token.Data)
			}
		default:
			panic("unknown token type")
		}
	}
}

func getType(attrs []html.Attribute) string {
	for _, attr := range attrs {
		if attr.Key == "type" {
			return attr.Val
		}
	}
	return ""
}

func getSrc(attrs []html.Attribute) string {
	for _, attr := range attrs {
		if attr.Key == "src" {
			return attr.Val
		}
	}
	return ""
}

func goToJs(goCode string) (jsCode string) {
	started := time.Now()
	defer func() { fmt.Println("goToJs taken:", time.Since(started)) }()

	code := strings.NewReader(goCode)

	var out bytes.Buffer
	err := gopherjslib.Build(code, &out, nil)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return goCode
	}

	return out.String()
}
<commit_msg>gopherjs_http: Display GopherJS compile error in console.<commit_after>package gopherjs_http

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"
	"github.com/go-on/gopherjslib"
)

// HtmlFile returns a handler that serves the given .html file, with the "text/go" scripts compiled to JavaScript via GopherJS.
func HtmlFile(name string) http.Handler {
	return &htmlFile{name: name}
}

type htmlFile struct {
	name string
}

func (this *htmlFile) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	file, err := os.Open(this.name)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	ProcessHtml(file).WriteTo(w)
}

// TODO: Write into writer, no need for buffer (unless want to be able to abort on error?). Or, alternatively, parse html and serve minified version?
func ProcessHtml(r io.Reader) *bytes.Buffer {
	insideTextGo := false
	tokenizer := html.NewTokenizer(r)
	var buf bytes.Buffer

	for {
		if tokenizer.Next() == html.ErrorToken {
			err := tokenizer.Err()
			if err == io.EOF {
				return &buf
			}

			return &bytes.Buffer{}
		}

		token := tokenizer.Token()
		switch token.Type {
		case html.DoctypeToken:
			buf.WriteString(token.String())
		case html.CommentToken:
			buf.WriteString(token.String())
		case html.StartTagToken:
			if token.DataAtom == atom.Script && getType(token.Attr) == "text/go" {
				insideTextGo = true

				buf.WriteString(`<script type="text/javascript">`)

				if src := getSrc(token.Attr); src != "" {
					if data, err := ioutil.ReadFile(src); err == nil {
						buf.WriteString(goToJs(string(data)))
					}
				}
			} else {
				buf.WriteString(token.String())
			}
		case html.EndTagToken:
			if token.DataAtom == atom.Script && insideTextGo {
				insideTextGo = false
			}
			buf.WriteString(token.String())
		case html.SelfClosingTagToken:
			// TODO: Support <script type="text/go" src="..." />.
			buf.WriteString(token.String())
		case html.TextToken:
			if insideTextGo {
				buf.WriteString(goToJs(token.Data))
			} else {
				buf.WriteString(token.Data)
			}
		default:
			panic("unknown token type")
		}
	}
}

func getType(attrs []html.Attribute) string {
	for _, attr := range attrs {
		if attr.Key == "type" {
			return attr.Val
		}
	}
	return ""
}

func getSrc(attrs []html.Attribute) string {
	for _, attr := range attrs {
		if attr.Key == "src" {
			return attr.Val
		}
	}
	return ""
}

func goToJs(goCode string) (jsCode string) {
	started := time.Now()
	defer func() { fmt.Println("goToJs taken:", time.Since(started)) }()

	code := strings.NewReader(goCode)

	var out bytes.Buffer
	err := gopherjslib.Build(code, &out, nil)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return "console.error(\"" + template.JSEscapeString(err.Error()) + "\");"
	}

	return out.String()
}
<|endoftext|>


<commit_before>// +build linux

package btrfs

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/containerd/btrfs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/plugin"
	"github.com/containerd/containerd/snapshot"
	"github.com/containerd/containerd/snapshot/storage"
	"github.com/pkg/errors"
)

func init() {
	plugin.Register(&plugin.Registration{
		ID:   "btrfs",
		Type: plugin.SnapshotPlugin,
		Init: func(ic *plugin.InitContext) (interface{}, error) {
			return NewSnapshotter(ic.Root)
		},
	})
}

type snapshotter struct {
	device string // device of the root
	root   string // root provides paths for internal storage.
	ms     *storage.MetaStore
}

// NewSnapshotter returns a Snapshotter using btrfs. Uses the provided
// root directory for snapshots and stores the metadata in
// a file in the provided root.
// root needs to be a mount point of btrfs.
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
	mnt, err := mount.Lookup(root)
	if mnt.FSType != "btrfs" {
		return nil, fmt.Errorf("expected btrfs, got %s", mnt.FSType)
	}
	if err != nil {
		return nil, err
	}
	var (
		active    = filepath.Join(root, "active")
		snapshots = filepath.Join(root, "snapshots")
	)

	for _, path := range []string{
		active,
		snapshots,
	} {
		if err := os.Mkdir(path, 0755); err != nil && !os.IsExist(err) {
			return nil, err
		}
	}
	ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
	if err != nil {
		return nil, err
	}

	return &snapshotter{
		device: mnt.Source,
		root:   root,
		ms:     ms,
	}, nil
}

// Stat returns the info for an active or committed snapshot by name or
// key.
//
// Should be used for parent resolution, existence checks and to discern
// the kind of snapshot.
func (b *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return snapshot.Info{}, err
	}
	defer t.Rollback()
	_, info, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return snapshot.Info{}, err
	}

	return info, nil
}

// Usage retrieves the disk usage of the top-level snapshot.
func (b *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
	panic("not implemented")

	// TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a
	// snapshot or shared among other resources. We may find that this is the
	// correct value to reoprt but the stability of the implementation is under
	// question.
	//
	// In general, this has impact on the model we choose for reporting usage.
	// Ideally, the value should allow aggregration. For overlay, this is
	// simple since we can scan the diff directory to get a unique value. This
	// breaks down when start looking the behavior when data is shared between
	// snapshots, such as that for btrfs.
}

// Walk the committed snapshots.
func (b *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return err
	}
	defer t.Rollback()
	return storage.WalkInfo(ctx, fn)
}

func (b *snapshotter) Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error) {
	return b.makeActive(ctx, key, parent, false)
}

func (b *snapshotter) View(ctx context.Context, key, parent string) ([]mount.Mount, error) {
	return b.makeActive(ctx, key, parent, true)
}

func (b *snapshotter) makeActive(ctx context.Context, key, parent string, readonly bool) ([]mount.Mount, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}
	}()

	a, err := storage.CreateActive(ctx, key, parent, readonly)
	if err != nil {
		return nil, err
	}

	target := filepath.Join(b.root, "active", a.ID)

	if len(a.ParentIDs) == 0 {
		// create new subvolume
		// btrfs subvolume create /dir
		if err = btrfs.SubvolCreate(target); err != nil {
			return nil, err
		}
	} else {
		parentp := filepath.Join(b.root, "snapshots", a.ParentIDs[0])
		// btrfs subvolume snapshot /parent /subvol
		if err = btrfs.SubvolSnapshot(target, parentp, a.Readonly); err != nil {
			return nil, err
		}
	}
	err = t.Commit()
	t = nil
	if err != nil {
		if derr := btrfs.SubvolDelete(target); derr != nil {
			log.G(ctx).WithError(derr).WithField("subvolume", target).Error("Failed to delete subvolume")
		}
		return nil, err
	}

	return b.mounts(target)
}

func (b *snapshotter) mounts(dir string) ([]mount.Mount, error) {
	var options []string

	// get the subvolume id back out for the mount
	info, err := btrfs.SubvolInfo(dir)
	if err != nil {
		return nil, err
	}

	options = append(options, fmt.Sprintf("subvolid=%d", info.ID))

	if info.Readonly {
		options = append(options, "ro")
	}

	return []mount.Mount{
		{
			Type:   "btrfs",
			Source: b.device,
			// NOTE(stevvooe): While it would be nice to use to uuids for
			// mounts, they don't work reliably if the uuids are missing.
			Options: options,
		},
	}, nil
}

func (b *snapshotter) Commit(ctx context.Context, name, key string) (err error) {
	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}
	}()

	id, err := storage.CommitActive(ctx, key, name, snapshot.Usage{}) // TODO(stevvooe): Resolve a usage value for btrfs
	if err != nil {
		return errors.Wrap(err, "failed to commit")
	}

	source := filepath.Join(b.root, "active", id)
	target := filepath.Join(b.root, "snapshots", id)

	if err := btrfs.SubvolSnapshot(target, source, true); err != nil {
		return err
	}

	err = t.Commit()
	t = nil
	if err != nil {
		if derr := btrfs.SubvolDelete(target); derr != nil {
			log.G(ctx).WithError(derr).WithField("subvolume", target).Error("Failed to delete subvolume")
		}
		return err
	}

	if derr := btrfs.SubvolDelete(source); derr != nil {
		// Log as warning, only needed for cleanup, will not cause name collision
		log.G(ctx).WithError(derr).WithField("subvolume", source).Warn("Failed to delete subvolume")
	}

	return nil
}

// Mounts returns the mounts for the transaction identified by key. Can be
// called on an read-write or readonly transaction.
//
// This can be used to recover mounts after calling View or Prepare.
func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return nil, err
	}
	a, err := storage.GetActive(ctx, key)
	t.Rollback()
	if err != nil {
		return nil, errors.Wrap(err, "failed to get active snapshot")
	}
	dir := filepath.Join(b.root, "active", a.ID)
	return b.mounts(dir)
}

// Remove abandons the transaction identified by key. All resources
// associated with the key will be removed.
func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
	var (
		source, removed string
		readonly        bool
	)

	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}

		if removed != "" {
			if derr := btrfs.SubvolDelete(removed); derr != nil {
				log.G(ctx).WithError(derr).WithField("subvolume", removed).Warn("Failed to delete subvolume")
			}
		}
	}()

	id, k, err := storage.Remove(ctx, key)
	if err != nil {
		return errors.Wrap(err, "failed to remove snapshot")
	}

	if k == snapshot.KindActive {
		source = filepath.Join(b.root, "active", id)

		info, err := btrfs.SubvolInfo(source)
		if err != nil {
			source = ""
			return err
		}

		readonly = info.Readonly
		removed = filepath.Join(b.root, "active", "rm-"+id)
	} else {
		source = filepath.Join(b.root, "snapshots", id)
		removed = filepath.Join(b.root, "snapshots", "rm-"+id)
		readonly = true
	}

	if err := btrfs.SubvolSnapshot(removed, source, readonly); err != nil {
		removed = ""
		return err
	}

	if err := btrfs.SubvolDelete(source); err != nil {
		return errors.Wrapf(err, "failed to remove snapshot %v", source)
	}

	err = t.Commit()
	t = nil
	if err != nil {
		// Attempt to restore source
		if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil {
			log.G(ctx).WithFields(logrus.Fields{
				logrus.ErrorKey: err1,
				"subvolume":     source,
				"renamed":       removed,
			}).Error("Failed to restore subvolume from renamed")
			// Keep removed to allow for manual restore
			removed = ""
		}
		return err
	}

	return nil
}
<commit_msg>Create btrfs directory if it does not exist<commit_after>// +build linux

package btrfs

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/containerd/btrfs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/plugin"
	"github.com/containerd/containerd/snapshot"
	"github.com/containerd/containerd/snapshot/storage"
	"github.com/pkg/errors"
)

func init() {
	plugin.Register(&plugin.Registration{
		ID:   "btrfs",
		Type: plugin.SnapshotPlugin,
		Init: func(ic *plugin.InitContext) (interface{}, error) {
			return NewSnapshotter(ic.Root)
		},
	})
}

type snapshotter struct {
	device string // device of the root
	root   string // root provides paths for internal storage.
	ms     *storage.MetaStore
}

// NewSnapshotter returns a Snapshotter using btrfs. Uses the provided
// root directory for snapshots and stores the metadata in
// a file in the provided root.
// root needs to be a mount point of btrfs.
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
	// If directory does not exist, create it
	if _, err := os.Stat(root); err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		if err := os.Mkdir(root, 0755); err != nil {
			return nil, err
		}
	}

	mnt, err := mount.Lookup(root)
	if err != nil {
		return nil, err
	}
	if mnt.FSType != "btrfs" {
		return nil, fmt.Errorf("expected btrfs, got %s", mnt.FSType)
	}
	var (
		active    = filepath.Join(root, "active")
		snapshots = filepath.Join(root, "snapshots")
	)

	for _, path := range []string{
		active,
		snapshots,
	} {
		if err := os.Mkdir(path, 0755); err != nil && !os.IsExist(err) {
			return nil, err
		}
	}
	ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
	if err != nil {
		return nil, err
	}

	return &snapshotter{
		device: mnt.Source,
		root:   root,
		ms:     ms,
	}, nil
}

// Stat returns the info for an active or committed snapshot by name or
// key.
//
// Should be used for parent resolution, existence checks and to discern
// the kind of snapshot.
func (b *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return snapshot.Info{}, err
	}
	defer t.Rollback()
	_, info, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return snapshot.Info{}, err
	}

	return info, nil
}

// Usage retrieves the disk usage of the top-level snapshot.
func (b *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
	panic("not implemented")

	// TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a
	// snapshot or shared among other resources. We may find that this is the
	// correct value to reoprt but the stability of the implementation is under
	// question.
	//
	// In general, this has impact on the model we choose for reporting usage.
	// Ideally, the value should allow aggregration. For overlay, this is
	// simple since we can scan the diff directory to get a unique value. This
	// breaks down when start looking the behavior when data is shared between
	// snapshots, such as that for btrfs.
}

// Walk the committed snapshots.
func (b *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return err
	}
	defer t.Rollback()
	return storage.WalkInfo(ctx, fn)
}

func (b *snapshotter) Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error) {
	return b.makeActive(ctx, key, parent, false)
}

func (b *snapshotter) View(ctx context.Context, key, parent string) ([]mount.Mount, error) {
	return b.makeActive(ctx, key, parent, true)
}

func (b *snapshotter) makeActive(ctx context.Context, key, parent string, readonly bool) ([]mount.Mount, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}
	}()

	a, err := storage.CreateActive(ctx, key, parent, readonly)
	if err != nil {
		return nil, err
	}

	target := filepath.Join(b.root, "active", a.ID)

	if len(a.ParentIDs) == 0 {
		// create new subvolume
		// btrfs subvolume create /dir
		if err = btrfs.SubvolCreate(target); err != nil {
			return nil, err
		}
	} else {
		parentp := filepath.Join(b.root, "snapshots", a.ParentIDs[0])
		// btrfs subvolume snapshot /parent /subvol
		if err = btrfs.SubvolSnapshot(target, parentp, a.Readonly); err != nil {
			return nil, err
		}
	}
	err = t.Commit()
	t = nil
	if err != nil {
		if derr := btrfs.SubvolDelete(target); derr != nil {
			log.G(ctx).WithError(derr).WithField("subvolume", target).Error("Failed to delete subvolume")
		}
		return nil, err
	}

	return b.mounts(target)
}

func (b *snapshotter) mounts(dir string) ([]mount.Mount, error) {
	var options []string

	// get the subvolume id back out for the mount
	info, err := btrfs.SubvolInfo(dir)
	if err != nil {
		return nil, err
	}

	options = append(options, fmt.Sprintf("subvolid=%d", info.ID))

	if info.Readonly {
		options = append(options, "ro")
	}

	return []mount.Mount{
		{
			Type:   "btrfs",
			Source: b.device,
			// NOTE(stevvooe): While it would be nice to use to uuids for
			// mounts, they don't work reliably if the uuids are missing.
			Options: options,
		},
	}, nil
}

func (b *snapshotter) Commit(ctx context.Context, name, key string) (err error) {
	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}
	}()

	id, err := storage.CommitActive(ctx, key, name, snapshot.Usage{}) // TODO(stevvooe): Resolve a usage value for btrfs
	if err != nil {
		return errors.Wrap(err, "failed to commit")
	}

	source := filepath.Join(b.root, "active", id)
	target := filepath.Join(b.root, "snapshots", id)

	if err := btrfs.SubvolSnapshot(target, source, true); err != nil {
		return err
	}

	err = t.Commit()
	t = nil
	if err != nil {
		if derr := btrfs.SubvolDelete(target); derr != nil {
			log.G(ctx).WithError(derr).WithField("subvolume", target).Error("Failed to delete subvolume")
		}
		return err
	}

	if derr := btrfs.SubvolDelete(source); derr != nil {
		// Log as warning, only needed for cleanup, will not cause name collision
		log.G(ctx).WithError(derr).WithField("subvolume", source).Warn("Failed to delete subvolume")
	}

	return nil
}

// Mounts returns the mounts for the transaction identified by key. Can be
// called on an read-write or readonly transaction.
//
// This can be used to recover mounts after calling View or Prepare.
func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
	ctx, t, err := b.ms.TransactionContext(ctx, false)
	if err != nil {
		return nil, err
	}
	a, err := storage.GetActive(ctx, key)
	t.Rollback()
	if err != nil {
		return nil, errors.Wrap(err, "failed to get active snapshot")
	}
	dir := filepath.Join(b.root, "active", a.ID)
	return b.mounts(dir)
}

// Remove abandons the transaction identified by key. All resources
// associated with the key will be removed.
func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
	var (
		source, removed string
		readonly        bool
	)

	ctx, t, err := b.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil && t != nil {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
			}
		}

		if removed != "" {
			if derr := btrfs.SubvolDelete(removed); derr != nil {
				log.G(ctx).WithError(derr).WithField("subvolume", removed).Warn("Failed to delete subvolume")
			}
		}
	}()

	id, k, err := storage.Remove(ctx, key)
	if err != nil {
		return errors.Wrap(err, "failed to remove snapshot")
	}

	if k == snapshot.KindActive {
		source = filepath.Join(b.root, "active", id)

		info, err := btrfs.SubvolInfo(source)
		if err != nil {
			source = ""
			return err
		}

		readonly = info.Readonly
		removed = filepath.Join(b.root, "active", "rm-"+id)
	} else {
		source = filepath.Join(b.root, "snapshots", id)
		removed = filepath.Join(b.root, "snapshots", "rm-"+id)
		readonly = true
	}

	if err := btrfs.SubvolSnapshot(removed, source, readonly); err != nil {
		removed = ""
		return err
	}

	if err := btrfs.SubvolDelete(source); err != nil {
		return errors.Wrapf(err, "failed to remove snapshot %v", source)
	}

	err = t.Commit()
	t = nil
	if err != nil {
		// Attempt to restore source
		if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil {
			log.G(ctx).WithFields(logrus.Fields{
				logrus.ErrorKey: err1,
				"subvolume":     source,
				"renamed":       removed,
			}).Error("Failed to restore subvolume from renamed")
			// Keep removed to allow for manual restore
			removed = ""
		}
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "code.google.com/p/go-tour/reader"

type MyReader struct{}

// TODO: Add a Read(byte) (int, error) method to MyReader.

func main() {
	reader.Validate(MyReader{})
}
<commit_msg>[x/tour] go-tour: fix typo in comment<commit_after>package main

import "code.google.com/p/go-tour/reader"

type MyReader struct{}

// TODO: Add a Read([]byte) (int, error) method to MyReader.

func main() {
	reader.Validate(MyReader{})
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package storage

import (
	"fmt"
	"math/rand"
	"net"
	"path"

	"time"

	. "github.com/onsi/ginkgo"
	"k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	versionutil "k8s.io/apimachinery/pkg/util/version"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/version"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e/framework/testfiles"
	"k8s.io/kubernetes/test/e2e/storage/utils"
)

const (
	sshPort                = "22"
	driverDir              = "test/e2e/testing-manifests/flexvolume/"
	defaultVolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec"
	// TODO: change this and config-test.sh when default flex volume install path is changed for GCI
	// On gci, root is read-only and controller-manager containerized. Assume
	// controller-manager has started with --flex-volume-plugin-dir equal to this
	// (see cluster/gce/config-test.sh)
	gciVolumePluginDir        = "/home/kubernetes/flexvolume"
	gciVolumePluginDirLegacy  = "/etc/srv/kubernetes/kubelet-plugins/volume/exec"
	gciVolumePluginDirVersion = "1.10.0"
	detachTimeout             = 10 * time.Second
)

// testFlexVolume tests that a client pod using a given flexvolume driver
// successfully mounts it and runs
func testFlexVolume(driver string, cs clientset.Interface, config framework.VolumeTestConfig, f *framework.Framework) {
	tests := []framework.VolumeTest{
		{
			Volume: v1.VolumeSource{
				FlexVolume: &v1.FlexVolumeSource{
					Driver: "k8s/" + driver,
				},
			},
			File: "index.html",
			// Must match content of examples/volumes/flexvolume/dummy(-attachable) domount
			ExpectedContent: "Hello from flexvolume!",
		},
	}
	framework.TestVolumeClient(cs, config, nil, tests)

	framework.VolumeTestCleanup(f, config)
}

// installFlex installs the driver found at filePath on the node, and restarts
// kubelet if 'restart' is true. If node is nil, installs on the master, and restarts
// controller-manager if 'restart' is true.
func installFlex(c clientset.Interface, node *v1.Node, vendor, driver, filePath string) {
	flexDir := getFlexDir(c, node, vendor, driver)
	flexFile := path.Join(flexDir, driver)

	host := ""
	var err error
	if node != nil {
		host, err = framework.GetNodeExternalIP(node)
		if err != nil {
			host, err = framework.GetNodeInternalIP(node)
		}
	} else {
		masterHostWithPort := framework.GetMasterHost()
		hostName := getHostFromHostPort(masterHostWithPort)
		host = net.JoinHostPort(hostName, sshPort)
	}

	framework.ExpectNoError(err)

	cmd := fmt.Sprintf("sudo mkdir -p %s", flexDir)
	sshAndLog(cmd, host, true /*failOnError*/)

	data := testfiles.ReadOrDie(filePath, Fail)
	cmd = fmt.Sprintf("sudo tee <<'EOF' %s\n%s\nEOF", flexFile, string(data))
	sshAndLog(cmd, host, true /*failOnError*/)

	cmd = fmt.Sprintf("sudo chmod +x %s", flexFile)
	sshAndLog(cmd, host, true /*failOnError*/)
}

func uninstallFlex(c clientset.Interface, node *v1.Node, vendor, driver string) {
	flexDir := getFlexDir(c, node, vendor, driver)

	host := ""
	var err error
	if node != nil {
		host, err = framework.GetNodeExternalIP(node)
		if err != nil {
			host, err = framework.GetNodeInternalIP(node)
		}
	} else {
		masterHostWithPort := framework.GetMasterHost()
		hostName := getHostFromHostPort(masterHostWithPort)
		host = net.JoinHostPort(hostName, sshPort)
	}

	if host == "" {
		framework.Failf("Error getting node ip : %v", err)
	}

	cmd := fmt.Sprintf("sudo rm -r %s", flexDir)
	sshAndLog(cmd, host, false /*failOnError*/)
}

func getFlexDir(c clientset.Interface, node *v1.Node, vendor, driver string) string {
	volumePluginDir := defaultVolumePluginDir
	if framework.ProviderIs("gce") {
		if node == nil && framework.MasterOSDistroIs("gci", "ubuntu") {
			v, err := getMasterVersion(c)
			if err != nil {
				framework.Failf("Error getting master version: %v", err)
			}

			if v.AtLeast(versionutil.MustParseGeneric(gciVolumePluginDirVersion)) {
				volumePluginDir = gciVolumePluginDir
			} else {
				volumePluginDir = gciVolumePluginDirLegacy
			}
		} else if node != nil && framework.NodeOSDistroIs("gci", "ubuntu") {
			if getNodeVersion(node).AtLeast(versionutil.MustParseGeneric(gciVolumePluginDirVersion)) {
				volumePluginDir = gciVolumePluginDir
			} else {
				volumePluginDir = gciVolumePluginDirLegacy
			}
		}
	}
	flexDir := path.Join(volumePluginDir, fmt.Sprintf("/%s~%s/", vendor, driver))
	return flexDir
}

func sshAndLog(cmd, host string, failOnError bool) {
	result, err := framework.SSH(cmd, host, framework.TestContext.Provider)
	framework.LogSSHResult(result)
	framework.ExpectNoError(err)
	if result.Code != 0 && failOnError {
		framework.Failf("%s returned non-zero, stderr: %s", cmd, result.Stderr)
	}
}

func getMasterVersion(c clientset.Interface) (*versionutil.Version, error) {
	var err error
	var v *version.Info
	waitErr := wait.PollImmediate(5*time.Second, 2*time.Minute, func() (bool, error) {
		v, err = c.Discovery().ServerVersion()
		return err == nil, nil
	})
	if waitErr != nil {
		return nil, fmt.Errorf("Could not get the master version: %v", waitErr)
	}

	return versionutil.MustParseSemantic(v.GitVersion), nil
}

func getNodeVersion(node *v1.Node) *versionutil.Version {
	return versionutil.MustParseSemantic(node.Status.NodeInfo.KubeletVersion)
}

func getHostFromHostPort(hostPort string) string {
	// try to split host and port
	var host string
	var err error
	if host, _, err = net.SplitHostPort(hostPort); err != nil {
		// if SplitHostPort returns an error, the entire hostport is considered as host
		host = hostPort
	}
	return host
}

var _ = utils.SIGDescribe("Flexvolumes", func() {
	f := framework.NewDefaultFramework("flexvolume")

	// note that namespace deletion is handled by delete-namespace flag

	var cs clientset.Interface
	var ns *v1.Namespace
	var node v1.Node
	var config framework.VolumeTestConfig
	var suffix string

	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gce", "local")
		framework.SkipUnlessMasterOSDistroIs("debian", "ubuntu", "gci", "custom")
		framework.SkipUnlessNodeOSDistroIs("debian", "ubuntu", "gci", "custom")
		framework.SkipUnlessSSHKeyPresent()

		cs = f.ClientSet
		ns = f.Namespace
		nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
		node = nodes.Items[rand.Intn(len(nodes.Items))]
		config = framework.VolumeTestConfig{
			Namespace:      ns.Name,
			Prefix:         "flex",
			ClientNodeName: node.Name,
		}
		suffix = ns.Name
	})

	It("should be mountable when non-attachable", func() {
		driver := "dummy"
		driverInstallAs := driver + "-" + suffix

		By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
		installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))

		testFlexVolume(driverInstallAs, cs, config, f)

		By("waiting for flex client pod to terminate")
		if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
			framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
		}

		By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
		uninstallFlex(cs, &node, "k8s", driverInstallAs)
	})

	It("should be mountable when attachable", func() {
		driver := "dummy-attachable"
		driverInstallAs := driver + "-" + suffix

		By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
		installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))
		By(fmt.Sprintf("installing flexvolume %s on master as %s", path.Join(driverDir, driver), driverInstallAs))
		installFlex(cs, nil, "k8s", driverInstallAs, path.Join(driverDir, driver))

		testFlexVolume(driverInstallAs, cs, config, f)

		By("waiting for flex client pod to terminate")
		if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
			framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
		}

		// Detach might occur after pod deletion. Wait before deleting driver.
		time.Sleep(detachTimeout)

		By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
		uninstallFlex(cs, &node, "k8s", driverInstallAs)
		By(fmt.Sprintf("uninstalling flexvolume %s from master", driverInstallAs))
		uninstallFlex(cs, nil, "k8s", driverInstallAs)
	})
})
<commit_msg>Remove legacy gci flex directory from e2e tests<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package storage

import (
	"fmt"
	"math/rand"
	"net"
	"path"

	"time"

	. "github.com/onsi/ginkgo"
	"k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	versionutil "k8s.io/apimachinery/pkg/util/version"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/version"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e/framework/testfiles"
	"k8s.io/kubernetes/test/e2e/storage/utils"
)

const (
	sshPort                = "22"
	driverDir              = "test/e2e/testing-manifests/flexvolume/"
	defaultVolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec"
	// TODO: change this and config-test.sh when default flex volume install path is changed for GCI
	// On gci, root is read-only and controller-manager containerized. Assume
	// controller-manager has started with --flex-volume-plugin-dir equal to this
	// (see cluster/gce/config-test.sh)
	gciVolumePluginDir = "/home/kubernetes/flexvolume"
	detachTimeout      = 10 * time.Second
)

// testFlexVolume tests that a client pod using a given flexvolume driver
// successfully mounts it and runs
func testFlexVolume(driver string, cs clientset.Interface, config framework.VolumeTestConfig, f *framework.Framework) {
	tests := []framework.VolumeTest{
		{
			Volume: v1.VolumeSource{
				FlexVolume: &v1.FlexVolumeSource{
					Driver: "k8s/" + driver,
				},
			},
			File: "index.html",
			// Must match content of examples/volumes/flexvolume/dummy(-attachable) domount
			ExpectedContent: "Hello from flexvolume!",
		},
	}
	framework.TestVolumeClient(cs, config, nil, tests)

	framework.VolumeTestCleanup(f, config)
}

// installFlex installs the driver found at filePath on the node, and restarts
// kubelet if 'restart' is true. If node is nil, installs on the master, and restarts
// controller-manager if 'restart' is true.
func installFlex(c clientset.Interface, node *v1.Node, vendor, driver, filePath string) {
	flexDir := getFlexDir(c, node, vendor, driver)
	flexFile := path.Join(flexDir, driver)

	host := ""
	var err error
	if node != nil {
		host, err = framework.GetNodeExternalIP(node)
		if err != nil {
			host, err = framework.GetNodeInternalIP(node)
		}
	} else {
		masterHostWithPort := framework.GetMasterHost()
		hostName := getHostFromHostPort(masterHostWithPort)
		host = net.JoinHostPort(hostName, sshPort)
	}

	framework.ExpectNoError(err)

	cmd := fmt.Sprintf("sudo mkdir -p %s", flexDir)
	sshAndLog(cmd, host, true /*failOnError*/)

	data := testfiles.ReadOrDie(filePath, Fail)
	cmd = fmt.Sprintf("sudo tee <<'EOF' %s\n%s\nEOF", flexFile, string(data))
	sshAndLog(cmd, host, true /*failOnError*/)

	cmd = fmt.Sprintf("sudo chmod +x %s", flexFile)
	sshAndLog(cmd, host, true /*failOnError*/)
}

func uninstallFlex(c clientset.Interface, node *v1.Node, vendor, driver string) {
	flexDir := getFlexDir(c, node, vendor, driver)

	host := ""
	var err error
	if node != nil {
		host, err = framework.GetNodeExternalIP(node)
		if err != nil {
			host, err = framework.GetNodeInternalIP(node)
		}
	} else {
		masterHostWithPort := framework.GetMasterHost()
		hostName := getHostFromHostPort(masterHostWithPort)
		host = net.JoinHostPort(hostName, sshPort)
	}

	if host == "" {
		framework.Failf("Error getting node ip : %v", err)
	}

	cmd := fmt.Sprintf("sudo rm -r %s", flexDir)
	sshAndLog(cmd, host, false /*failOnError*/)
}

func getFlexDir(c clientset.Interface, node *v1.Node, vendor, driver string) string {
	volumePluginDir := defaultVolumePluginDir
	if framework.ProviderIs("gce") {
		volumePluginDir = gciVolumePluginDir
	}
	flexDir := path.Join(volumePluginDir, fmt.Sprintf("/%s~%s/", vendor, driver))
	return flexDir
}

func sshAndLog(cmd, host string, failOnError bool) {
	result, err := framework.SSH(cmd, host, framework.TestContext.Provider)
	framework.LogSSHResult(result)
	framework.ExpectNoError(err)
	if result.Code != 0 && failOnError {
		framework.Failf("%s returned non-zero, stderr: %s", cmd, result.Stderr)
	}
}

func getMasterVersion(c clientset.Interface) (*versionutil.Version, error) {
	var err error
	var v *version.Info
	waitErr := wait.PollImmediate(5*time.Second, 2*time.Minute, func() (bool, error) {
		v, err = c.Discovery().ServerVersion()
		return err == nil, nil
	})
	if waitErr != nil {
		return nil, fmt.Errorf("Could not get the master version: %v", waitErr)
	}

	return versionutil.MustParseSemantic(v.GitVersion), nil
}

func getNodeVersion(node *v1.Node) *versionutil.Version {
	return versionutil.MustParseSemantic(node.Status.NodeInfo.KubeletVersion)
}

func getHostFromHostPort(hostPort string) string {
	// try to split host and port
	var host string
	var err error
	if host, _, err = net.SplitHostPort(hostPort); err != nil {
		// if SplitHostPort returns an error, the entire hostport is considered as host
		host = hostPort
	}
	return host
}

var _ = utils.SIGDescribe("Flexvolumes", func() {
	f := framework.NewDefaultFramework("flexvolume")

	// note that namespace deletion is handled by delete-namespace flag

	var cs clientset.Interface
	var ns *v1.Namespace
	var node v1.Node
	var config framework.VolumeTestConfig
	var suffix string

	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gce", "local")
		framework.SkipUnlessMasterOSDistroIs("debian", "ubuntu", "gci", "custom")
		framework.SkipUnlessNodeOSDistroIs("debian", "ubuntu", "gci", "custom")
		framework.SkipUnlessSSHKeyPresent()

		cs = f.ClientSet
		ns = f.Namespace
		nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
		node = nodes.Items[rand.Intn(len(nodes.Items))]
		config = framework.VolumeTestConfig{
			Namespace:      ns.Name,
			Prefix:         "flex",
			ClientNodeName: node.Name,
		}
		suffix = ns.Name
	})

	It("should be mountable when non-attachable", func() {
		driver := "dummy"
		driverInstallAs := driver + "-" + suffix

		By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
		installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))

		testFlexVolume(driverInstallAs, cs, config, f)

		By("waiting for flex client pod to terminate")
		if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
			framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
		}

		By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
		uninstallFlex(cs, &node, "k8s", driverInstallAs)
	})

	It("should be mountable when attachable", func() {
		driver := "dummy-attachable"
		driverInstallAs := driver + "-" + suffix

		By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
		installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))
		By(fmt.Sprintf("installing flexvolume %s on master as %s", path.Join(driverDir, driver), driverInstallAs))
		installFlex(cs, nil, "k8s", driverInstallAs, path.Join(driverDir, driver))

		testFlexVolume(driverInstallAs, cs, config, f)

		By("waiting for flex client pod to terminate")
		if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
			framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
		}

		// Detach might occur after pod deletion. Wait before deleting driver.
		time.Sleep(detachTimeout)

		By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
		uninstallFlex(cs, &node, "k8s", driverInstallAs)
		By(fmt.Sprintf("uninstalling flexvolume %s from master", driverInstallAs))
		uninstallFlex(cs, nil, "k8s", driverInstallAs)
	})
})
<|endoftext|>