<commit_before>// composer is a basic package to run composer tasks in a Drupal 8 docroot.
package composer

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

	log "github.com/Sirupsen/logrus"
	"github.com/fubarhouse/golang-drush/makeupdater"
)

// DrupalProject is a type to provide both name and verison of a given Drupal project.
type DrupalProject struct {
	Project string
	Version string
	Patch   string
	Subdir  string
}

// GetProjects will return all projects in a make file with the format of a DrupalProject.
// This function will slowly take over a lot of similar functionality.
func GetProjects(fullpath string) []DrupalProject {
	Projects := []DrupalProject{}
	for _, Project := range makeupdater.GetProjectsFromMake(fullpath) {
		catCmd := fmt.Sprintf("cat %v | grep \"projects\\[%v\\]\"", fullpath, Project)
		y, _ := exec.Command("sh", "-c", catCmd).CombinedOutput()
		DrupalProject := DrupalProject{}
		for _, Line := range strings.Split(string(y), "\n") {
			if strings.Contains(Line, "projects["+Project+"][version] = ") {
				Version := strings.Split(Line, "=")
				Version[1] = strings.Trim(Version[1], " ")
				Version[1] = strings.Replace(Version[1], "\"", "", -1)
				DrupalProject.Version = Version[1]
			} else if strings.Contains(Line, "projects["+Project+"][subdir] = ") {
				Subdir := strings.Split(Line, "=")
				Subdir[1] = strings.Trim(Subdir[1], " ")
				Subdir[1] = strings.Replace(Subdir[1], "\"", "", -1)
				DrupalProject.Subdir = Subdir[1]
			} else if strings.Contains(Line, "projects["+Project+"][patch] = ") {
				Patch := strings.Split(Line, "=")
				Patch[1] = strings.Trim(Patch[1], " ")
				Patch[1] = strings.Replace(Patch[1], "\"", "", -1)
				DrupalProject.Patch = Patch[1]
			}
		}
		if Project != "drupal" {
			DrupalProject.Project = Project
			Projects = append(Projects, DrupalProject)
		}
	}
	return Projects
}

// InstallProjects will install drupal composer projects via composer.
// It will do this for every project in the make file, or as a []DrupalProject input.
func InstallProjects(Projects []DrupalProject, Path string) {
	for _, Project := range Projects {
		log.Infof("Processing drupal/%v:%v:", Project.Project, Project.Version)
		ProjectString := fmt.Sprintf("drupal/%v:%v", Project.Project, Project.Version)
		cpCmd := exec.Command("sh", "-c", "cd "+Path+" && composer require "+ProjectString)
		_, cpErr := cpCmd.CombinedOutput()
		if cpErr != nil {
			log.Errorln("Could not complete:", cpErr)
		}
	}
}

// FindComposerJSONFiles will find all the composer.json files inside custom modules/themes
// in a Drupal 8 (or even 7) file system. It will return the paths  in a []string
// which can be iterated over for processing.
func FindComposerJSONFiles(Path string) []string {
	fileList := []string{}
	fmt.Println(len(fileList))
	filepath.Walk(Path, func(path string, f os.FileInfo, err error) error {
		fileList = append(fileList, path)
		return nil
	})

	results := []string{}
	for _, file := range fileList {
		if strings.Contains(file, "/custom/") && strings.HasSuffix(file, "composer.json") {
			results = append(results, file)
		}
	}

	return results
}

// InstallComposerJSONFiles will accept a []string of paths
// and run a composer install over each of the files found.
func InstallComposerJSONFiles(Paths []string) {
	for _, v := range Paths {
		v = strings.Replace(v, "composer.json", "", -1)
		cpCmd := exec.Command("composer", "install", "--prefer-dist", "--working-dir="+v)
		cpOut, cpErr := cpCmd.CombinedOutput()
		if cpErr != nil {
			log.Errorln("Could not complete:", string(cpOut), cpErr)
		} else {
			log.Println(string(cpOut))
		}
	}
}

// copy will copy a file to a destination.
func copy(src, dest string) error {
	data, err := ioutil.ReadFile(src)
	if err != nil {
		log.Fatalf("Could not read %v: %v", src, err.Error())
		return err
	}
	err = ioutil.WriteFile(dest, data, 0644)
	if err != nil {
		log.Fatalf("Could not write %v: %v", dest, err.Error())
		return err
	}
	return nil
}

// InstallComposerCodebase will accept a []string of paths
// and run a composer install over each of the files found.
func InstallComposerCodebase(Name, Timestamp string, ComposerFile, Destination string) {
	Destination += "/" + Name + Timestamp
	// Identify if copying the file is required.
	ComposerPath := strings.Replace(ComposerFile, "/composer.json", "", -1)
	ComposerPath = strings.TrimRight(ComposerPath, "/")
	ComposerDestination := strings.TrimRight(Destination, "/") + "/" + Name + Timestamp

	if _, err := os.Stat(Destination); err != nil {
		ok := os.MkdirAll(Destination, 0775)
		if ok != nil {
			log.Fatalf("could not create directory %v: %v", Destination, ok.Error())
		}
	}

	if !strings.HasSuffix(ComposerDestination, ComposerPath) {
		log.Infof("composer.json not found, copying from %v", ComposerFile)
		copy(ComposerFile, Destination+"/composer.json")
	} else {
		log.Infof("%v/composer.json was found, not copying", ComposerDestination)
	}
	cpCmd := exec.Command("sh", "-c", "composer install --prefer-source")
	cpCmd.Dir = Destination
	cpCmd.Stdout = os.Stdout
	cpCmd.Stderr = os.Stderr
	cpCmd.Run()
	cpCmd.Wait()

}
<commit_msg>Update to improve gometalinter scores<commit_after>// Package composer is a basic package to run composer tasks in a Drupal 8 docroot.
package composer

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

	log "github.com/Sirupsen/logrus"
	"github.com/fubarhouse/golang-drush/makeupdater"
)

// DrupalProject is a type to provide both name and verison of a given Drupal project.
type DrupalProject struct {
	Project string
	Version string
	Patch   string
	Subdir  string
}

// GetProjects will return all projects in a make file with the format of a DrupalProject.
// This function will slowly take over a lot of similar functionality.
func GetProjects(fullpath string) []DrupalProject {
	Projects := []DrupalProject{}
	for _, Project := range makeupdater.GetProjectsFromMake(fullpath) {
		catCmd := fmt.Sprintf("cat %v | grep \"projects\\[%v\\]\"", fullpath, Project)
		y, e := exec.Command("sh", "-c", catCmd).CombinedOutput()
		if e != nil {
			log.Warnf("Could not execute `%v`\n", catCmd)
		}
		project := DrupalProject{}
		for _, Line := range strings.Split(string(y), "\n") {
			if strings.Contains(Line, "projects["+Project+"][version] = ") {
				Version := strings.Split(Line, "=")
				Version[1] = strings.Trim(Version[1], " ")
				Version[1] = strings.Replace(Version[1], "\"", "", -1)
				project.Version = Version[1]
			} else if strings.Contains(Line, "projects["+Project+"][subdir] = ") {
				Subdir := strings.Split(Line, "=")
				Subdir[1] = strings.Trim(Subdir[1], " ")
				Subdir[1] = strings.Replace(Subdir[1], "\"", "", -1)
				project.Subdir = Subdir[1]
			} else if strings.Contains(Line, "projects["+Project+"][patch] = ") {
				Patch := strings.Split(Line, "=")
				Patch[1] = strings.Trim(Patch[1], " ")
				Patch[1] = strings.Replace(Patch[1], "\"", "", -1)
				project.Patch = Patch[1]
			}
		}
		if Project != "drupal" {
			project.Project = Project
			Projects = append(Projects, project)
		}
	}
	return Projects
}

// InstallProjects will install drupal composer projects via composer.
// It will do this for every project in the make file, or as a []DrupalProject input.
func InstallProjects(Projects []DrupalProject, Path string) {
	for _, Project := range Projects {
		log.Infof("Processing drupal/%v:%v:", Project.Project, Project.Version)
		ProjectString := fmt.Sprintf("drupal/%v:%v", Project.Project, Project.Version)
		cpCmd := exec.Command("sh", "-c", "cd "+Path+" && composer require "+ProjectString)
		_, cpErr := cpCmd.CombinedOutput()
		if cpErr != nil {
			log.Errorln("Could not complete:", cpErr)
		}
	}
}

// FindComposerJSONFiles will find all the composer.json files inside custom modules/themes
// in a Drupal 8 (or even 7) file system. It will return the paths  in a []string
// which can be iterated over for processing.
func FindComposerJSONFiles(Path string) []string {
	fileList := []string{}
	fmt.Println(len(fileList))
	e := filepath.Walk(Path, func(path string, f os.FileInfo, err error) error {
		fileList = append(fileList, path)
		return nil
	})

	if e != nil {
		log.Warnf("Could not scan for composer.json files under %v: %v\n", Path, e)
	}

	results := []string{}
	for _, file := range fileList {
		if strings.Contains(file, "/custom/") && strings.HasSuffix(file, "composer.json") {
			results = append(results, file)
		}
	}

	return results
}

// InstallComposerJSONFiles will accept a []string of paths
// and run a composer install over each of the files found.
func InstallComposerJSONFiles(Paths []string) {
	for _, v := range Paths {
		v = strings.Replace(v, "composer.json", "", -1)
		cpCmd := exec.Command("composer", "install", "--prefer-dist", "--working-dir="+v)
		cpOut, cpErr := cpCmd.CombinedOutput()
		if cpErr != nil {
			log.Errorln("Could not complete:", string(cpOut), cpErr)
		} else {
			log.Println(string(cpOut))
		}
	}
}

// copy will copy a file to a destination.
func copy(src, dest string) error {
	data, err := ioutil.ReadFile(src)
	if err != nil {
		return errors.New("could not read " + src + ": " + err.Error())
	}
	err = ioutil.WriteFile(dest, data, 0644)
	if err != nil {
		return errors.New("could not write " + src + ": " + err.Error())
	}
	return nil
}

// InstallComposerCodebase will accept a []string of paths
// and run a composer install over each of the files found.
func InstallComposerCodebase(Name, Timestamp string, ComposerFile, Destination string) {
	Destination += "/" + Name + Timestamp
	// Identify if copying the file is required.
	ComposerPath := strings.Replace(ComposerFile, "/composer.json", "", -1)
	ComposerPath = strings.TrimRight(ComposerPath, "/")
	ComposerDestination := strings.TrimRight(Destination, "/") + "/" + Name + Timestamp

	if _, err := os.Stat(Destination); err != nil {
		ok := os.MkdirAll(Destination, 0700)
		if ok != nil {
			log.Fatalf("could not create directory %v: %v", Destination, ok.Error())
		}
	}

	if !strings.HasSuffix(ComposerDestination, ComposerPath) {
		log.Infof("composer.json not found, copying from %v", ComposerFile)
		e := copy(ComposerFile, Destination+"/composer.json")
		if e != nil {
			log.Warnln(e)
		} else {
			log.Printf("Copied %v to %v\n", ComposerFile, Destination+"/composer.json")
		}
	} else {
		log.Infof("%v/composer.json was found, not copying", ComposerDestination)
	}
	cpCmd := exec.Command("sh", "-c", "composer install --prefer-source")
	cpCmd.Dir = Destination
	cpCmd.Stdout = os.Stdout
	cpCmd.Stderr = os.Stderr
	e := cpCmd.Run()
	if e != nil {
		log.Warnln("could not execute composer install --prefer-source\n")
	}
	f := cpCmd.Wait()
	if f != nil {
		log.Warnln(f)
	}

}
<|endoftext|>


<commit_before>package common

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/user"
	"strconv"
	"strings"
)

// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
// FROM: https://stackoverflow.com/a/21067803/1515875
func CopyFile(src, dst string) (err error) {
	sfi, err := os.Stat(src)
	if err != nil {
		return
	}
	if !sfi.Mode().IsRegular() {
		// cannot copy non-regular files (e.g., directories,
		// symlinks, devices, etc.)
		return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
	}
	dfi, err := os.Stat(dst)
	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
	} else {
		if !(dfi.Mode().IsRegular()) {
			return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
		}
		if os.SameFile(sfi, dfi) {
			return
		}
	}
	if err = os.Link(src, dst); err == nil {
		return
	}
	err = copyFileContents(src, dst)
	return
}

// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
// FROM: https://stackoverflow.com/a/21067803/1515875
func copyFileContents(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()
	if _, err = io.Copy(out, in); err != nil {
		return
	}
	err = out.Sync()
	return
}

// DirectoryExists returns if a path exists and is a directory
func DirectoryExists(filename string) bool {
	fi, err := os.Stat(filename)
	if err != nil {
		return false
	}

	return fi.IsDir()
}

// FileToSlice reads in all the lines from a file into a string slice
func FileToSlice(filename string) (lines []string, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		text := strings.TrimSpace(scanner.Text())
		if text == "" {
			continue
		}
		lines = append(lines, text)
	}
	err = scanner.Err()
	return
}

// FileExists returns if a path exists and is a file
func FileExists(filename string) bool {
	fi, err := os.Stat(filename)
	if err != nil {
		return false
	}

	return fi.Mode().IsRegular()
}

// IsAbsPath returns 0 if input path is absolute
func IsAbsPath(path string) bool {
	return strings.HasPrefix(path, "/")
}

// ReadFirstLine gets the first line of a file that has contents and returns it
// if there are no contents, an empty string is returned
// will also return an empty string if the file does not exist
func ReadFirstLine(filename string) (text string) {
	if !FileExists(filename) {
		return
	}
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		if text = strings.TrimSpace(scanner.Text()); text == "" {
			continue
		}
		return
	}
	return
}

// SetPermissions sets the proper owner and filemode for a given file
func SetPermissions(path string, fileMode os.FileMode) error {
	if err := os.Chmod(path, fileMode); err != nil {
		return err
	}

	systemGroup := GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
	systemUser := GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")

	group, err := user.LookupGroup(systemGroup)
	if err != nil {
		return err
	}
	user, err := user.Lookup(systemUser)
	if err != nil {
		return err
	}

	uid, err := strconv.Atoi(user.Uid)
	if err != nil {
		return err
	}

	gid, err := strconv.Atoi(group.Gid)
	if err != nil {
		return err
	}
	return os.Chown(path, uid, gid)
}

func WriteSliceToFile(filename string, lines []string) error {
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return err
	}

	file.Chmod(0600)
	SetPermissions(filename, 0600)

	return nil
}
<commit_msg>fix: add missing comment<commit_after>package common

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/user"
	"strconv"
	"strings"
)

// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
// FROM: https://stackoverflow.com/a/21067803/1515875
func CopyFile(src, dst string) (err error) {
	sfi, err := os.Stat(src)
	if err != nil {
		return
	}
	if !sfi.Mode().IsRegular() {
		// cannot copy non-regular files (e.g., directories,
		// symlinks, devices, etc.)
		return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
	}
	dfi, err := os.Stat(dst)
	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
	} else {
		if !(dfi.Mode().IsRegular()) {
			return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
		}
		if os.SameFile(sfi, dfi) {
			return
		}
	}
	if err = os.Link(src, dst); err == nil {
		return
	}
	err = copyFileContents(src, dst)
	return
}

// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
// FROM: https://stackoverflow.com/a/21067803/1515875
func copyFileContents(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()
	if _, err = io.Copy(out, in); err != nil {
		return
	}
	err = out.Sync()
	return
}

// DirectoryExists returns if a path exists and is a directory
func DirectoryExists(filename string) bool {
	fi, err := os.Stat(filename)
	if err != nil {
		return false
	}

	return fi.IsDir()
}

// FileToSlice reads in all the lines from a file into a string slice
func FileToSlice(filename string) (lines []string, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		text := strings.TrimSpace(scanner.Text())
		if text == "" {
			continue
		}
		lines = append(lines, text)
	}
	err = scanner.Err()
	return
}

// FileExists returns if a path exists and is a file
func FileExists(filename string) bool {
	fi, err := os.Stat(filename)
	if err != nil {
		return false
	}

	return fi.Mode().IsRegular()
}

// IsAbsPath returns 0 if input path is absolute
func IsAbsPath(path string) bool {
	return strings.HasPrefix(path, "/")
}

// ReadFirstLine gets the first line of a file that has contents and returns it
// if there are no contents, an empty string is returned
// will also return an empty string if the file does not exist
func ReadFirstLine(filename string) (text string) {
	if !FileExists(filename) {
		return
	}
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		if text = strings.TrimSpace(scanner.Text()); text == "" {
			continue
		}
		return
	}
	return
}

// SetPermissions sets the proper owner and filemode for a given file
func SetPermissions(path string, fileMode os.FileMode) error {
	if err := os.Chmod(path, fileMode); err != nil {
		return err
	}

	systemGroup := GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
	systemUser := GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")

	group, err := user.LookupGroup(systemGroup)
	if err != nil {
		return err
	}
	user, err := user.Lookup(systemUser)
	if err != nil {
		return err
	}

	uid, err := strconv.Atoi(user.Uid)
	if err != nil {
		return err
	}

	gid, err := strconv.Atoi(group.Gid)
	if err != nil {
		return err
	}
	return os.Chown(path, uid, gid)
}

// WriteSliceToFile writes a slice of strings to a file
func WriteSliceToFile(filename string, lines []string) error {
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if err = w.Flush(); err != nil {
		return err
	}

	file.Chmod(0600)
	SetPermissions(filename, 0600)

	return nil
}
<|endoftext|>


<commit_before>package file

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/vektra/cypress"
	"github.com/vektra/tai64n"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/tomb.v2"
)

type Line struct {
	Line   string
	Offset int64
	Time   time.Time
}

func (l *Line) Next() int64 {
	return l.Offset + int64(len(l.Line))
}

type File struct {
	path string
	name string

	lastOffset int64

	t tomb.Tomb

	offset int64
	lines  chan Line
}

func NewFile(path string, offset int64) (*File, error) {
	f := &File{
		path:   path,
		name:   filepath.Base(path),
		lines:  make(chan Line),
		offset: offset,
	}

	f.t.Go(f.readOnce)

	return f, nil
}

func (f *File) readOnce() error {
	defer close(f.lines)

	r, err := os.Open(f.path)

	if err != nil {
		return err
	}

	defer r.Close()

	_, err = r.Seek(f.offset, os.SEEK_SET)
	if err != nil {
		return err
	}

	buf := bufio.NewReader(r)

	offset := f.offset

	for {
		str, err := buf.ReadString('\n')
		if err != nil {
			return err
		}

		select {
		case f.lines <- Line{str, offset, time.Now()}:
		case <-f.t.Dying():
			return nil
		}

		offset += int64(len(str))
	}

	return nil
}

func NewFollowFile(path string, offset int64) (*File, error) {
	f := &File{
		path:   path,
		name:   filepath.Base(path),
		lines:  make(chan Line),
		offset: offset,
	}

	f.t.Go(f.readFollow)

	return f, nil
}

func (f *File) readFollow() error {
	defer close(f.lines)

	r, err := os.Open(f.path)

	if err != nil {
		return err
	}

	defer r.Close()

	_, err = r.Seek(f.offset, os.SEEK_SET)
	if err != nil {
		return err
	}

	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	defer w.Close()

	w.Add(f.path)
	w.Add(filepath.Dir(f.path))

	buf := bufio.NewReader(r)

	offset := f.offset

top:
	for {
		str, err := buf.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return err
			}

			for {
				select {
				case evt := <-w.Events:
					if evt.Op&fsnotify.Write == fsnotify.Write {
						continue top
					}

					if evt.Op&fsnotify.Create == fsnotify.Create {
						if evt.Name == f.path {
							r.Close()

							r, err = os.Open(f.path)
							if err != nil {
								return err
							}

							buf = bufio.NewReader(r)

							offset = 0

							continue top
						}
					}
				case err := <-w.Errors:
					return err
				case <-f.t.Dying():
					return nil
				}
			}
		}

		select {
		case f.lines <- Line{str, offset, time.Now()}:
			// nothing

		case <-f.t.Dying():
			return nil
		}

		offset += int64(len(str))
	}

	return nil
}

func (f *File) Tell() (int64, error) {
	return f.lastOffset, nil
}

func (f *File) Close() error {
	f.t.Kill(nil)
	f.t.Wait()

	return nil
}

func (f *File) Generate() (*cypress.Message, error) {
	line, err := f.GenerateLine()
	if err != nil {
		return nil, err
	}

	f.lastOffset = line.Offset + int64(len(line.Line))

	m := cypress.Log()
	m.Timestamp = tai64n.FromTime(line.Time)
	m.AddTag("source", f.name)
	m.Add("message", strings.TrimSpace(line.Line))

	return m, nil
}

func (f *File) GenerateLine() (*Line, error) {
	line, ok := <-f.lines
	if !ok {
		return nil, io.EOF
	}

	return &line, nil
}
<commit_msg>Add ability to access the channel directly<commit_after>package file

import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/vektra/cypress"
	"github.com/vektra/tai64n"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/tomb.v2"
)

type Line struct {
	Line   string
	Offset int64
	Time   time.Time
}

func (l *Line) Next() int64 {
	return l.Offset + int64(len(l.Line))
}

type File struct {
	path string
	name string

	lastOffset int64

	t tomb.Tomb

	offset int64
	lines  chan Line
}

func NewFile(path string, offset int64) (*File, error) {
	f := &File{
		path:   path,
		name:   filepath.Base(path),
		lines:  make(chan Line),
		offset: offset,
	}

	f.t.Go(f.readOnce)

	return f, nil
}

func (f *File) readOnce() error {
	defer close(f.lines)

	r, err := os.Open(f.path)

	if err != nil {
		return err
	}

	defer r.Close()

	_, err = r.Seek(f.offset, os.SEEK_SET)
	if err != nil {
		return err
	}

	buf := bufio.NewReader(r)

	offset := f.offset

	for {
		str, err := buf.ReadString('\n')
		if err != nil {
			return err
		}

		select {
		case f.lines <- Line{str, offset, time.Now()}:
		case <-f.t.Dying():
			return nil
		}

		offset += int64(len(str))
	}

	return nil
}

func NewFollowFile(path string, offset int64) (*File, error) {
	f := &File{
		path:   path,
		name:   filepath.Base(path),
		lines:  make(chan Line),
		offset: offset,
	}

	f.t.Go(f.readFollow)

	return f, nil
}

func (f *File) readFollow() error {
	defer close(f.lines)

	r, err := os.Open(f.path)

	if err != nil {
		return err
	}

	defer r.Close()

	_, err = r.Seek(f.offset, os.SEEK_SET)
	if err != nil {
		return err
	}

	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	defer w.Close()

	w.Add(f.path)
	w.Add(filepath.Dir(f.path))

	buf := bufio.NewReader(r)

	offset := f.offset

top:
	for {
		str, err := buf.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return err
			}

			for {
				select {
				case evt := <-w.Events:
					if evt.Op&fsnotify.Write == fsnotify.Write {
						continue top
					}

					if evt.Op&fsnotify.Create == fsnotify.Create {
						if evt.Name == f.path {
							r.Close()

							r, err = os.Open(f.path)
							if err != nil {
								return err
							}

							buf = bufio.NewReader(r)

							offset = 0

							continue top
						}
					}
				case err := <-w.Errors:
					return err
				case <-f.t.Dying():
					return nil
				}
			}
		}

		select {
		case f.lines <- Line{str, offset, time.Now()}:
			// nothing

		case <-f.t.Dying():
			return nil
		}

		offset += int64(len(str))
	}

	return nil
}

func (f *File) Tell() (int64, error) {
	return f.lastOffset, nil
}

func (f *File) Close() error {
	f.t.Kill(nil)
	f.t.Wait()

	return nil
}

func (f *File) Generate() (*cypress.Message, error) {
	line, err := f.GenerateLine()
	if err != nil {
		return nil, err
	}

	f.lastOffset = line.Offset + int64(len(line.Line))

	m := cypress.Log()
	m.Timestamp = tai64n.FromTime(line.Time)
	m.AddTag("source", f.name)
	m.Add("message", strings.TrimSpace(line.Line))

	return m, nil
}

func (f *File) Lines() chan Line {
	return f.lines
}

func (f *File) GenerateLine() (*Line, error) {
	line, ok := <-f.lines
	if !ok {
		return nil, io.EOF
	}

	return &line, nil
}
<|endoftext|>


<commit_before>package push

import (
	"io/ioutil"
	"os"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("deprecated route command-line flags", func() {

	const deprecationTemplate = "Deprecation warning: Use of the '%[1]s' command-line flag option is deprecated in favor of the 'routes' property in the manifest. Please see https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#routes for usage information. The '%[1]s' command-line flag option will be removed in the future."

	const legacyManifestDeprecationTemplate = `Deprecation warning: Specifying app manifest attributes at the top level is deprecated. Found: host.
Please see https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#deprecated for alternatives and other app manifest deprecations. This feature will be removed in the future.`

	var (
		appName       string
		host          string
		privateDomain string
		localArgs     []string
		session       *Session
	)

	BeforeEach(func() {
		appName = helpers.NewAppName()
		host = helpers.NewAppName()

		privateDomain = helpers.NewDomainName()
		domain := helpers.NewDomain(organization, privateDomain)
		domain.Create()
	})
	When("with no manifest", func() {
		JustBeforeEach(func() {
			helpers.WithHelloWorldApp(func(dir string) {
				allArgs := []string{PushCommandName, appName, "--no-start"}
				allArgs = append(allArgs, localArgs...)
				session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, allArgs...)
				Eventually(session).Should(Exit(0))
			})
		})

		When("no deprecated flags are provided", func() {
			It("does not output a deprecation warning", func() {
				Expect(string(session.Err.Contents())).ToNot(ContainSubstring("deprecated"))
			})
		})

		When("the -d (domains) flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "-d"))
			})
		})

		When("the --hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--hostname", host}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--hostname"))
			})
		})

		When("the --no-hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--no-hostname", "-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--no-hostname"))
			})
		})

		When("the --route-path flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--route-path", "some-path"}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--route-path"))
			})
		})
	})

	When("with a legacy (no applications section) manifest", func() {
		var (
			pathToManifest string // Can be a filepath or a directory with a manifest.
		)

		BeforeEach(func() {
			tmpFile, err := ioutil.TempFile("", "manifest.yml")
			Expect(err).ToNot(HaveOccurred())
			pathToManifest = tmpFile.Name()
			Expect(tmpFile.Close()).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			Expect(os.Remove(pathToManifest)).ToNot(HaveOccurred())
		})

		BeforeEach(func() {
			helpers.WriteManifest(pathToManifest, map[string]interface{}{
				"host": "blatz" + helpers.NewHostName(),
			})
		})
		JustBeforeEach(func() {
			helpers.WithHelloWorldApp(func(dir string) {
				allArgs := []string{PushCommandName, appName, "-f", pathToManifest, "--no-start"}
				allArgs = append(allArgs, localArgs...)
				session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, allArgs...)
				Eventually(session).Should(Exit(0))
			})
		})
		JustAfterEach(func() {
			Expect(session.Err).Should(Say(legacyManifestDeprecationTemplate))
		})

		It("does not output a deprecation warning", func() {
			Expect(session.Err).Should(Not(Say(deprecationTemplate)))
		})

		When("the -d (domains) flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say("howdy"+deprecationTemplate, "-d"))
			})
		})

		When("the --hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--hostname", host}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--hostname"))
			})
		})

		When("the --no-hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--no-hostname", "-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--no-hostname"))
			})
		})

		When("the --route-path flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--route-path", "some-path"}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--route-path"))
			})
		})
	})

})
<commit_msg>Remove temporary test code to force failure<commit_after>package push

import (
	"io/ioutil"
	"os"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("deprecated route command-line flags", func() {

	const deprecationTemplate = "Deprecation warning: Use of the '%[1]s' command-line flag option is deprecated in favor of the 'routes' property in the manifest. Please see https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#routes for usage information. The '%[1]s' command-line flag option will be removed in the future."

	const legacyManifestDeprecationTemplate = `Deprecation warning: Specifying app manifest attributes at the top level is deprecated. Found: host.
Please see https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html#deprecated for alternatives and other app manifest deprecations. This feature will be removed in the future.`

	var (
		appName       string
		host          string
		privateDomain string
		localArgs     []string
		session       *Session
	)

	BeforeEach(func() {
		appName = helpers.NewAppName()
		host = helpers.NewAppName()

		privateDomain = helpers.NewDomainName()
		domain := helpers.NewDomain(organization, privateDomain)
		domain.Create()
	})
	When("with no manifest", func() {
		JustBeforeEach(func() {
			helpers.WithHelloWorldApp(func(dir string) {
				allArgs := []string{PushCommandName, appName, "--no-start"}
				allArgs = append(allArgs, localArgs...)
				session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, allArgs...)
				Eventually(session).Should(Exit(0))
			})
		})

		When("no deprecated flags are provided", func() {
			It("does not output a deprecation warning", func() {
				Expect(string(session.Err.Contents())).ToNot(ContainSubstring("deprecated"))
			})
		})

		When("the -d (domains) flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "-d"))
			})
		})

		When("the --hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--hostname", host}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--hostname"))
			})
		})

		When("the --no-hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--no-hostname", "-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--no-hostname"))
			})
		})

		When("the --route-path flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--route-path", "some-path"}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--route-path"))
			})
		})
	})

	When("with a legacy (no applications section) manifest", func() {
		var (
			pathToManifest string // Can be a filepath or a directory with a manifest.
		)

		BeforeEach(func() {
			tmpFile, err := ioutil.TempFile("", "manifest.yml")
			Expect(err).ToNot(HaveOccurred())
			pathToManifest = tmpFile.Name()
			Expect(tmpFile.Close()).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			Expect(os.Remove(pathToManifest)).ToNot(HaveOccurred())
		})

		BeforeEach(func() {
			helpers.WriteManifest(pathToManifest, map[string]interface{}{
				"host": "blatz" + helpers.NewHostName(),
			})
		})
		JustBeforeEach(func() {
			helpers.WithHelloWorldApp(func(dir string) {
				allArgs := []string{PushCommandName, appName, "-f", pathToManifest, "--no-start"}
				allArgs = append(allArgs, localArgs...)
				session = helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, allArgs...)
				Eventually(session).Should(Exit(0))
			})
		})
		JustAfterEach(func() {
			Expect(session.Err).Should(Say(legacyManifestDeprecationTemplate))
		})

		It("does not output a deprecation warning", func() {
			Expect(session.Err).Should(Not(Say(deprecationTemplate)))
		})

		When("the -d (domains) flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "-d"))
			})
		})

		When("the --hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--hostname", host}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--hostname"))
			})
		})

		When("the --no-hostname flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--no-hostname", "-d", privateDomain}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--no-hostname"))
			})
		})

		When("the --route-path flag is provided", func() {
			BeforeEach(func() {
				localArgs = []string{"--route-path", "some-path"}
			})
			It("outputs a deprecation warning", func() {
				Expect(session.Err).Should(Say(deprecationTemplate, "--route-path"))
			})
		})
	})

})
<|endoftext|>


<commit_before>package sysfs

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

type SysFs interface {
	DirExists(dirPath string) bool
	FileExists(filePath string) bool
	Read(filePath string) (io.ReadCloser, error)
	Write(filePath string) (io.WriteCloser, error)
	DeleteFile(filePath string) error
	DeleteDir(dirPath string) error
	ReadDir(dirPath string) ([]os.FileInfo, error)
}

type sysFs struct{}

var _ SysFs = &sysFs{}

func New() SysFs {
	return &sysFs{}
}

func (fs *sysFs) DeleteDir(dirPath string) error {
	return os.RemoveAll(dirPath)
}

func (fs *sysFs) DirExists(dirPath string) bool {
	stat, err := os.Stat(dirPath)
	if err != nil || !stat.IsDir() {
		return false
	}

	return true
}

func (fs *sysFs) FileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	if err != nil {
		return false
	}

	return true
}

func (fs *sysFs) Read(filePath string) (io.ReadCloser, error) {
	return os.Open(filePath)
}

func (fs *sysFs) Write(filePath string) (io.WriteCloser, error) {
	return os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0666)
}

func (fs *sysFs) DeleteFile(filePath string) error {
	return os.Remove(filePath)
}

func (fs *sysFs) ReadDir(dirPath string) ([]os.FileInfo, error) {
	return ioutil.ReadDir(dirPath)
}

func WriteJson(fs SysFs, filePath string, v interface{}) error {
	w, err := fs.Write(filePath)
	if err != nil {
		return err
	}

	defer w.Close()
	e := json.NewEncoder(w)
	return e.Encode(v)
}

func ReadJson(fs SysFs, filePath string, v interface{}) error {
	r, err := fs.Read(filePath)
	if err != nil {
		return err
	}

	defer r.Close()
	d := json.NewDecoder(r)
	if err := d.Decode(v); err != nil {
		return err
	}

	return nil
}

func ReadAll(fs SysFs, filePath string) ([]byte, error) {
	r, err := fs.Read(filePath)
	if err != nil {
		return nil, err
	}

	defer r.Close()
	return ioutil.ReadAll(r)
}

func ClearDir(fs SysFs, dirPath string) error {
	files, err := fs.ReadDir(dirPath)
	if err != nil {
		return err
	}

	for _, f := range files {
		p := filepath.Join(dirPath, f.Name())
		if f.IsDir() {
			err = fs.DeleteDir(p)
		} else {
			err = fs.DeleteFile(p)
		}

		if err != nil {
			return err
		}
	}

	return nil

}

func CopyFile(fs SysFs, srcPath, dstPath string) (int64, error) {
	src, err := fs.Read(srcPath)
	if err != nil {
		return -1, err
	}

	defer src.Close()
	dst, err := fs.Write(dstPath)
	if err != nil {
		return -1, err
	}

	defer dst.Close()
	return io.Copy(dst, src)
}
<commit_msg>add mkdir fn to sysfs interface<commit_after>package sysfs

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

type SysFs interface {
	DirExists(dirPath string) bool
	FileExists(filePath string) bool
	Read(filePath string) (io.ReadCloser, error)
	Write(filePath string) (io.WriteCloser, error)
	DeleteFile(filePath string) error
	DeleteDir(dirPath string) error
	ReadDir(dirPath string) ([]os.FileInfo, error)
	CreateDir(dirPath string) error
}

type sysFs struct{}

var _ SysFs = &sysFs{}

func New() SysFs {
	return &sysFs{}
}

func (fs *sysFs) DeleteDir(dirPath string) error {
	return os.RemoveAll(dirPath)
}

func (fs *sysFs) DirExists(dirPath string) bool {
	stat, err := os.Stat(dirPath)
	if err != nil || !stat.IsDir() {
		return false
	}

	return true
}

func (fs *sysFs) FileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	if err != nil {
		return false
	}

	return true
}

func (fs *sysFs) Read(filePath string) (io.ReadCloser, error) {
	return os.Open(filePath)
}

func (fs *sysFs) Write(filePath string) (io.WriteCloser, error) {
	return os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0666)
}

func (fs *sysFs) DeleteFile(filePath string) error {
	return os.Remove(filePath)
}

func (fs *sysFs) ReadDir(dirPath string) ([]os.FileInfo, error) {
	return ioutil.ReadDir(dirPath)
}

func (fs *sysFs) CreateDir(dirPath string) error {
	return os.Mkdir(dirPath, 0666)
}

func WriteJson(fs SysFs, filePath string, v interface{}) error {
	w, err := fs.Write(filePath)
	if err != nil {
		return err
	}

	defer w.Close()
	e := json.NewEncoder(w)
	return e.Encode(v)
}

func ReadJson(fs SysFs, filePath string, v interface{}) error {
	r, err := fs.Read(filePath)
	if err != nil {
		return err
	}

	defer r.Close()
	d := json.NewDecoder(r)
	if err := d.Decode(v); err != nil {
		return err
	}

	return nil
}

func ReadAll(fs SysFs, filePath string) ([]byte, error) {
	r, err := fs.Read(filePath)
	if err != nil {
		return nil, err
	}

	defer r.Close()
	return ioutil.ReadAll(r)
}

func ClearDir(fs SysFs, dirPath string) error {
	files, err := fs.ReadDir(dirPath)
	if err != nil {
		return err
	}

	for _, f := range files {
		p := filepath.Join(dirPath, f.Name())
		if f.IsDir() {
			err = fs.DeleteDir(p)
		} else {
			err = fs.DeleteFile(p)
		}

		if err != nil {
			return err
		}
	}

	return nil

}

func CopyFile(fs SysFs, srcPath, dstPath string) (int64, error) {
	src, err := fs.Read(srcPath)
	if err != nil {
		return -1, err
	}

	defer src.Close()
	dst, err := fs.Write(dstPath)
	if err != nil {
		return -1, err
	}

	defer dst.Close()
	return io.Copy(dst, src)
}

func SizeOf(r io.ReadCloser) (int64, error) {
	fr, ok := r.(*os.File)
	if !ok {
		return -1, errors.New("couldn't read size: not a valid file reader")
	}

	fi, err := fr.Stat()
	if err != nil {
		return -1, err
	}

	return fi.Size(), nil
}
<|endoftext|>


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

package vhost

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/fatedier/frp/utils/log"
	frpNet "github.com/fatedier/frp/utils/net"
)

type muxFunc func(frpNet.Conn) (frpNet.Conn, map[string]string, error)
type httpAuthFunc func(frpNet.Conn, string, string, string) (bool, error)
type hostRewriteFunc func(frpNet.Conn, string) (frpNet.Conn, error)

type VhostMuxer struct {
	listener       frpNet.Listener
	timeout        time.Duration
	vhostFunc      muxFunc
	authFunc       httpAuthFunc
	rewriteFunc    hostRewriteFunc
	registryRouter *VhostRouters
	mutex          sync.RWMutex
}

func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
	mux = &VhostMuxer{
		listener:       listener,
		timeout:        timeout,
		vhostFunc:      vhostFunc,
		authFunc:       authFunc,
		rewriteFunc:    rewriteFunc,
		registryRouter: NewVhostRouters(),
	}
	go mux.run()
	return mux, nil
}

type VhostRouteConfig struct {
	Domain      string
	Location    string
	RewriteHost string
	Username    string
	Password    string
}

// listen for a new domain name, if rewriteHost is not empty  and rewriteFunc is not nil
// then rewrite the host header to rewriteHost
func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	_, ok := v.registryRouter.Exist(cfg.Domain, cfg.Location)
	if ok {
		return nil, fmt.Errorf("hostname [%s] location [%s] is already registered", cfg.Domain, cfg.Location)
	}

	l = &Listener{
		name:        cfg.Domain,
		location:    cfg.Location,
		rewriteHost: cfg.RewriteHost,
		userName:    cfg.Username,
		passWord:    cfg.Password,
		mux:         v,
		accept:      make(chan frpNet.Conn),
		Logger:      log.NewPrefixLogger(""),
	}
	v.registryRouter.Add(cfg.Domain, cfg.Location, l)
	return l, nil
}

func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	// first we check the full hostname
	// if not exist, then check the wildcard_domain such as *.example.com
	vr, found := v.registryRouter.Get(name, path)
	if found {
		return vr.listener, true
	}

	domainSplit := strings.Split(name, ".")
	if len(domainSplit) < 3 {
		return l, false
	}
	domainSplit[0] = "*"
	name = strings.Join(domainSplit, ".")

	vr, found = v.registryRouter.Get(name, path)
	if !found {
		return
	}

	return vr.listener, true
}

func (v *VhostMuxer) run() {
	for {
		conn, err := v.listener.Accept()
		if err != nil {
			return
		}
		go v.handle(conn)
	}
}

func (v *VhostMuxer) handle(c frpNet.Conn) {
	if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
		c.Close()
		return
	}

	sConn, reqInfoMap, err := v.vhostFunc(c)
	if err != nil {
		log.Warn("get hostname from http/https request error: %v", err)
		c.Close()
		return
	}

	name := strings.ToLower(reqInfoMap["Host"])
	path := strings.ToLower(reqInfoMap["Path"])
	l, ok := v.getListener(name, path)
	if !ok {
		res := notFoundResponse()
		res.Write(c)
		log.Debug("http request for host [%s] path [%s] not found", name, path)
		c.Close()
		return
	}

	// if authFunc is exist and userName/password is set
	// then verify user access
	if l.mux.authFunc != nil && l.userName != "" && l.passWord != "" {
		bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"])
		if bAccess == false || err != nil {
			l.Debug("check http Authorization failed")
			res := noAuthResponse()
			res.Write(c)
			c.Close()
			return
		}
	}

	if err = sConn.SetDeadline(time.Time{}); err != nil {
		c.Close()
		return
	}
	c = sConn

	l.Debug("get new http request host [%s] path [%s]", name, path)
	l.accept <- c
}

type Listener struct {
	name        string
	location    string
	rewriteHost string
	userName    string
	passWord    string
	mux         *VhostMuxer // for closing VhostMuxer
	accept      chan frpNet.Conn
	log.Logger
}

func (l *Listener) Accept() (frpNet.Conn, error) {
	conn, ok := <-l.accept
	if !ok {
		return nil, fmt.Errorf("Listener closed")
	}

	// if rewriteFunc is exist
	// rewrite http requests with a modified host header
	// if l.rewriteHost is empty, nothing to do
	if l.mux.rewriteFunc != nil {
		sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
		if err != nil {
			l.Warn("host header rewrite failed: %v", err)
			return nil, fmt.Errorf("host header rewrite failed")
		}
		l.Debug("rewrite host to [%s] success", l.rewriteHost)
		conn = sConn
	}

	for _, prefix := range l.GetAllPrefix() {
		conn.AddLogPrefix(prefix)
	}
	return conn, nil
}

func (l *Listener) Close() error {
	l.mux.registryRouter.Del(l.name, l.location)
	close(l.accept)
	return nil
}

func (l *Listener) Name() string {
	return l.name
}
<commit_msg>prevent sending on a closed channel in vhost package, fix #502<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vhost

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/fatedier/frp/utils/errors"
	"github.com/fatedier/frp/utils/log"
	frpNet "github.com/fatedier/frp/utils/net"
)

type muxFunc func(frpNet.Conn) (frpNet.Conn, map[string]string, error)
type httpAuthFunc func(frpNet.Conn, string, string, string) (bool, error)
type hostRewriteFunc func(frpNet.Conn, string) (frpNet.Conn, error)

type VhostMuxer struct {
	listener       frpNet.Listener
	timeout        time.Duration
	vhostFunc      muxFunc
	authFunc       httpAuthFunc
	rewriteFunc    hostRewriteFunc
	registryRouter *VhostRouters
	mutex          sync.RWMutex
}

func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
	mux = &VhostMuxer{
		listener:       listener,
		timeout:        timeout,
		vhostFunc:      vhostFunc,
		authFunc:       authFunc,
		rewriteFunc:    rewriteFunc,
		registryRouter: NewVhostRouters(),
	}
	go mux.run()
	return mux, nil
}

type VhostRouteConfig struct {
	Domain      string
	Location    string
	RewriteHost string
	Username    string
	Password    string
}

// listen for a new domain name, if rewriteHost is not empty  and rewriteFunc is not nil
// then rewrite the host header to rewriteHost
func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
	v.mutex.Lock()
	defer v.mutex.Unlock()

	_, ok := v.registryRouter.Exist(cfg.Domain, cfg.Location)
	if ok {
		return nil, fmt.Errorf("hostname [%s] location [%s] is already registered", cfg.Domain, cfg.Location)
	}

	l = &Listener{
		name:        cfg.Domain,
		location:    cfg.Location,
		rewriteHost: cfg.RewriteHost,
		userName:    cfg.Username,
		passWord:    cfg.Password,
		mux:         v,
		accept:      make(chan frpNet.Conn),
		Logger:      log.NewPrefixLogger(""),
	}
	v.registryRouter.Add(cfg.Domain, cfg.Location, l)
	return l, nil
}

func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()

	// first we check the full hostname
	// if not exist, then check the wildcard_domain such as *.example.com
	vr, found := v.registryRouter.Get(name, path)
	if found {
		return vr.listener, true
	}

	domainSplit := strings.Split(name, ".")
	if len(domainSplit) < 3 {
		return l, false
	}
	domainSplit[0] = "*"
	name = strings.Join(domainSplit, ".")

	vr, found = v.registryRouter.Get(name, path)
	if !found {
		return
	}

	return vr.listener, true
}

func (v *VhostMuxer) run() {
	for {
		conn, err := v.listener.Accept()
		if err != nil {
			return
		}
		go v.handle(conn)
	}
}

func (v *VhostMuxer) handle(c frpNet.Conn) {
	if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
		c.Close()
		return
	}

	sConn, reqInfoMap, err := v.vhostFunc(c)
	if err != nil {
		log.Warn("get hostname from http/https request error: %v", err)
		c.Close()
		return
	}

	name := strings.ToLower(reqInfoMap["Host"])
	path := strings.ToLower(reqInfoMap["Path"])
	l, ok := v.getListener(name, path)
	if !ok {
		res := notFoundResponse()
		res.Write(c)
		log.Debug("http request for host [%s] path [%s] not found", name, path)
		c.Close()
		return
	}

	// if authFunc is exist and userName/password is set
	// then verify user access
	if l.mux.authFunc != nil && l.userName != "" && l.passWord != "" {
		bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"])
		if bAccess == false || err != nil {
			l.Debug("check http Authorization failed")
			res := noAuthResponse()
			res.Write(c)
			c.Close()
			return
		}
	}

	if err = sConn.SetDeadline(time.Time{}); err != nil {
		c.Close()
		return
	}
	c = sConn

	l.Debug("get new http request host [%s] path [%s]", name, path)
	err = errors.PanicToError(func() {
		l.accept <- c
	})
	if err != nil {
		l.Warn("listener is already closed, ignore this request")
	}
}

type Listener struct {
	name        string
	location    string
	rewriteHost string
	userName    string
	passWord    string
	mux         *VhostMuxer // for closing VhostMuxer
	accept      chan frpNet.Conn
	log.Logger
}

func (l *Listener) Accept() (frpNet.Conn, error) {
	conn, ok := <-l.accept
	if !ok {
		return nil, fmt.Errorf("Listener closed")
	}

	// if rewriteFunc is exist
	// rewrite http requests with a modified host header
	// if l.rewriteHost is empty, nothing to do
	if l.mux.rewriteFunc != nil {
		sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
		if err != nil {
			l.Warn("host header rewrite failed: %v", err)
			return nil, fmt.Errorf("host header rewrite failed")
		}
		l.Debug("rewrite host to [%s] success", l.rewriteHost)
		conn = sConn
	}

	for _, prefix := range l.GetAllPrefix() {
		conn.AddLogPrefix(prefix)
	}
	return conn, nil
}

func (l *Listener) Close() error {
	l.mux.registryRouter.Del(l.name, l.location)
	close(l.accept)
	return nil
}

func (l *Listener) Name() string {
	return l.name
}
<|endoftext|>


<commit_before>/*

	Very simple program for automatically spinning up a h.264 stream
	when requested on port 10001. Kills the stream when the client
	disconnects, saving CPU time / power.

	Alter the values in the Command to suit. The location of the
	command is probably Arch specific, might need changing on
	another distro. To compile on Mac or Linux:

	GOOS=linux GOARM=6 GOARCH=arm go build <filename_of_this_file>

	Then copy the file to the Raspberry Pi. When you want to stream
	video run:

	nc <IP address of pi> 10001 | mplayer -fs -fps 200 -demuxer h264es -fs -

*/

package v_stream

import (
	"io"
	"log"
	"net"
	"os/exec"
	"strconv"
	"syscall"
)

var read_chunk int64 = 500

func ServeVideo(x_res, y_res, bitrate int) {
	log.Println("Starting video stream server")

	tcp_addr, err := net.ResolveTCPAddr("tcp4", ":10001")
	if err != nil {
		log.Fatal("Couldn't resolve tcp address, ", err)
	}

	ln, err := net.ListenTCP("tcp4", tcp_addr)

	// ln, err := net.Listen("tcp", ":10001")
	// if err != nil {
	// 	log.Fatal(err)
	// }

	for {
		conn, err := ln.AcceptTCP()
		if err != nil {
			log.Fatal("Error accepting connection, ", err)
		}

		conn.SetNoDelay(true)
		conn.SetWriteBuffer(10e7)

		cmd := exec.Command("/opt/vc/bin/raspivid", "-g", "10", "-n", "-w", strconv.Itoa(x_res), "-h", strconv.Itoa(y_res), "-b", strconv.Itoa(bitrate), "-fps", "30", "-t", "0", "-o", "-")
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal("raspivid failed to start: ", err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		written, err := io.Copy(conn, stdout)
		if err != nil {
			log.Println("User disconnected, ", err)
		}

		log.Printf("Wrote %v bytes", written)
		conn.Close()
		log.Printf("Closed remote connection")

		if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
			log.Fatal(err)
		}

		cmd.Process.Wait()
	}
}
<commit_msg>Make fps an option, dump out raspivid command<commit_after>/*

	Very simple program for automatically spinning up a h.264 stream
	when requested on port 10001. Kills the stream when the client
	disconnects, saving CPU time / power.

	Alter the values in the Command to suit. The location of the
	command is probably Arch specific, might need changing on
	another distro. To compile on Mac or Linux:

	GOOS=linux GOARM=6 GOARCH=arm go build <filename_of_this_file>

	Then copy the file to the Raspberry Pi. When you want to stream
	video run:

	nc <IP address of pi> 10001 | mplayer -fs -fps 200 -demuxer h264es -fs -

*/

package v_stream

import (
	"io"
	"log"
	"net"
	"os/exec"
	"strconv"
	"syscall"
)

var read_chunk int64 = 500

func ServeVideo(x_res, y_res, bitrate, fps int) {
	log.Println("Starting video stream server")

	tcp_addr, err := net.ResolveTCPAddr("tcp4", ":10001")
	if err != nil {
		log.Fatal("Couldn't resolve tcp address, ", err)
	}

	ln, err := net.ListenTCP("tcp4", tcp_addr)

	// ln, err := net.Listen("tcp", ":10001")
	// if err != nil {
	// 	log.Fatal(err)
	// }

	for {
		conn, err := ln.AcceptTCP()
		if err != nil {
			log.Fatal("Error accepting connection, ", err)
		}

		conn.SetNoDelay(true)
		conn.SetWriteBuffer(10e7)

		cmd := exec.Command("/opt/vc/bin/raspivid", "-g", "10", "-n", "-w", strconv.Itoa(x_res), "-h", strconv.Itoa(y_res), "-b", strconv.Itoa(bitrate), "-fps", strconv.Itoa(fps), "-t", "0", "-o", "-")
		log.Println("Streaming video with command: ", cmd)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal("raspivid failed to start: ", err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		written, err := io.Copy(conn, stdout)
		if err != nil {
			log.Println("User disconnected, ", err)
		}

		log.Printf("Wrote %v bytes", written)
		conn.Close()
		log.Printf("Closed remote connection")

		if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
			log.Fatal(err)
		}

		cmd.Process.Wait()
	}
}
<|endoftext|>


<commit_before>// nl: place line numbers at the beginning of lines
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
)

func main() {
	i := flag.Int("i", 1, "line-increment")
	s := flag.String("s", "\t", "number seperator")

	flag.Parse()

	args := flag.Args()

	var lineNum = 1
	for _, file := range args {

		f, err := os.Open(file)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		}

		r := bufio.NewReader(f)
		for {
			var lastLine = false
			line, err := r.ReadString('\n')
			if err == io.EOF {
				if len(line) == 0 {
					break
				}
				lastLine = true
			} else if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				break
			}

			isBlank := len(strings.TrimSpace(line)) > 0

			// Only print line number if it's a non-blank line
			if isBlank {
				fmt.Printf("%6d%s", lineNum, *s)
			}

			fmt.Printf("%s", line)

			// Only iterate line number if it's a non-blank line
			if isBlank {
				lineNum += *i
			}

			if lastLine {
				break
			}
		}
	}
}
<commit_msg>Update nl.go to use log.Fatal over Fprintf(os.Stderr).<commit_after>// nl: place line numbers at the beginning of lines
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)

func main() {
	i := flag.Int("i", 1, "line-increment")
	s := flag.String("s", "\t", "number seperator")

	flag.Parse()

	args := flag.Args()

	var lineNum = 1
	for _, file := range args {

		f, err := os.Open(file)
		if err != nil {
			log.Fatalln("Error: %v\n", err)
		}

		r := bufio.NewReader(f)
		for {
			var lastLine = false
			line, err := r.ReadString('\n')
			if err == io.EOF {
				if len(line) == 0 {
					break
				}
				lastLine = true
			} else if err != nil {
				log.Fatalln("Error: %v\n", err)
			}

			isBlank := len(strings.TrimSpace(line)) == 0

			// Only print line number if it's a non-blank line
			if !isBlank {
				fmt.Printf("%6d%s", lineNum, *s)
			}

			fmt.Printf("%s", line)

			// Only iterate line number if it's a non-blank line
			if !isBlank {
				lineNum += *i
			}

			if lastLine {
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>/* Copyright 2018 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 main

import (
	"flag"
	"fmt"
	"github.com/knative/test-infra/tools/coverage/artifacts"
	"github.com/knative/test-infra/tools/coverage/gcs"
	"github.com/knative/test-infra/tools/coverage/githubUtil/githubPr"
	"github.com/knative/test-infra/tools/coverage/logUtil"
	"github.com/knative/test-infra/tools/coverage/testgrid"
	"log"
	"os"
	"strconv"
)

const (
	keyCovProfileFileName    = "key-cov-prof.txt"
	defaultStdoutRedirect    = "stdout.txt"
	defaultCoverageTargetDir = "./pkg/"
	defaultGcsBucket         = "knative-prow"
	defaultPostSubmitJobName = "post-knative-serving-go-coverage"
	defaultCovThreshold      = 80
)

var (
	artifactsDir        = "./artifacts/"
	coverageProfileName = "coverage_profile.txt"
)

func main() {
	fmt.Println("entering code coverage main")

	gcsBucketName := flag.String("postsubmit-gcs-bucket", defaultGcsBucket, "gcs bucket name")
	postSubmitJobName := flag.String("postsubmit-job-name", defaultPostSubmitJobName, "name of the prow job")
	artifactsDirFlag := flag.String("artifacts", artifactsDir, "directory for artifacts")
	coverageTargetDir := flag.String("cov-target", defaultCoverageTargetDir, "target directory for test coverage")
	flag.StringVar(&coverageProfileName, "profile-name", coverageProfileName, "file name for coverage profile")
	githubTokenPath := flag.String("github-token", "", "path to token to access github repo")
	covThresholdFlag := flag.Int("cov-threshold-percentage", defaultCovThreshold, "token to access github repo")
	covbotUserName := flag.String("covbot-username", "covbot", "github user name for coverage robot")
	flag.Parse()

	log.Printf("container flag list: postsubmit-gcs-bucket=%s; postSubmitJobName=%s; "+
		"artifacts=%s; cov-target=%s; profile-name=%s; github-token=%s; "+
		"cov-threshold-percentage=%d; covbot-username=%s;",
		*gcsBucketName, *postSubmitJobName, *artifactsDirFlag, *coverageTargetDir, coverageProfileName,
		*githubTokenPath, *covThresholdFlag, *covbotUserName)

	log.Println("Getting env values")
	pr := os.Getenv("PULL_NUMBER")
	pullSha := os.Getenv("PULL_PULL_SHA")
	baseSha := os.Getenv("PULL_BASE_SHA")
	repoOwner := os.Getenv("REPO_OWNER")
	repoName := os.Getenv("REPO_NAME")
	jobType := os.Getenv("JOB_TYPE")
	jobName := os.Getenv("JOB_NAME")

	fmt.Printf("Running coverage for PR %s with PR commit SHA %s and base SHA %s", pr, pullSha, baseSha)

	artifactsDir = *artifactsDirFlag

	localArtifacts := artifacts.NewLocalArtifacts(
		artifactsDir,
		coverageProfileName,
		keyCovProfileFileName,
		defaultStdoutRedirect,
	)

	localArtifacts.ProduceProfileFile(*coverageTargetDir)

	switch jobType {
	case "presubmit":
		buildStr := os.Getenv("BUILD_NUMBER")
		build, err := strconv.Atoi(buildStr)
		if err != nil {
			logUtil.LogFatalf("BUILD_NUMBER(%s) cannot be converted to int, err=%v",
				buildStr, err)
		}

		prData := githubPr.New(*githubTokenPath, repoOwner, repoName, pr, *covbotUserName)
		gcsData := &gcs.PresubmitBuild{GcsBuild: gcs.GcsBuild{
			StorageClient: gcs.NewStorageClient(prData.Ctx),
			Bucket:        *gcsBucketName,
			Job:           jobName,
			Build:         build,
			CovThreshold:  *covThresholdFlag,
		},
			PostSubmitJob: *postSubmitJobName,
		}
		presubmit := &gcs.PreSubmit{
			GithubPr:       *prData,
			PresubmitBuild: *gcsData,
		}

		presubmit.Artifacts = *presubmit.MakeGcsArtifacts(*localArtifacts)

		isCoverageLow := RunPresubmit(presubmit, localArtifacts)

		if isCoverageLow {
			logUtil.LogFatalf("Code coverage is below threshold (%d%%), "+
				"fail presubmit workflow intentionally", *covThresholdFlag)
		}
	case "periodic":
		log.Printf("job type is %v, producing testsuite xml...\n", jobType)
		testgrid.ProfileToTestsuiteXML(localArtifacts, *covThresholdFlag)
	}

	fmt.Println("end of code coverage main")
}
<commit_msg>remove post-knative-serving-go-coverage as defaultPostSubmitJobName (#644)<commit_after>/* Copyright 2018 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 main

import (
	"flag"
	"fmt"
	"github.com/knative/test-infra/tools/coverage/artifacts"
	"github.com/knative/test-infra/tools/coverage/gcs"
	"github.com/knative/test-infra/tools/coverage/githubUtil/githubPr"
	"github.com/knative/test-infra/tools/coverage/logUtil"
	"github.com/knative/test-infra/tools/coverage/testgrid"
	"log"
	"os"
	"strconv"
)

const (
	keyCovProfileFileName    = "key-cov-prof.txt"
	defaultStdoutRedirect    = "stdout.txt"
	defaultCoverageTargetDir = "./pkg/"
	defaultGcsBucket         = "knative-prow"
	defaultPostSubmitJobName = ""
	defaultCovThreshold      = 80
)

var (
	artifactsDir        = "./artifacts/"
	coverageProfileName = "coverage_profile.txt"
)

func main() {
	fmt.Println("entering code coverage main")

	gcsBucketName := flag.String("postsubmit-gcs-bucket", defaultGcsBucket, "gcs bucket name")
	postSubmitJobName := flag.String("postsubmit-job-name", defaultPostSubmitJobName, "name of the prow job")
	artifactsDirFlag := flag.String("artifacts", artifactsDir, "directory for artifacts")
	coverageTargetDir := flag.String("cov-target", defaultCoverageTargetDir, "target directory for test coverage")
	flag.StringVar(&coverageProfileName, "profile-name", coverageProfileName, "file name for coverage profile")
	githubTokenPath := flag.String("github-token", "", "path to token to access github repo")
	covThresholdFlag := flag.Int("cov-threshold-percentage", defaultCovThreshold, "token to access github repo")
	covbotUserName := flag.String("covbot-username", "covbot", "github user name for coverage robot")
	flag.Parse()

	log.Printf("container flag list: postsubmit-gcs-bucket=%s; postSubmitJobName=%s; "+
		"artifacts=%s; cov-target=%s; profile-name=%s; github-token=%s; "+
		"cov-threshold-percentage=%d; covbot-username=%s;",
		*gcsBucketName, *postSubmitJobName, *artifactsDirFlag, *coverageTargetDir, coverageProfileName,
		*githubTokenPath, *covThresholdFlag, *covbotUserName)

	log.Println("Getting env values")
	pr := os.Getenv("PULL_NUMBER")
	pullSha := os.Getenv("PULL_PULL_SHA")
	baseSha := os.Getenv("PULL_BASE_SHA")
	repoOwner := os.Getenv("REPO_OWNER")
	repoName := os.Getenv("REPO_NAME")
	jobType := os.Getenv("JOB_TYPE")
	jobName := os.Getenv("JOB_NAME")

	fmt.Printf("Running coverage for PR %s with PR commit SHA %s and base SHA %s", pr, pullSha, baseSha)

	artifactsDir = *artifactsDirFlag

	localArtifacts := artifacts.NewLocalArtifacts(
		artifactsDir,
		coverageProfileName,
		keyCovProfileFileName,
		defaultStdoutRedirect,
	)

	localArtifacts.ProduceProfileFile(*coverageTargetDir)

	switch jobType {
	case "presubmit":
		buildStr := os.Getenv("BUILD_NUMBER")
		build, err := strconv.Atoi(buildStr)
		if err != nil {
			logUtil.LogFatalf("BUILD_NUMBER(%s) cannot be converted to int, err=%v",
				buildStr, err)
		}

		prData := githubPr.New(*githubTokenPath, repoOwner, repoName, pr, *covbotUserName)
		gcsData := &gcs.PresubmitBuild{GcsBuild: gcs.GcsBuild{
			StorageClient: gcs.NewStorageClient(prData.Ctx),
			Bucket:        *gcsBucketName,
			Job:           jobName,
			Build:         build,
			CovThreshold:  *covThresholdFlag,
		},
			PostSubmitJob: *postSubmitJobName,
		}
		presubmit := &gcs.PreSubmit{
			GithubPr:       *prData,
			PresubmitBuild: *gcsData,
		}

		presubmit.Artifacts = *presubmit.MakeGcsArtifacts(*localArtifacts)

		isCoverageLow := RunPresubmit(presubmit, localArtifacts)

		if isCoverageLow {
			logUtil.LogFatalf("Code coverage is below threshold (%d%%), "+
				"fail presubmit workflow intentionally", *covThresholdFlag)
		}
	case "periodic":
		log.Printf("job type is %v, producing testsuite xml...\n", jobType)
		testgrid.ProfileToTestsuiteXML(localArtifacts, *covThresholdFlag)
	}

	fmt.Println("end of code coverage main")
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/agent/mongo"
	coreCloudinit "launchpad.net/juju-core/cloudinit"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/container"
	"launchpad.net/juju-core/container/lxc"
	containertesting "launchpad.net/juju-core/container/testing"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/jujutest"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/juju/arch"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/provider/local"
	"launchpad.net/juju-core/state/api/params"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/upstart"
)

const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" >> $0.args"

type environSuite struct {
	baseProviderSuite
	envtesting.ToolsFixture
}

var _ = gc.Suite(&environSuite{})

func (s *environSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	s.ToolsFixture.SetUpTest(c)
}

func (s *environSuite) TearDownTest(c *gc.C) {
	s.ToolsFixture.TearDownTest(c)
	s.baseProviderSuite.TearDownTest(c)
}

func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) {
	testConfig := minimalConfig(c)
	testConfig, err := testConfig.Apply(map[string]interface{}{
		"root-dir": "/usr/lib/juju",
	})
	c.Assert(err, gc.IsNil)

	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.ErrorMatches, "mkdir .* permission denied")
	c.Assert(environ, gc.IsNil)
}

func (s *environSuite) TestNameAndStorage(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	c.Assert(environ.Name(), gc.Equals, "test")
	c.Assert(environ.Storage(), gc.NotNil)
}

func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	sources, err := tools.GetMetadataSources(environ)
	c.Assert(err, gc.IsNil)
	c.Assert(len(sources), gc.Equals, 1)
	url, err := sources[0].URL("")
	c.Assert(err, gc.IsNil)
	c.Assert(strings.Contains(url, "/tools"), jc.IsTrue)
}

func (*environSuite) TestSupportedArchitectures(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	arches, err := environ.SupportedArchitectures()
	c.Assert(err, gc.IsNil)
	for _, a := range arches {
		c.Assert(arch.IsSupportedArch(a), jc.IsTrue)
	}
}

func (*environSuite) TestSupportNetworks(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	c.Assert(environ.SupportNetworks(), jc.IsFalse)
}

type localJujuTestSuite struct {
	baseProviderSuite
	jujutest.Tests
	oldUpstartLocation string
	testPath           string
	fakesudo           string
}

func (s *localJujuTestSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	// Construct the directories first.
	err := local.CreateDirs(c, minimalConfig(c))
	c.Assert(err, gc.IsNil)
	s.testPath = c.MkDir()
	s.fakesudo = filepath.Join(s.testPath, "sudo")
	s.PatchEnvPathPrepend(s.testPath)

	// Write a fake "sudo" which records its args to sudo.args.
	err = ioutil.WriteFile(s.fakesudo, []byte(echoCommandScript), 0755)
	c.Assert(err, gc.IsNil)

	// Add in an admin secret
	s.Tests.TestConfig["admin-secret"] = "sekrit"
	s.PatchValue(local.CheckIfRoot, func() bool { return false })
	s.Tests.SetUpTest(c)

	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
		return nil
	})
}

func (s *localJujuTestSuite) TearDownTest(c *gc.C) {
	s.Tests.TearDownTest(c)
	s.baseProviderSuite.TearDownTest(c)
}

func (s *localJujuTestSuite) MakeTool(c *gc.C, name, script string) {
	path := filepath.Join(s.testPath, name)
	script = "#!/bin/bash\n" + script
	err := ioutil.WriteFile(path, []byte(script), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *localJujuTestSuite) StoppedStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
}

func (s *localJujuTestSuite) RunningStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
}

var _ = gc.Suite(&localJujuTestSuite{
	Tests: jujutest.Tests{
		TestConfig: minimalConfigValues(),
	},
})

func (s *localJujuTestSuite) TestStartStop(c *gc.C) {
	c.Skip("StartInstance not implemented yet.")
}

func (s *localJujuTestSuite) testBootstrap(c *gc.C, cfg *config.Config) (env environs.Environ) {
	ctx := coretesting.Context(c)
	environ, err := local.Provider.Prepare(ctx, cfg)
	c.Assert(err, gc.IsNil)
	envtesting.UploadFakeTools(c, environ.Storage())
	defer environ.Storage().RemoveAll()
	err = environ.Bootstrap(ctx, environs.BootstrapParams{})
	c.Assert(err, gc.IsNil)
	return environ
}

func (s *localJujuTestSuite) TestBootstrap(c *gc.C) {
	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
		c.Assert(cloudcfg.AptUpdate(), jc.IsFalse)
		c.Assert(cloudcfg.AptUpgrade(), jc.IsFalse)
		c.Assert(cloudcfg.Packages(), gc.HasLen, 0)
		c.Assert(mcfg.AgentEnvironment, gc.Not(gc.IsNil))
		// local does not allow machine-0 to host units
		c.Assert(mcfg.Jobs, gc.DeepEquals, []params.MachineJob{params.JobManageEnviron})
		return nil
	})
	s.testBootstrap(c, minimalConfig(c))
}

func (s *localJujuTestSuite) TestDestroy(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	err := env.Destroy()
	// Succeeds because there's no "agents" directory,
	// so destroy will just return without attempting
	// sudo or anything.
	c.Assert(err, gc.IsNil)
	c.Assert(s.fakesudo+".args", jc.DoesNotExist)
}

func (s *localJujuTestSuite) makeAgentsDir(c *gc.C, env environs.Environ) {
	rootDir := env.Config().AllAttrs()["root-dir"].(string)
	agentsDir := filepath.Join(rootDir, "agents")
	err := os.Mkdir(agentsDir, 0755)
	c.Assert(err, gc.IsNil)
}

func (s *localJujuTestSuite) TestDestroyCallSudo(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	err := env.Destroy()
	c.Assert(err, gc.IsNil)
	data, err := ioutil.ReadFile(s.fakesudo + ".args")
	c.Assert(err, gc.IsNil)
	expected := []string{
		s.fakesudo,
		"env",
		"JUJU_HOME=" + osenv.JujuHome(),
		os.Args[0],
		"destroy-environment",
		"-y",
		"--force",
		env.Config().Name(),
	}
	c.Assert(string(data), gc.Equals, strings.Join(expected, " ")+"\n")
}

func (s *localJujuTestSuite) makeFakeUpstartScripts(c *gc.C, env environs.Environ,
) (mongoService *upstart.Service, machineAgent *upstart.Service) {
	upstartDir := c.MkDir()
	s.PatchValue(&upstart.InitDir, upstartDir)
	s.MakeTool(c, "start", `echo "some-service start/running, process 123"`)

	namespace := env.Config().AllAttrs()["namespace"].(string)
	mongoService = upstart.NewService(mongo.ServiceName(namespace))
	mongoConf := upstart.Conf{
		Service: *mongoService,
		Desc:    "fake mongo",
		Cmd:     "echo FAKE",
	}
	err := mongoConf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(mongoService.Installed(), jc.IsTrue)

	machineAgent = upstart.NewService(fmt.Sprintf("juju-agent-%s", namespace))
	agentConf := upstart.Conf{
		Service: *machineAgent,
		Desc:    "fake agent",
		Cmd:     "echo FAKE",
	}
	err = agentConf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(machineAgent.Installed(), jc.IsTrue)

	return mongoService, machineAgent
}

func (s *localJujuTestSuite) TestDestroyRemovesUpstartServices(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	mongo, machineAgent := s.makeFakeUpstartScripts(c, env)
	s.PatchValue(local.CheckIfRoot, func() bool { return true })

	err := env.Destroy()
	c.Assert(err, gc.IsNil)

	c.Assert(mongo.Installed(), jc.IsFalse)
	c.Assert(machineAgent.Installed(), jc.IsFalse)
}

func (s *localJujuTestSuite) TestDestroyRemovesContainers(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	s.PatchValue(local.CheckIfRoot, func() bool { return true })

	namespace := env.Config().AllAttrs()["namespace"].(string)
	manager, err := lxc.NewContainerManager(container.ManagerConfig{
		container.ConfigName:   namespace,
		container.ConfigLogDir: "logdir",
	})
	c.Assert(err, gc.IsNil)

	machine1 := containertesting.CreateContainer(c, manager, "1")

	err = env.Destroy()
	c.Assert(err, gc.IsNil)

	container := s.Factory.New(string(machine1.Id()))
	c.Assert(container.IsConstructed(), jc.IsFalse)
}

func (s *localJujuTestSuite) TestBootstrapRemoveLeftovers(c *gc.C) {
	cfg := minimalConfig(c)
	rootDir := cfg.AllAttrs()["root-dir"].(string)

	// Create a dir inside local/log that should be removed by Bootstrap.
	logThings := filepath.Join(rootDir, "log", "things")
	err := os.MkdirAll(logThings, 0755)
	c.Assert(err, gc.IsNil)

	// Create a cloud-init-output.log in root-dir that should be
	// removed/truncated by Bootstrap.
	cloudInitOutputLog := filepath.Join(rootDir, "cloud-init-output.log")
	err = ioutil.WriteFile(cloudInitOutputLog, []byte("ohai"), 0644)
	c.Assert(err, gc.IsNil)

	s.testBootstrap(c, cfg)
	c.Assert(logThings, jc.DoesNotExist)
	c.Assert(cloudInitOutputLog, jc.DoesNotExist)
	c.Assert(filepath.Join(rootDir, "log"), jc.IsSymlink)
}

func (s *localJujuTestSuite) TestConstraintsValidator(c *gc.C) {
	ctx := coretesting.Context(c)
	env, err := local.Provider.Prepare(ctx, minimalConfig(c))
	c.Assert(err, gc.IsNil)
	validator, err := env.ConstraintsValidator()
	c.Assert(err, gc.IsNil)
	hostArch := arch.HostArch()
	cons := constraints.MustParse(fmt.Sprintf("arch=%s instance-type=foo tags=bar cpu-power=10 cpu-cores=2", hostArch))
	unsupported, err := validator.Validate(cons)
	c.Assert(err, gc.IsNil)
	c.Assert(unsupported, gc.DeepEquals, []string{"cpu-cores", "cpu-power", "instance-type", "tags"})
}

func (s *localJujuTestSuite) TestConstraintsValidatorVocab(c *gc.C) {
	env := s.Prepare(c)
	validator, err := env.ConstraintsValidator()
	c.Assert(err, gc.IsNil)

	hostArch := arch.HostArch()
	var invalidArch string
	for _, a := range arch.AllSupportedArches {
		if a != hostArch {
			invalidArch = a
			break
		}
	}
	cons := constraints.MustParse(fmt.Sprintf("arch=%s", invalidArch))
	_, err = validator.Validate(cons)
	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch="+invalidArch+"\nvalid values are:.*")
}
<commit_msg>fix test to check new error message<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/agent/mongo"
	coreCloudinit "launchpad.net/juju-core/cloudinit"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/container"
	"launchpad.net/juju-core/container/lxc"
	containertesting "launchpad.net/juju-core/container/testing"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/cloudinit"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/jujutest"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/juju/arch"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/provider/local"
	"launchpad.net/juju-core/state/api/params"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/upstart"
)

const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" >> $0.args"

type environSuite struct {
	baseProviderSuite
	envtesting.ToolsFixture
}

var _ = gc.Suite(&environSuite{})

func (s *environSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	s.ToolsFixture.SetUpTest(c)
}

func (s *environSuite) TearDownTest(c *gc.C) {
	s.ToolsFixture.TearDownTest(c)
	s.baseProviderSuite.TearDownTest(c)
}

func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) {
	testConfig := minimalConfig(c)
	testConfig, err := testConfig.Apply(map[string]interface{}{
		"root-dir": "/usr/lib/juju",
	})
	c.Assert(err, gc.IsNil)

	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.ErrorMatches, "failure setting config: mkdir .* permission denied")
	c.Assert(environ, gc.IsNil)
}

func (s *environSuite) TestNameAndStorage(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	c.Assert(environ.Name(), gc.Equals, "test")
	c.Assert(environ.Storage(), gc.NotNil)
}

func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	sources, err := tools.GetMetadataSources(environ)
	c.Assert(err, gc.IsNil)
	c.Assert(len(sources), gc.Equals, 1)
	url, err := sources[0].URL("")
	c.Assert(err, gc.IsNil)
	c.Assert(strings.Contains(url, "/tools"), jc.IsTrue)
}

func (*environSuite) TestSupportedArchitectures(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	arches, err := environ.SupportedArchitectures()
	c.Assert(err, gc.IsNil)
	for _, a := range arches {
		c.Assert(arch.IsSupportedArch(a), jc.IsTrue)
	}
}

func (*environSuite) TestSupportNetworks(c *gc.C) {
	testConfig := minimalConfig(c)
	environ, err := local.Provider.Open(testConfig)
	c.Assert(err, gc.IsNil)
	c.Assert(environ.SupportNetworks(), jc.IsFalse)
}

type localJujuTestSuite struct {
	baseProviderSuite
	jujutest.Tests
	oldUpstartLocation string
	testPath           string
	fakesudo           string
}

func (s *localJujuTestSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	// Construct the directories first.
	err := local.CreateDirs(c, minimalConfig(c))
	c.Assert(err, gc.IsNil)
	s.testPath = c.MkDir()
	s.fakesudo = filepath.Join(s.testPath, "sudo")
	s.PatchEnvPathPrepend(s.testPath)

	// Write a fake "sudo" which records its args to sudo.args.
	err = ioutil.WriteFile(s.fakesudo, []byte(echoCommandScript), 0755)
	c.Assert(err, gc.IsNil)

	// Add in an admin secret
	s.Tests.TestConfig["admin-secret"] = "sekrit"
	s.PatchValue(local.CheckIfRoot, func() bool { return false })
	s.Tests.SetUpTest(c)

	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
		return nil
	})
}

func (s *localJujuTestSuite) TearDownTest(c *gc.C) {
	s.Tests.TearDownTest(c)
	s.baseProviderSuite.TearDownTest(c)
}

func (s *localJujuTestSuite) MakeTool(c *gc.C, name, script string) {
	path := filepath.Join(s.testPath, name)
	script = "#!/bin/bash\n" + script
	err := ioutil.WriteFile(path, []byte(script), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *localJujuTestSuite) StoppedStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
}

func (s *localJujuTestSuite) RunningStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
}

var _ = gc.Suite(&localJujuTestSuite{
	Tests: jujutest.Tests{
		TestConfig: minimalConfigValues(),
	},
})

func (s *localJujuTestSuite) TestStartStop(c *gc.C) {
	c.Skip("StartInstance not implemented yet.")
}

func (s *localJujuTestSuite) testBootstrap(c *gc.C, cfg *config.Config) (env environs.Environ) {
	ctx := coretesting.Context(c)
	environ, err := local.Provider.Prepare(ctx, cfg)
	c.Assert(err, gc.IsNil)
	envtesting.UploadFakeTools(c, environ.Storage())
	defer environ.Storage().RemoveAll()
	err = environ.Bootstrap(ctx, environs.BootstrapParams{})
	c.Assert(err, gc.IsNil)
	return environ
}

func (s *localJujuTestSuite) TestBootstrap(c *gc.C) {
	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
		c.Assert(cloudcfg.AptUpdate(), jc.IsFalse)
		c.Assert(cloudcfg.AptUpgrade(), jc.IsFalse)
		c.Assert(cloudcfg.Packages(), gc.HasLen, 0)
		c.Assert(mcfg.AgentEnvironment, gc.Not(gc.IsNil))
		// local does not allow machine-0 to host units
		c.Assert(mcfg.Jobs, gc.DeepEquals, []params.MachineJob{params.JobManageEnviron})
		return nil
	})
	s.testBootstrap(c, minimalConfig(c))
}

func (s *localJujuTestSuite) TestDestroy(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	err := env.Destroy()
	// Succeeds because there's no "agents" directory,
	// so destroy will just return without attempting
	// sudo or anything.
	c.Assert(err, gc.IsNil)
	c.Assert(s.fakesudo+".args", jc.DoesNotExist)
}

func (s *localJujuTestSuite) makeAgentsDir(c *gc.C, env environs.Environ) {
	rootDir := env.Config().AllAttrs()["root-dir"].(string)
	agentsDir := filepath.Join(rootDir, "agents")
	err := os.Mkdir(agentsDir, 0755)
	c.Assert(err, gc.IsNil)
}

func (s *localJujuTestSuite) TestDestroyCallSudo(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	err := env.Destroy()
	c.Assert(err, gc.IsNil)
	data, err := ioutil.ReadFile(s.fakesudo + ".args")
	c.Assert(err, gc.IsNil)
	expected := []string{
		s.fakesudo,
		"env",
		"JUJU_HOME=" + osenv.JujuHome(),
		os.Args[0],
		"destroy-environment",
		"-y",
		"--force",
		env.Config().Name(),
	}
	c.Assert(string(data), gc.Equals, strings.Join(expected, " ")+"\n")
}

func (s *localJujuTestSuite) makeFakeUpstartScripts(c *gc.C, env environs.Environ,
) (mongoService *upstart.Service, machineAgent *upstart.Service) {
	upstartDir := c.MkDir()
	s.PatchValue(&upstart.InitDir, upstartDir)
	s.MakeTool(c, "start", `echo "some-service start/running, process 123"`)

	namespace := env.Config().AllAttrs()["namespace"].(string)
	mongoService = upstart.NewService(mongo.ServiceName(namespace))
	mongoConf := upstart.Conf{
		Service: *mongoService,
		Desc:    "fake mongo",
		Cmd:     "echo FAKE",
	}
	err := mongoConf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(mongoService.Installed(), jc.IsTrue)

	machineAgent = upstart.NewService(fmt.Sprintf("juju-agent-%s", namespace))
	agentConf := upstart.Conf{
		Service: *machineAgent,
		Desc:    "fake agent",
		Cmd:     "echo FAKE",
	}
	err = agentConf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(machineAgent.Installed(), jc.IsTrue)

	return mongoService, machineAgent
}

func (s *localJujuTestSuite) TestDestroyRemovesUpstartServices(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	mongo, machineAgent := s.makeFakeUpstartScripts(c, env)
	s.PatchValue(local.CheckIfRoot, func() bool { return true })

	err := env.Destroy()
	c.Assert(err, gc.IsNil)

	c.Assert(mongo.Installed(), jc.IsFalse)
	c.Assert(machineAgent.Installed(), jc.IsFalse)
}

func (s *localJujuTestSuite) TestDestroyRemovesContainers(c *gc.C) {
	env := s.testBootstrap(c, minimalConfig(c))
	s.makeAgentsDir(c, env)
	s.PatchValue(local.CheckIfRoot, func() bool { return true })

	namespace := env.Config().AllAttrs()["namespace"].(string)
	manager, err := lxc.NewContainerManager(container.ManagerConfig{
		container.ConfigName:   namespace,
		container.ConfigLogDir: "logdir",
	})
	c.Assert(err, gc.IsNil)

	machine1 := containertesting.CreateContainer(c, manager, "1")

	err = env.Destroy()
	c.Assert(err, gc.IsNil)

	container := s.Factory.New(string(machine1.Id()))
	c.Assert(container.IsConstructed(), jc.IsFalse)
}

func (s *localJujuTestSuite) TestBootstrapRemoveLeftovers(c *gc.C) {
	cfg := minimalConfig(c)
	rootDir := cfg.AllAttrs()["root-dir"].(string)

	// Create a dir inside local/log that should be removed by Bootstrap.
	logThings := filepath.Join(rootDir, "log", "things")
	err := os.MkdirAll(logThings, 0755)
	c.Assert(err, gc.IsNil)

	// Create a cloud-init-output.log in root-dir that should be
	// removed/truncated by Bootstrap.
	cloudInitOutputLog := filepath.Join(rootDir, "cloud-init-output.log")
	err = ioutil.WriteFile(cloudInitOutputLog, []byte("ohai"), 0644)
	c.Assert(err, gc.IsNil)

	s.testBootstrap(c, cfg)
	c.Assert(logThings, jc.DoesNotExist)
	c.Assert(cloudInitOutputLog, jc.DoesNotExist)
	c.Assert(filepath.Join(rootDir, "log"), jc.IsSymlink)
}

func (s *localJujuTestSuite) TestConstraintsValidator(c *gc.C) {
	ctx := coretesting.Context(c)
	env, err := local.Provider.Prepare(ctx, minimalConfig(c))
	c.Assert(err, gc.IsNil)
	validator, err := env.ConstraintsValidator()
	c.Assert(err, gc.IsNil)
	hostArch := arch.HostArch()
	cons := constraints.MustParse(fmt.Sprintf("arch=%s instance-type=foo tags=bar cpu-power=10 cpu-cores=2", hostArch))
	unsupported, err := validator.Validate(cons)
	c.Assert(err, gc.IsNil)
	c.Assert(unsupported, gc.DeepEquals, []string{"cpu-cores", "cpu-power", "instance-type", "tags"})
}

func (s *localJujuTestSuite) TestConstraintsValidatorVocab(c *gc.C) {
	env := s.Prepare(c)
	validator, err := env.ConstraintsValidator()
	c.Assert(err, gc.IsNil)

	hostArch := arch.HostArch()
	var invalidArch string
	for _, a := range arch.AllSupportedArches {
		if a != hostArch {
			invalidArch = a
			break
		}
	}
	cons := constraints.MustParse(fmt.Sprintf("arch=%s", invalidArch))
	_, err = validator.Validate(cons)
	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch="+invalidArch+"\nvalid values are:.*")
}
<|endoftext|>