<commit_before>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// updatestd is an experimental program that has been used to update
// the standard library modules as part of golang.org/issue/36905 in
// CL 255860 and CL 266898. It's expected to be modified to meet the
// ongoing needs of that recurring maintenance work.
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"golang.org/x/build/gerrit"
	"golang.org/x/build/internal/envutil"
)

func main() {
	log.SetFlags(0)

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: updatestd -goroot=<goroot> -branch=<branch>")
		flag.PrintDefaults()
	}
	goroot := flag.String("goroot", "", "path to a working copy of https://go.googlesource.com/go (required)")
	branch := flag.String("branch", "", "branch to target, such as master or release-branch.go1.Y (required)")
	flag.Parse()
	if flag.NArg() != 0 || *goroot == "" || *branch == "" {
		flag.Usage()
		os.Exit(2)
	}

	// Determine the Go version from the GOROOT source tree.
	goVersion, err := gorootVersion(*goroot)
	if err != nil {
		log.Fatalln(err)
	}

	// Confirm that bundle is in PATH.
	// It's needed for a go generate step later.
	bundlePath, err := exec.LookPath("bundle")
	if err != nil {
		log.Fatalln("can't find bundle in PATH; did you run 'go install golang.org/x/tools/cmd/bundle@latest' and add it to PATH?")
	}

	// Fetch latest hashes of Go projects from Gerrit,
	// using the specified branch name.
	//
	// This gives us a consistent snapshot of all golang.org/x module versions
	// at a given point in time. This ensures selection of latest available
	// pseudo-versions is done without being subject to module mirror caching,
	// and that selected pseudo-versions can be re-used across multiple modules.
	//
	// TODO: Consider a future enhancement of fetching build status for all
	// commits that are selected and reporting if any of them have a failure.
	//
	cl := gerrit.NewClient("https://go-review.googlesource.com", gerrit.NoAuth)
	projs, err := cl.GetProjects(context.Background(), *branch)
	if err != nil {
		log.Fatalln("failed to get a list of Gerrit projects:", err)
	}
	hashes := map[string]string{}
	for name, p := range projs {
		if p.State != "ACTIVE" {
			continue
		}
		if hash, ok := p.Branches[*branch]; ok {
			hashes[name] = hash
		}
	}

	w := Work{
		Branch:        *branch,
		GoVersion:     fmt.Sprintf("1.%d", goVersion),
		ProjectHashes: hashes,
	}

	// Print environment information.
	r := runner{filepath.Join(*goroot, "src")}
	r.run("go", "version")
	r.run("go", "env", "GOROOT")
	r.run("go", "version", "-m", bundlePath)
	log.Println()

	// Walk the standard library source tree (GOROOT/src),
	// skipping directories that start with "." and named "testdata" or "vendor",
	// and update modules that are found.
	err = filepath.Walk(filepath.Join(*goroot, "src"), func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() && (strings.HasPrefix(fi.Name(), ".") || fi.Name() == "testdata" || fi.Name() == "vendor") {
			return filepath.SkipDir
		}
		goModFile := fi.Name() == "go.mod" && !fi.IsDir()
		if goModFile {
			moduleDir := filepath.Dir(path)
			err := w.UpdateModule(moduleDir)
			if err != nil {
				return fmt.Errorf("failed to update module in %s: %v", moduleDir, err)
			}
			return filepath.SkipDir // Skip the remaining files in this directory.
		}
		return nil
	})
	if err != nil {
		log.Fatalln(err)
	}

	// Re-bundle packages in the standard library.
	//
	// TODO: Maybe do GOBIN=$(mktemp -d) go install golang.org/x/tools/cmd/bundle@version or so,
	// and add it to PATH to eliminate variance in bundle tool version. Can be considered later.
	//
	log.Println("updating bundles in", r.dir)
	r.run("go", "generate", "-run=bundle", "std", "cmd")
}

type Work struct {
	Branch        string            // Target branch name.
	GoVersion     string            // Major Go version, like "1.x".
	ProjectHashes map[string]string // Gerrit project name → commit hash.
}

// UpdateModule updates the standard library module found in dir:
//
// 	1.	Set the expected Go version in go.mod file to w.GoVersion.
// 	2.	For modules in the build list with "golang.org/x/" prefix,
// 		update to pseudo-version corresponding to w.ProjectHashes.
// 	3.	Run go mod tidy.
// 	4.	Run go mod vendor.
//
// The logic in this method needs to serve the dependency update
// policy for the purpose of golang.org/issue/36905, although it
// does not directly define said policy.
//
func (w Work) UpdateModule(dir string) error {
	// Determine the build list.
	main, deps := buildList(dir)

	// Determine module versions to get.
	goGet := []string{"go", "get", "-d"}
	for _, m := range deps {
		if !strings.HasPrefix(m.Path, "golang.org/x/") {
			log.Printf("skipping %s (out of scope, it's not a golang.org/x dependency)\n", m.Path)
			continue
		}
		gerritProj := m.Path[len("golang.org/x/"):]
		hash, ok := w.ProjectHashes[gerritProj]
		if !ok {
			if m.Indirect {
				log.Printf("skipping %s because branch %s doesn't exist and it's indirect\n", m.Path, w.Branch)
				continue
			}
			return fmt.Errorf("no hash for Gerrit project %q", gerritProj)
		}
		goGet = append(goGet, m.Path+"@"+hash)
	}

	// Run all the commands.
	log.Println("updating module", main.Path, "in", dir)
	r := runner{dir}
	r.run("go", "mod", "edit", "-go="+w.GoVersion)
	r.run(goGet...)
	r.run("go", "mod", "tidy")
	r.run("go", "mod", "vendor")
	log.Println()
	return nil
}

// buildList determines the build list in the directory dir
// by invoking the go command. It uses -mod=readonly mode.
// It returns the main module and other modules separately
// for convenience to the UpdateModule caller.
//
// See https://golang.org/cmd/go/#hdr-The_main_module_and_the_build_list
// and https://golang.org/ref/mod#glos-build-list.
func buildList(dir string) (main module, deps []module) {
	out := runner{dir}.runOut("go", "list", "-mod=readonly", "-m", "-json", "all")
	for dec := json.NewDecoder(bytes.NewReader(out)); ; {
		var m module
		err := dec.Decode(&m)
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatalf("internal error: unexpected problem decoding JSON returned by go list -json: %v", err)
		}
		if m.Main {
			main = m
			continue
		}
		deps = append(deps, m)
	}
	return main, deps
}

type module struct {
	Path     string // Module path.
	Main     bool   // Is this the main module?
	Indirect bool   // Is this module only an indirect dependency of main module?
}

// gorootVersion reads the GOROOT/src/internal/goversion/goversion.go
// file and reports the Version declaration value found therein.
func gorootVersion(goroot string) (int, error) {
	// Parse the goversion.go file, extract the declaration from the AST.
	//
	// This is a pragmatic approach that relies on the trajectory of the
	// internal/goversion package being predictable and unlikely to change.
	// If that stops being true, this small helper is easy to re-write.
	//
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filepath.Join(goroot, "src", "internal", "goversion", "goversion.go"), nil, 0)
	if os.IsNotExist(err) {
		return 0, fmt.Errorf("did not find goversion.go file (%v); wrong goroot or did internal/goversion package change?", err)
	} else if err != nil {
		return 0, err
	}
	for _, d := range f.Decls {
		g, ok := d.(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, s := range g.Specs {
			v, ok := s.(*ast.ValueSpec)
			if !ok || len(v.Names) != 1 || v.Names[0].String() != "Version" || len(v.Values) != 1 {
				continue
			}
			l, ok := v.Values[0].(*ast.BasicLit)
			if !ok || l.Kind != token.INT {
				continue
			}
			return strconv.Atoi(l.Value)
		}
	}
	return 0, fmt.Errorf("did not find Version declaration in %s; wrong goroot or did internal/goversion package change?", fset.File(f.Pos()).Name())
}

type runner struct{ dir string }

// run runs the command and requires that it succeeds.
// It logs the command's combined output.
func (r runner) run(args ...string) {
	log.Printf("> %s\n", strings.Join(args, " "))
	cmd := exec.Command(args[0], args[1:]...)
	envutil.SetDir(cmd, r.dir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("command failed: %s\n%s", err, out)
	}
	if len(out) != 0 {
		log.Print(string(out))
	}
}

// runOut runs the command, requires that it succeeds,
// and returns the command's standard output.
func (r runner) runOut(args ...string) []byte {
	cmd := exec.Command(args[0], args[1:]...)
	envutil.SetDir(cmd, r.dir)
	out, err := cmd.Output()
	if err != nil {
		log.Printf("> %s\n", strings.Join(args, " "))
		log.Fatalf("command failed: %s\n%s", err, out)
	}
	return out
}
<commit_msg>cmd/updatestd: ignore _ directories<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// updatestd is an experimental program that has been used to update
// the standard library modules as part of golang.org/issue/36905 in
// CL 255860 and CL 266898. It's expected to be modified to meet the
// ongoing needs of that recurring maintenance work.
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"golang.org/x/build/gerrit"
	"golang.org/x/build/internal/envutil"
)

func main() {
	log.SetFlags(0)

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: updatestd -goroot=<goroot> -branch=<branch>")
		flag.PrintDefaults()
	}
	goroot := flag.String("goroot", "", "path to a working copy of https://go.googlesource.com/go (required)")
	branch := flag.String("branch", "", "branch to target, such as master or release-branch.go1.Y (required)")
	flag.Parse()
	if flag.NArg() != 0 || *goroot == "" || *branch == "" {
		flag.Usage()
		os.Exit(2)
	}

	// Determine the Go version from the GOROOT source tree.
	goVersion, err := gorootVersion(*goroot)
	if err != nil {
		log.Fatalln(err)
	}

	// Confirm that bundle is in PATH.
	// It's needed for a go generate step later.
	bundlePath, err := exec.LookPath("bundle")
	if err != nil {
		log.Fatalln("can't find bundle in PATH; did you run 'go install golang.org/x/tools/cmd/bundle@latest' and add it to PATH?")
	}

	// Fetch latest hashes of Go projects from Gerrit,
	// using the specified branch name.
	//
	// This gives us a consistent snapshot of all golang.org/x module versions
	// at a given point in time. This ensures selection of latest available
	// pseudo-versions is done without being subject to module mirror caching,
	// and that selected pseudo-versions can be re-used across multiple modules.
	//
	// TODO: Consider a future enhancement of fetching build status for all
	// commits that are selected and reporting if any of them have a failure.
	//
	cl := gerrit.NewClient("https://go-review.googlesource.com", gerrit.NoAuth)
	projs, err := cl.GetProjects(context.Background(), *branch)
	if err != nil {
		log.Fatalln("failed to get a list of Gerrit projects:", err)
	}
	hashes := map[string]string{}
	for name, p := range projs {
		if p.State != "ACTIVE" {
			continue
		}
		if hash, ok := p.Branches[*branch]; ok {
			hashes[name] = hash
		}
	}

	w := Work{
		Branch:        *branch,
		GoVersion:     fmt.Sprintf("1.%d", goVersion),
		ProjectHashes: hashes,
	}

	// Print environment information.
	r := runner{filepath.Join(*goroot, "src")}
	r.run("go", "version")
	r.run("go", "env", "GOROOT")
	r.run("go", "version", "-m", bundlePath)
	log.Println()

	// Walk the standard library source tree (GOROOT/src),
	// skipping directories that the Go command ignores (see go help packages)
	// and update modules that are found.
	err = filepath.Walk(filepath.Join(*goroot, "src"), func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() && (strings.HasPrefix(fi.Name(), ".") || strings.HasPrefix(fi.Name(), "_") || fi.Name() == "testdata" || fi.Name() == "vendor") {
			return filepath.SkipDir
		}
		goModFile := fi.Name() == "go.mod" && !fi.IsDir()
		if goModFile {
			moduleDir := filepath.Dir(path)
			err := w.UpdateModule(moduleDir)
			if err != nil {
				return fmt.Errorf("failed to update module in %s: %v", moduleDir, err)
			}
			return filepath.SkipDir // Skip the remaining files in this directory.
		}
		return nil
	})
	if err != nil {
		log.Fatalln(err)
	}

	// Re-bundle packages in the standard library.
	//
	// TODO: Maybe do GOBIN=$(mktemp -d) go install golang.org/x/tools/cmd/bundle@version or so,
	// and add it to PATH to eliminate variance in bundle tool version. Can be considered later.
	//
	log.Println("updating bundles in", r.dir)
	r.run("go", "generate", "-run=bundle", "std", "cmd")
}

type Work struct {
	Branch        string            // Target branch name.
	GoVersion     string            // Major Go version, like "1.x".
	ProjectHashes map[string]string // Gerrit project name → commit hash.
}

// UpdateModule updates the standard library module found in dir:
//
// 	1.	Set the expected Go version in go.mod file to w.GoVersion.
// 	2.	For modules in the build list with "golang.org/x/" prefix,
// 		update to pseudo-version corresponding to w.ProjectHashes.
// 	3.	Run go mod tidy.
// 	4.	Run go mod vendor.
//
// The logic in this method needs to serve the dependency update
// policy for the purpose of golang.org/issue/36905, although it
// does not directly define said policy.
//
func (w Work) UpdateModule(dir string) error {
	// Determine the build list.
	main, deps := buildList(dir)

	// Determine module versions to get.
	goGet := []string{"go", "get", "-d"}
	for _, m := range deps {
		if !strings.HasPrefix(m.Path, "golang.org/x/") {
			log.Printf("skipping %s (out of scope, it's not a golang.org/x dependency)\n", m.Path)
			continue
		}
		gerritProj := m.Path[len("golang.org/x/"):]
		hash, ok := w.ProjectHashes[gerritProj]
		if !ok {
			if m.Indirect {
				log.Printf("skipping %s because branch %s doesn't exist and it's indirect\n", m.Path, w.Branch)
				continue
			}
			return fmt.Errorf("no hash for Gerrit project %q", gerritProj)
		}
		goGet = append(goGet, m.Path+"@"+hash)
	}

	// Run all the commands.
	log.Println("updating module", main.Path, "in", dir)
	r := runner{dir}
	r.run("go", "mod", "edit", "-go="+w.GoVersion)
	r.run(goGet...)
	r.run("go", "mod", "tidy")
	r.run("go", "mod", "vendor")
	log.Println()
	return nil
}

// buildList determines the build list in the directory dir
// by invoking the go command. It uses -mod=readonly mode.
// It returns the main module and other modules separately
// for convenience to the UpdateModule caller.
//
// See https://golang.org/cmd/go/#hdr-The_main_module_and_the_build_list
// and https://golang.org/ref/mod#glos-build-list.
func buildList(dir string) (main module, deps []module) {
	out := runner{dir}.runOut("go", "list", "-mod=readonly", "-m", "-json", "all")
	for dec := json.NewDecoder(bytes.NewReader(out)); ; {
		var m module
		err := dec.Decode(&m)
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatalf("internal error: unexpected problem decoding JSON returned by go list -json: %v", err)
		}
		if m.Main {
			main = m
			continue
		}
		deps = append(deps, m)
	}
	return main, deps
}

type module struct {
	Path     string // Module path.
	Main     bool   // Is this the main module?
	Indirect bool   // Is this module only an indirect dependency of main module?
}

// gorootVersion reads the GOROOT/src/internal/goversion/goversion.go
// file and reports the Version declaration value found therein.
func gorootVersion(goroot string) (int, error) {
	// Parse the goversion.go file, extract the declaration from the AST.
	//
	// This is a pragmatic approach that relies on the trajectory of the
	// internal/goversion package being predictable and unlikely to change.
	// If that stops being true, this small helper is easy to re-write.
	//
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filepath.Join(goroot, "src", "internal", "goversion", "goversion.go"), nil, 0)
	if os.IsNotExist(err) {
		return 0, fmt.Errorf("did not find goversion.go file (%v); wrong goroot or did internal/goversion package change?", err)
	} else if err != nil {
		return 0, err
	}
	for _, d := range f.Decls {
		g, ok := d.(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, s := range g.Specs {
			v, ok := s.(*ast.ValueSpec)
			if !ok || len(v.Names) != 1 || v.Names[0].String() != "Version" || len(v.Values) != 1 {
				continue
			}
			l, ok := v.Values[0].(*ast.BasicLit)
			if !ok || l.Kind != token.INT {
				continue
			}
			return strconv.Atoi(l.Value)
		}
	}
	return 0, fmt.Errorf("did not find Version declaration in %s; wrong goroot or did internal/goversion package change?", fset.File(f.Pos()).Name())
}

type runner struct{ dir string }

// run runs the command and requires that it succeeds.
// It logs the command's combined output.
func (r runner) run(args ...string) {
	log.Printf("> %s\n", strings.Join(args, " "))
	cmd := exec.Command(args[0], args[1:]...)
	envutil.SetDir(cmd, r.dir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("command failed: %s\n%s", err, out)
	}
	if len(out) != 0 {
		log.Print(string(out))
	}
}

// runOut runs the command, requires that it succeeds,
// and returns the command's standard output.
func (r runner) runOut(args ...string) []byte {
	cmd := exec.Command(args[0], args[1:]...)
	envutil.SetDir(cmd, r.dir)
	out, err := cmd.Output()
	if err != nil {
		log.Printf("> %s\n", strings.Join(args, " "))
		log.Fatalf("command failed: %s\n%s", err, out)
	}
	return out
}
<|endoftext|>


<commit_before>package cmd_verbs

import (
	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"

	"github.com/ghthor/netmon/cmd_verbs/daemon"
	"github.com/ghthor/netmon/cmd_verbs/status"
)

func DescribeRegisteredCommands(c gospec.Context) {
	c.Specify("commands that are registered are", func() {
		c.Specify("daemon", func() {
			c.Expect(MatchVerb("daemon"), Equals, daemon.Cmd)
		})

		c.Specify("status", func() {
			c.Expect(MatchVerb("status"), Equals, status.Cmd)
		})
	})
}
<commit_msg>Removed this redundancy check | I would like to have this in the future, but I'm not happy with the way it was. There was too much boilerplate.<commit_after><|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/golang/protobuf/proto"

	"pixur.org/pixur/schema"
	"pixur.org/pixur/schema/db"
	tab "pixur.org/pixur/schema/tables"
	"pixur.org/pixur/server"
	"pixur.org/pixur/tasks"
)

// TODO: make this not a hack
func run() error {
	f, err := os.Open(".config.textpb")
	if err != nil {
		return err
	}
	defer f.Close()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	var config = new(server.Config)
	if err := proto.UnmarshalText(string(data), config); err != nil {
		return err
	}
	DB, err := sql.Open(config.DbName, config.DbConfig)
	if err != nil {
		return err
	}
	defer DB.Close()
	if err := DB.Ping(); err != nil {
		return err
	}
	j, err := tab.NewJob(DB)
	if err != nil {
		return err
	}
	defer j.Rollback()

	perPicFn := func(p *schema.Pic) error {
		return perPic(p, DB, config.PixPath)
	}

	return j.ScanPics(db.Opts{
		Prefix: tab.PicsPrimary{},
		Lock:   db.LockNone,
	}, perPicFn)
}

func perPic(p *schema.Pic, DB *sql.DB, pixPath string) error {
	now := time.Now()
	// No deletion info
	if p.DeletionStatus == nil {
		return nil
	}
	// Some deletion info, but it isn't on the chopping block.
	if p.DeletionStatus.PendingDeletedTs == nil {
		return nil
	}
	// It was already hard deleted, ignore it
	if p.DeletionStatus.ActualDeletedTs != nil {
		return nil
	}

	pendingTime := schema.FromTs(p.DeletionStatus.PendingDeletedTs)
	// It is pending deletion, just not yet.
	if !now.After(pendingTime) {
		return nil
	}

	log.Println("Preparing to delete", p.GetVarPicID(), pendingTime)
	var task = &tasks.HardDeletePicTask{
		DB:      DB,
		PixPath: pixPath,
		PicID:   p.PicId,
	}
	runner := new(tasks.TaskRunner)
	if err := runner.Run(task); err != nil {
		return err
	}

	return nil
}

func main() {
	if err := run(); err != nil {
		log.Println(err)
	}
}
<commit_msg>tools: fix pruner to use a user id<commit_after>package main

import (
	"context"
	"database/sql"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/golang/protobuf/proto"

	"pixur.org/pixur/schema"
	"pixur.org/pixur/schema/db"
	tab "pixur.org/pixur/schema/tables"
	"pixur.org/pixur/server"
	"pixur.org/pixur/tasks"
)

// TODO: make this not a hack
func run() error {
	f, err := os.Open(".config.textpb")
	if err != nil {
		return err
	}
	defer f.Close()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	var config = new(server.Config)
	if err := proto.UnmarshalText(string(data), config); err != nil {
		return err
	}
	DB, err := sql.Open(config.DbName, config.DbConfig)
	if err != nil {
		return err
	}
	defer DB.Close()
	if err := DB.Ping(); err != nil {
		return err
	}
	j, err := tab.NewJob(DB)
	if err != nil {
		return err
	}
	defer j.Rollback()

	perPicFn := func(p *schema.Pic) error {
		return perPic(p, DB, config.PixPath)
	}

	return j.ScanPics(db.Opts{
		Prefix: tab.PicsPrimary{},
		Lock:   db.LockNone,
	}, perPicFn)
}

func perPic(p *schema.Pic, DB *sql.DB, pixPath string) error {
	now := time.Now()
	// No deletion info
	if p.DeletionStatus == nil {
		return nil
	}
	// Some deletion info, but it isn't on the chopping block.
	if p.DeletionStatus.PendingDeletedTs == nil {
		return nil
	}
	// It was already hard deleted, ignore it
	if p.DeletionStatus.ActualDeletedTs != nil {
		return nil
	}

	pendingTime := schema.FromTs(p.DeletionStatus.PendingDeletedTs)
	// It is pending deletion, just not yet.
	if !now.After(pendingTime) {
		return nil
	}

	log.Println("Preparing to delete", p.GetVarPicID(), pendingTime)
	var task = &tasks.HardDeletePicTask{
		DB:      DB,
		PixPath: pixPath,
		PicID:   p.PicId,
		Ctx:     tasks.CtxFromUserID(context.TODO(), -12345), // TODO: use real userid
	}
	runner := new(tasks.TaskRunner)
	if err := runner.Run(task); err != nil {
		return err
	}

	return nil
}

func main() {
	if err := run(); err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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 util

import (
	"fmt"
	"github.com/getkin/kin-openapi/openapi3"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
)

// test holds the openapi3.Operation and HTTP method associated with a single
type test struct {
	operation  *openapi3.Operation
	httpMethod string
}

// ValidateEndpoints tests all paths (represented by openapi3.Paths) with all HTTP methods and given response bodies
// and make sure they respond with the expected status code. Returns a success bool based on whether all the tests
// passed.
func ValidateEndpoints(serviceURL string, paths *openapi3.Paths, identityToken string) (bool, error) {
	success := true
	for endpoint, pathItem := range *paths {
		log.Printf("Testing %s endpoint\n", endpoint)
		tests := []test{
			{pathItem.Connect, http.MethodConnect},
			{pathItem.Delete, http.MethodDelete},
			{pathItem.Get, http.MethodGet},
			{pathItem.Head, http.MethodHead},
			{pathItem.Options, http.MethodOptions},
			{pathItem.Patch, http.MethodPatch},
			{pathItem.Post, http.MethodPost},
			{pathItem.Put, http.MethodPut},
			{pathItem.Trace, http.MethodTrace},
		}

		endpointURL := serviceURL + endpoint
		for _, t := range tests {
			s, err := validateEndpointOperation(endpointURL, t.operation, t.httpMethod, identityToken)
			if err != nil {
				return s, fmt.Errorf("[util.ValidateEndpoints] testing %s requests on %s: %w", t.httpMethod, endpointURL, err)
			}

			success = s && success
		}
	}

	return success, nil
}

// validateEndpointOperation validates a single endpoint and a single HTTP method, and ensures that the request --
// including the provided sample request body -- elicits the expected status code.
func validateEndpointOperation(endpointURL string, operation *openapi3.Operation, httpMethod string, identityToken string) (bool, error) {
	if operation == nil {
		return true, nil
	}
	log.Printf("Executing %s %s\n", httpMethod, endpointURL)

	if operation.RequestBody == nil {
		log.Println("Sending empty request body")
		reqBodyReader := strings.NewReader("")

		s, err := makeTestRequest(endpointURL, httpMethod, "", reqBodyReader, operation, identityToken)
		if err != nil {
			return s, fmt.Errorf("[util.validateEndpointOperation] testing %s request on %s: %w", httpMethod, endpointURL, err)
		}

		return s, nil
	}

	reqBodies := operation.RequestBody.Value.Content
	allTestsPassed := true
	for mimeType, mediaType := range reqBodies {
		reqBodyStr := mediaType.Example.(string)
		log.Printf("Sending %s: %s", mimeType, reqBodyStr)

		reqBodyReader := strings.NewReader(reqBodyStr)

		s, err := makeTestRequest(endpointURL, httpMethod, mimeType, reqBodyReader, operation, identityToken)
		if err != nil {
			return s, fmt.Errorf("[util.validateEndpointOperation] testing %s %s request on %s: %w", httpMethod, mimeType, endpointURL, err)
		}

		allTestsPassed = allTestsPassed && s
	}

	return allTestsPassed, nil
}

// makeTestRequest returns a success bool based on whether the returned status code  was included in the provided
// openapi3.Operation expected responses.
func makeTestRequest(endpointURL, httpMethod, mimeType string, reqBodyReader *strings.Reader, operation *openapi3.Operation, identityToken string) (bool, error) {
	client := &http.DefaultClient

	req, err := http.NewRequest(httpMethod, endpointURL, reqBodyReader)
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest] creating an http.Request: %w", err)
	}

	req.Header.Add("Authorization", "Bearer "+identityToken)
	req.Header.Add("content-type", mimeType)

	resp, err := (*client).Do(req)
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest]: creating executing a http.Request: %w", err)
	}

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest]: reading http.Response: %w", err)
	}

	statusCode := strconv.Itoa(resp.StatusCode)
	log.Printf("Status code: %s\n", statusCode)

	if val, ok := operation.Responses[statusCode]; ok {
		log.Printf("Response description: %s\n", *val.Value.Description)
		return true, nil
	}

	log.Println("Unknown response description: FAIL")
	log.Println("Dumping response body")
	fmt.Println(string(body))

	return false, nil
}
<commit_msg>add timeout of 10s to HTTP requests made to cloud run services (fix #16) (#26)<commit_after>// Copyright 2020 Google LLC
//
// 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 util

import (
	"context"
	"fmt"
	"github.com/getkin/kin-openapi/openapi3"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// test holds the openapi3.Operation and HTTP method associated with a single
type test struct {
	operation  *openapi3.Operation
	httpMethod string
}

// httpTimeout is the default timeout that used for HTTP requests made to Cloud Run services.
const httpTimeout = 10 * time.Second

// ValidateEndpoints tests all paths (represented by openapi3.Paths) with all HTTP methods and given response bodies
// and make sure they respond with the expected status code. Returns a success bool based on whether all the tests
// passed.
func ValidateEndpoints(serviceURL string, paths *openapi3.Paths, identityToken string) (bool, error) {
	success := true
	for endpoint, pathItem := range *paths {
		log.Printf("Testing %s endpoint\n", endpoint)
		tests := []test{
			{pathItem.Connect, http.MethodConnect},
			{pathItem.Delete, http.MethodDelete},
			{pathItem.Get, http.MethodGet},
			{pathItem.Head, http.MethodHead},
			{pathItem.Options, http.MethodOptions},
			{pathItem.Patch, http.MethodPatch},
			{pathItem.Post, http.MethodPost},
			{pathItem.Put, http.MethodPut},
			{pathItem.Trace, http.MethodTrace},
		}

		endpointURL := serviceURL + endpoint
		for _, t := range tests {
			s, err := validateEndpointOperation(endpointURL, t.operation, t.httpMethod, identityToken)
			if err != nil {
				return s, fmt.Errorf("[util.ValidateEndpoints] testing %s requests on %s: %w", t.httpMethod, endpointURL, err)
			}

			success = s && success
		}
	}

	return success, nil
}

// validateEndpointOperation validates a single endpoint and a single HTTP method, and ensures that the request --
// including the provided sample request body -- elicits the expected status code.
func validateEndpointOperation(endpointURL string, operation *openapi3.Operation, httpMethod string, identityToken string) (bool, error) {
	if operation == nil {
		return true, nil
	}
	log.Printf("Executing %s %s\n", httpMethod, endpointURL)

	if operation.RequestBody == nil {
		log.Println("Sending empty request body")
		reqBodyReader := strings.NewReader("")

		s, err := makeTestRequest(endpointURL, httpMethod, "", reqBodyReader, operation, identityToken)
		if err != nil {
			return s, fmt.Errorf("[util.validateEndpointOperation] testing %s request on %s: %w", httpMethod, endpointURL, err)
		}

		return s, nil
	}

	reqBodies := operation.RequestBody.Value.Content
	allTestsPassed := true
	for mimeType, mediaType := range reqBodies {
		reqBodyStr := mediaType.Example.(string)
		log.Printf("Sending %s: %s", mimeType, reqBodyStr)

		reqBodyReader := strings.NewReader(reqBodyStr)

		s, err := makeTestRequest(endpointURL, httpMethod, mimeType, reqBodyReader, operation, identityToken)
		if err != nil {
			return s, fmt.Errorf("[util.validateEndpointOperation] testing %s %s request on %s: %w", httpMethod, mimeType, endpointURL, err)
		}

		allTestsPassed = allTestsPassed && s
	}

	return allTestsPassed, nil
}

// makeTestRequest returns a success bool based on whether the returned status code  was included in the provided
// openapi3.Operation expected responses.
func makeTestRequest(endpointURL, httpMethod, mimeType string, reqBodyReader *strings.Reader, operation *openapi3.Operation, identityToken string) (bool, error) {
	// TODO: add user option to configure timeout for each test request
	ctx, _ := context.WithTimeout(context.Background(), httpTimeout)
	req, err := http.NewRequestWithContext(ctx, httpMethod, endpointURL, reqBodyReader)
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest] creating an http.Request: %w", err)
	}

	req.Header.Add("Authorization", "Bearer "+identityToken)
	req.Header.Add("content-type", mimeType)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest]: creating executing a http.Request: %w", err)
	}

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return false, fmt.Errorf("[util.makeTestRequest]: reading http.Response: %w", err)
	}

	statusCode := strconv.Itoa(resp.StatusCode)
	log.Printf("Status code: %s\n", statusCode)

	if val, ok := operation.Responses[statusCode]; ok {
		log.Printf("Response description: %s\n", *val.Value.Description)
		return true, nil
	}

	log.Println("Unknown response description: FAIL")
	log.Println("Dumping response body")
	fmt.Println(string(body))

	return false, nil
}
<|endoftext|>


<commit_before>package allocdir

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

	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	// The name of the directory that is shared across tasks in a task group.
	SharedAllocName = "alloc"

	// The set of directories that exist inside eache shared alloc directory.
	SharedAllocDirs = []string{"logs", "tmp", "data"}

	// The name of the directory that exists inside each task directory
	// regardless of driver.
	TaskLocal = "local"
)

type AllocDir struct {
	// AllocDir is the directory used for storing any state
	// of this allocation. It will be purged on alloc destroy.
	AllocDir string

	// The shared directory is available to all tasks within the same task
	// group.
	SharedDir string

	// TaskDirs is a mapping of task names to their non-shared directory.
	TaskDirs map[string]string

	// A list of locations the shared alloc has been mounted to.
	mounted []string
}

type AllocFileInfo struct {
	Name  string
	IsDir bool
	Size  int64
}

type AllocDirFS interface {
	List(path string) ([]*AllocFileInfo, error)
	Stat(path string) (*AllocFileInfo, error)
	ReadAt(path string, offset int64, limit int64) (io.ReadCloser, error)
}

func NewAllocDir(allocDir string) *AllocDir {
	d := &AllocDir{AllocDir: allocDir, TaskDirs: make(map[string]string)}
	d.SharedDir = filepath.Join(d.AllocDir, SharedAllocName)
	return d
}

// Tears down previously build directory structure.
func (d *AllocDir) Destroy() error {
	// Unmount all mounted shared alloc dirs.
	for _, m := range d.mounted {
		if err := d.unmountSharedDir(m); err != nil {
			return fmt.Errorf("Failed to unmount shared directory: %v", err)
		}
	}

	return os.RemoveAll(d.AllocDir)
}

// Given a list of a task build the correct alloc structure.
func (d *AllocDir) Build(tasks []*structs.Task) error {
	// Make the alloc directory, owned by the nomad process.
	if err := os.MkdirAll(d.AllocDir, 0700); err != nil {
		return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err)
	}

	// Make the shared directory and make it availabe to all user/groups.
	if err := os.Mkdir(d.SharedDir, 0777); err != nil {
		return err
	}

	// Make the shared directory have non-root permissions.
	if err := d.dropDirPermissions(d.SharedDir); err != nil {
		return err
	}

	for _, dir := range SharedAllocDirs {
		p := filepath.Join(d.SharedDir, dir)
		if err := os.Mkdir(p, 0777); err != nil {
			return err
		}
	}

	// Make the task directories.
	for _, t := range tasks {
		taskDir := filepath.Join(d.AllocDir, t.Name)
		if err := os.Mkdir(taskDir, 0777); err != nil {
			return err
		}

		// Make the task directory have non-root permissions.
		if err := d.dropDirPermissions(taskDir); err != nil {
			return err
		}

		// Create a local directory that each task can use.
		local := filepath.Join(taskDir, TaskLocal)
		if err := os.Mkdir(local, 0777); err != nil {
			return err
		}

		if err := d.dropDirPermissions(local); err != nil {
			return err
		}

		d.TaskDirs[t.Name] = taskDir
	}

	return nil
}

// Embed takes a mapping of absolute directory or file paths on the host to
// their intended, relative location within the task directory. Embed attempts
// hardlink and then defaults to copying. If the path exists on the host and
// can't be embeded an error is returned.
func (d *AllocDir) Embed(task string, entries map[string]string) error {
	taskdir, ok := d.TaskDirs[task]
	if !ok {
		return fmt.Errorf("Task directory doesn't exist for task %v", task)
	}

	subdirs := make(map[string]string)
	for source, dest := range entries {
		// Check to see if directory exists on host.
		s, err := os.Stat(source)
		if os.IsNotExist(err) {
			continue
		}

		// Embedding a single file
		if !s.IsDir() {
			destDir := filepath.Join(taskdir, filepath.Dir(dest))
			if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil {
				return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
			}

			// Copy the file.
			taskEntry := filepath.Join(destDir, filepath.Base(dest))
			if err := d.linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil {
				return err
			}

			continue
		}

		// Create destination directory.
		destDir := filepath.Join(taskdir, dest)
		if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil {
			return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
		}

		// Enumerate the files in source.
		dirEntries, err := ioutil.ReadDir(source)
		if err != nil {
			return fmt.Errorf("Couldn't read directory %v: %v", source, err)
		}

		for _, entry := range dirEntries {
			hostEntry := filepath.Join(source, entry.Name())
			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
			if entry.IsDir() {
				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
				continue
			}

			// Check if entry exists. This can happen if restarting a failed
			// task.
			if _, err := os.Lstat(taskEntry); err == nil {
				continue
			}

			if !entry.Mode().IsRegular() {
				// If it is a symlink we can create it, otherwise we skip it.
				if entry.Mode()&os.ModeSymlink == 0 {
					continue
				}

				link, err := os.Readlink(hostEntry)
				if err != nil {
					return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err)
				}

				if err := os.Symlink(link, taskEntry); err != nil {
					return fmt.Errorf("Couldn't create symlink: %v", err)
				}
				continue
			}

			if err := d.linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil {
				return err
			}
		}
	}

	// Recurse on self to copy subdirectories.
	if len(subdirs) != 0 {
		return d.Embed(task, subdirs)
	}

	return nil
}

// MountSharedDir mounts the shared directory into the specified task's
// directory. Mount is documented at an OS level in their respective
// implementation files.
func (d *AllocDir) MountSharedDir(task string) error {
	taskDir, ok := d.TaskDirs[task]
	if !ok {
		return fmt.Errorf("No task directory exists for %v", task)
	}

	taskLoc := filepath.Join(taskDir, SharedAllocName)
	if err := d.mountSharedDir(taskLoc); err != nil {
		return fmt.Errorf("Failed to mount shared directory for task %v: %v", task, err)
	}

	d.mounted = append(d.mounted, taskLoc)
	return nil
}

// List returns the list of files at a path relative to the alloc dir
func (d *AllocDir) List(path string) ([]*AllocFileInfo, error) {
	p := filepath.Join(d.AllocDir, path)
	finfos, err := ioutil.ReadDir(p)
	if err != nil {
		return []*AllocFileInfo{}, err
	}
	files := make([]*AllocFileInfo, len(finfos))
	for idx, info := range finfos {
		files[idx] = &AllocFileInfo{
			Name:  info.Name(),
			IsDir: info.IsDir(),
			Size:  info.Size(),
		}
	}
	return files, err
}

// Stat returns information about the file at path relative to the alloc dir
func (d *AllocDir) Stat(path string) (*AllocFileInfo, error) {
	p := filepath.Join(d.AllocDir, path)
	info, err := os.Stat(p)
	if err != nil {
		return nil, err
	}

	return &AllocFileInfo{
		Size:  info.Size(),
		Name:  info.Name(),
		IsDir: info.IsDir(),
	}, nil
}

// ReadAt returns a reader  for a file at the path relative to the alloc dir
//which will read a chunk of bytes at a particular offset
func (d *AllocDir) ReadAt(path string, offset int64, limit int64) (io.ReadCloser, error) {
	p := filepath.Join(d.AllocDir, path)
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	return &FileReadCloser{Reader: io.LimitReader(f, limit), Closer: f}, nil
}

// FileReadCloser wraps a LimitReader so that a file is closed once it has been
// read
type FileReadCloser struct {
	io.Reader
	io.Closer
}

func fileCopy(src, dst string, perm os.FileMode) error {
	// Do a simple copy.
	srcFile, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("Couldn't open src file %v: %v", src, err)
	}

	dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, perm)
	if err != nil {
		return fmt.Errorf("Couldn't create destination file %v: %v", dst, err)
	}

	if _, err := io.Copy(dstFile, srcFile); err != nil {
		return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err)
	}

	return nil
}
<commit_msg>Added some more comments<commit_after>package allocdir

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

	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	// The name of the directory that is shared across tasks in a task group.
	SharedAllocName = "alloc"

	// The set of directories that exist inside eache shared alloc directory.
	SharedAllocDirs = []string{"logs", "tmp", "data"}

	// The name of the directory that exists inside each task directory
	// regardless of driver.
	TaskLocal = "local"
)

type AllocDir struct {
	// AllocDir is the directory used for storing any state
	// of this allocation. It will be purged on alloc destroy.
	AllocDir string

	// The shared directory is available to all tasks within the same task
	// group.
	SharedDir string

	// TaskDirs is a mapping of task names to their non-shared directory.
	TaskDirs map[string]string

	// A list of locations the shared alloc has been mounted to.
	mounted []string
}

// AllocFileInfo holds information about a file inside the AllocDir
type AllocFileInfo struct {
	Name  string
	IsDir bool
	Size  int64
}

// AllocDirFS returns methods which exposes file operations on the alloc dir
type AllocDirFS interface {
	List(path string) ([]*AllocFileInfo, error)
	Stat(path string) (*AllocFileInfo, error)
	ReadAt(path string, offset int64, limit int64) (io.ReadCloser, error)
}

func NewAllocDir(allocDir string) *AllocDir {
	d := &AllocDir{AllocDir: allocDir, TaskDirs: make(map[string]string)}
	d.SharedDir = filepath.Join(d.AllocDir, SharedAllocName)
	return d
}

// Tears down previously build directory structure.
func (d *AllocDir) Destroy() error {
	// Unmount all mounted shared alloc dirs.
	for _, m := range d.mounted {
		if err := d.unmountSharedDir(m); err != nil {
			return fmt.Errorf("Failed to unmount shared directory: %v", err)
		}
	}

	return os.RemoveAll(d.AllocDir)
}

// Given a list of a task build the correct alloc structure.
func (d *AllocDir) Build(tasks []*structs.Task) error {
	// Make the alloc directory, owned by the nomad process.
	if err := os.MkdirAll(d.AllocDir, 0700); err != nil {
		return fmt.Errorf("Failed to make the alloc directory %v: %v", d.AllocDir, err)
	}

	// Make the shared directory and make it availabe to all user/groups.
	if err := os.Mkdir(d.SharedDir, 0777); err != nil {
		return err
	}

	// Make the shared directory have non-root permissions.
	if err := d.dropDirPermissions(d.SharedDir); err != nil {
		return err
	}

	for _, dir := range SharedAllocDirs {
		p := filepath.Join(d.SharedDir, dir)
		if err := os.Mkdir(p, 0777); err != nil {
			return err
		}
	}

	// Make the task directories.
	for _, t := range tasks {
		taskDir := filepath.Join(d.AllocDir, t.Name)
		if err := os.Mkdir(taskDir, 0777); err != nil {
			return err
		}

		// Make the task directory have non-root permissions.
		if err := d.dropDirPermissions(taskDir); err != nil {
			return err
		}

		// Create a local directory that each task can use.
		local := filepath.Join(taskDir, TaskLocal)
		if err := os.Mkdir(local, 0777); err != nil {
			return err
		}

		if err := d.dropDirPermissions(local); err != nil {
			return err
		}

		d.TaskDirs[t.Name] = taskDir
	}

	return nil
}

// Embed takes a mapping of absolute directory or file paths on the host to
// their intended, relative location within the task directory. Embed attempts
// hardlink and then defaults to copying. If the path exists on the host and
// can't be embeded an error is returned.
func (d *AllocDir) Embed(task string, entries map[string]string) error {
	taskdir, ok := d.TaskDirs[task]
	if !ok {
		return fmt.Errorf("Task directory doesn't exist for task %v", task)
	}

	subdirs := make(map[string]string)
	for source, dest := range entries {
		// Check to see if directory exists on host.
		s, err := os.Stat(source)
		if os.IsNotExist(err) {
			continue
		}

		// Embedding a single file
		if !s.IsDir() {
			destDir := filepath.Join(taskdir, filepath.Dir(dest))
			if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil {
				return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
			}

			// Copy the file.
			taskEntry := filepath.Join(destDir, filepath.Base(dest))
			if err := d.linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil {
				return err
			}

			continue
		}

		// Create destination directory.
		destDir := filepath.Join(taskdir, dest)
		if err := os.MkdirAll(destDir, s.Mode().Perm()); err != nil {
			return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
		}

		// Enumerate the files in source.
		dirEntries, err := ioutil.ReadDir(source)
		if err != nil {
			return fmt.Errorf("Couldn't read directory %v: %v", source, err)
		}

		for _, entry := range dirEntries {
			hostEntry := filepath.Join(source, entry.Name())
			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
			if entry.IsDir() {
				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
				continue
			}

			// Check if entry exists. This can happen if restarting a failed
			// task.
			if _, err := os.Lstat(taskEntry); err == nil {
				continue
			}

			if !entry.Mode().IsRegular() {
				// If it is a symlink we can create it, otherwise we skip it.
				if entry.Mode()&os.ModeSymlink == 0 {
					continue
				}

				link, err := os.Readlink(hostEntry)
				if err != nil {
					return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err)
				}

				if err := os.Symlink(link, taskEntry); err != nil {
					return fmt.Errorf("Couldn't create symlink: %v", err)
				}
				continue
			}

			if err := d.linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil {
				return err
			}
		}
	}

	// Recurse on self to copy subdirectories.
	if len(subdirs) != 0 {
		return d.Embed(task, subdirs)
	}

	return nil
}

// MountSharedDir mounts the shared directory into the specified task's
// directory. Mount is documented at an OS level in their respective
// implementation files.
func (d *AllocDir) MountSharedDir(task string) error {
	taskDir, ok := d.TaskDirs[task]
	if !ok {
		return fmt.Errorf("No task directory exists for %v", task)
	}

	taskLoc := filepath.Join(taskDir, SharedAllocName)
	if err := d.mountSharedDir(taskLoc); err != nil {
		return fmt.Errorf("Failed to mount shared directory for task %v: %v", task, err)
	}

	d.mounted = append(d.mounted, taskLoc)
	return nil
}

// List returns the list of files at a path relative to the alloc dir
func (d *AllocDir) List(path string) ([]*AllocFileInfo, error) {
	p := filepath.Join(d.AllocDir, path)
	finfos, err := ioutil.ReadDir(p)
	if err != nil {
		return []*AllocFileInfo{}, err
	}
	files := make([]*AllocFileInfo, len(finfos))
	for idx, info := range finfos {
		files[idx] = &AllocFileInfo{
			Name:  info.Name(),
			IsDir: info.IsDir(),
			Size:  info.Size(),
		}
	}
	return files, err
}

// Stat returns information about the file at path relative to the alloc dir
func (d *AllocDir) Stat(path string) (*AllocFileInfo, error) {
	p := filepath.Join(d.AllocDir, path)
	info, err := os.Stat(p)
	if err != nil {
		return nil, err
	}

	return &AllocFileInfo{
		Size:  info.Size(),
		Name:  info.Name(),
		IsDir: info.IsDir(),
	}, nil
}

// ReadAt returns a reader  for a file at the path relative to the alloc dir
//which will read a chunk of bytes at a particular offset
func (d *AllocDir) ReadAt(path string, offset int64, limit int64) (io.ReadCloser, error) {
	p := filepath.Join(d.AllocDir, path)
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	return &FileReadCloser{Reader: io.LimitReader(f, limit), Closer: f}, nil
}

// FileReadCloser wraps a LimitReader so that a file is closed once it has been
// read
type FileReadCloser struct {
	io.Reader
	io.Closer
}

func fileCopy(src, dst string, perm os.FileMode) error {
	// Do a simple copy.
	srcFile, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("Couldn't open src file %v: %v", src, err)
	}

	dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, perm)
	if err != nil {
		return fmt.Errorf("Couldn't create destination file %v: %v", dst, err)
	}

	if _, err := io.Copy(dstFile, srcFile); err != nil {
		return fmt.Errorf("Couldn't copy %v to %v: %v", src, dst, err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Yuichi Araki. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package yall

import (
    "os"
)

func Lambda(env *Env, args *Cell) Expr {
    formalArgs := args.Car().(*Cell)
    body := args.Cdr()
    return NewFunction("#lambda", func(args *Cell) Expr {
        derived := env.Derive()
        formalArgs.Each(func(e Expr) {
            symbol := e.(*Symbol)
            expr := args.Car()
            derived.Intern(symbol, expr)
            args = args.Cdr()
        })
        return derived.Begin(body)
    })
}

var specialForms = map[string]func(*Env, *Cell) Expr{

    "lambda": Lambda,
    "fn":     Lambda,

    "def": func(env *Env, args *Cell) Expr {
        if symbol, ok := args.Car().(*Symbol); ok {
            value := env.Eval(args.Cadr())
            if function, ok := value.(*Function); ok {
                function.SetName(symbol.Name())
            }
            env.Intern(symbol, value)
            return symbol
        } else if cell, ok := args.Car().(*Cell); ok {
            symbol := cell.Car().(*Symbol)
            lambdaArgs := cell.Cdr()
            lambdaBody := args.Cdr()
            lambda := Lambda(env, NewCell(lambdaArgs, lambdaBody)).(*Function)
            lambda.SetName(symbol.Name())
            env.Intern(symbol, lambda)
            return symbol
        }
        panic(NewRuntimeError("Can't define"))
    },

    "macro": func(env *Env, args *Cell) Expr {
        formalArgs := args.Car().(*Cell)
        body := args.Cdr()
        return NewMacro("#macro", func(args *Cell) Expr {
            derived := env.Derive()
            formalArgs.Each(func(e Expr) {
                symbol := e.(*Symbol)
                expr := args.Car()
                derived.Intern(symbol, expr)
                args = args.Cdr()
            })
            return derived.Begin(body)
        })
    },

    "if": func(env *Env, args *Cell) Expr {
        condition := env.Eval(args.Car())
        if condition != False {
            return env.Eval(args.Cadr())
        }
        return env.Eval(args.Caddr())
    },

    "inc!": func(env *Env, args *Cell) Expr {
        symbol, ok := args.Car().(*Symbol)
        if !ok {
            panic(NewRuntimeError("inc! requires a symbol"))
        }
        integer, ok := env.Eval(symbol).(*Integer)
        integer.setValue(integer.Value() + 1)
        return integer
    },

    "load": func(env *Env, args *Cell) Expr {
        args.Each(func(expr Expr) {
            if filename, ok := expr.(*String); ok {
                file, err := os.Open(filename.value)
                if nil != err {
                    panic(NewRuntimeError("Cannot load: " + filename.String()))
                }
                defer file.Close()
                env.Load(file)
            } else {
                panic(NewRuntimeError("Cannot load: " + expr.String()))
            }
        })
        return True
    },
}
<commit_msg>Commonalized lambda-list binding in "lambda" and "macro"<commit_after>// Copyright 2012 Yuichi Araki. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package yall

import (
    "os"
)

// TODO: Optional argument ... (lambda (a (start 0)))
// TODO: Body (rest) argument ...  (lambda (a . body))
func bindLambdaList(env *Env, lambdaList *Cell, args *Cell) {
    lambdaList.Each(func(e Expr) {
        if symbol, ok := e.(*Symbol); ok {
            expr := args.Car()
            env.Intern(symbol, expr)
            args = args.Cdr()
        }
    })
}

func Lambda(env *Env, args *Cell) Expr {
    lambdaList := args.Car().(*Cell)
    body := args.Cdr()
    return NewFunction("#lambda", func(args *Cell) Expr {
        derived := env.Derive()
        bindLambdaList(derived, lambdaList, args)
        return derived.Begin(body)
    })
}

var specialForms = map[string]func(*Env, *Cell) Expr{

    "lambda": Lambda,
    "fn":     Lambda,

    "def": func(env *Env, args *Cell) Expr {
        if symbol, ok := args.Car().(*Symbol); ok {
            value := env.Eval(args.Cadr())
            if function, ok := value.(*Function); ok {
                function.SetName(symbol.Name())
            }
            env.Intern(symbol, value)
            return symbol
        } else if cell, ok := args.Car().(*Cell); ok {
            symbol := cell.Car().(*Symbol)
            lambdaArgs := cell.Cdr()
            lambdaBody := args.Cdr()
            lambda := Lambda(env, NewCell(lambdaArgs, lambdaBody)).(*Function)
            lambda.SetName(symbol.Name())
            env.Intern(symbol, lambda)
            return symbol
        }
        panic(NewRuntimeError("Can't define"))
    },

    "macro": func(env *Env, args *Cell) Expr {
        lambdaList := args.Car().(*Cell)
        body := args.Cdr()
        return NewMacro("#macro", func(args *Cell) Expr {
            derived := env.Derive()
            bindLambdaList(derived, lambdaList, args)
            return derived.Begin(body)
        })
    },

    "if": func(env *Env, args *Cell) Expr {
        condition := env.Eval(args.Car())
        if condition != False {
            return env.Eval(args.Cadr())
        }
        return env.Eval(args.Caddr())
    },

    "inc!": func(env *Env, args *Cell) Expr {
        symbol, ok := args.Car().(*Symbol)
        if !ok {
            panic(NewRuntimeError("inc! requires a symbol"))
        }
        integer, ok := env.Eval(symbol).(*Integer)
        integer.setValue(integer.Value() + 1)
        return integer
    },

    "load": func(env *Env, args *Cell) Expr {
        args.Each(func(expr Expr) {
            if filename, ok := expr.(*String); ok {
                file, err := os.Open(filename.value)
                if nil != err {
                    panic(NewRuntimeError("Cannot load: " + filename.String()))
                }
                defer file.Close()
                env.Load(file)
            } else {
                panic(NewRuntimeError("Cannot load: " + expr.String()))
            }
        })
        return True
    },
}
<|endoftext|>


<commit_before>// Copyright 2017 Istio 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 util

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
	"time"
	// TODO(nmittler): Remove this
	_ "github.com/golang/glog"

	"istio.io/istio/pkg/log"
)

const (
	podRunning   = "Running"
	podFailedGet = "Failed_Get"
)

// Fill complete a template with given values and generate a new output file
func Fill(outFile, inFile string, values interface{}) error {
	var bytes bytes.Buffer
	w := bufio.NewWriter(&bytes)
	tmpl, err := template.ParseFiles(inFile)
	if err != nil {
		return err
	}

	if err := tmpl.Execute(w, values); err != nil {
		return err
	}

	if err := w.Flush(); err != nil {
		return err
	}

	if err := ioutil.WriteFile(outFile, bytes.Bytes(), 0644); err != nil {
		return err
	}
	log.Infof("Created %s from template %s", outFile, inFile)
	return nil
}

// CreateNamespace create a kubernetes namespace
func CreateNamespace(n string) error {
	if _, err := Shell("kubectl create namespace %s", n); err != nil {
		return err
	}
	log.Infof("namespace %s created\n", n)
	return nil
}

// DeleteNamespace delete a kubernetes namespace
func DeleteNamespace(n string) error {
	_, err := Shell("kubectl delete namespace %s", n)
	return err
}

// NamespaceDeleted check if a kubernete namespace is deleted
func NamespaceDeleted(n string) (bool, error) {
	output, err := Shell("kubectl get namespace %s", n)
	if strings.Contains(output, "NotFound") {
		log.Infof("namespace %s deleted\n", n)
		return true, nil
	}
	log.Infof("namespace %s not deleted yet\n", n)
	return false, err
}

// KubeApplyContents kubectl apply from contents
func KubeApplyContents(namespace, yamlContents string) error {
	tmpfile, err := WriteTempfile(os.TempDir(), "kubeapply", ".yaml", yamlContents)
	if err != nil {
		return err
	}
	defer removeFile(tmpfile)
	return KubeApply(namespace, tmpfile)
}

// KubeApply kubectl apply from file
func KubeApply(namespace, yamlFileName string) error {
	_, err := Shell("kubectl apply -n %s -f %s", namespace, yamlFileName)
	return err
}

// KubeDeleteContents kubectl apply from contents
func KubeDeleteContents(namespace, yamlContents string) error {
	tmpfile, err := WriteTempfile(os.TempDir(), "kubedelete", ".yaml", yamlContents)
	if err != nil {
		return err
	}
	defer removeFile(tmpfile)
	return KubeDelete(namespace, tmpfile)
}

func removeFile(path string) {
	err := os.Remove(path)
	if err != nil {
		log.Errorf("Unable to remove %s: %v", path, err)
	}
}

// KubeDelete kubectl delete from file
func KubeDelete(namespace, yamlFileName string) error {
	_, err := Shell("kubectl delete -n %s -f %s", namespace, yamlFileName)
	return err
}

// GetIngress get istio ingress ip
func GetIngress(n string) (string, error) {
	retry := Retrier{
		BaseDelay: 5 * time.Second,
		MaxDelay:  20 * time.Second,
		Retries:   20,
	}
	ri := regexp.MustCompile(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`)
	//rp := regexp.MustCompile(`^[0-9]{1,5}$`) # Uncomment for minikube
	var ingress string
	retryFn := func(i int) error {
		ip, err := Shell("kubectl get svc istio-ingress -n %s -o jsonpath='{.status.loadBalancer.ingress[*].ip}'", n)
		// For minikube, comment out the previous line and uncomment the following line
		//ip, err := Shell("kubectl get po -l istio=ingress -n %s -o jsonpath='{.items[0].status.hostIP}'", n)
		if err != nil {
			return err
		}
		ip = strings.Trim(ip, "'")
		if ri.FindString(ip) == "" {
			pods, _ := Shell("kubectl get all -n %s -o wide", n)
			err = fmt.Errorf("unable to find ingress ip, state:\n%s", pods)
			log.Warna(err)
			return err
		}
		ingress = ip
		// For minikube, comment out the previous line and uncomment the following lines
		//port, e := Shell("kubectl get svc istio-ingress -n %s -o jsonpath='{.spec.ports[0].nodePort}'", n)
		//if e != nil {
		//	return e
		//}
		//port = strings.Trim(port, "'")
		//if rp.FindString(port) == "" {
		//	err = fmt.Errorf("unable to find ingress port")
		//	log.Warn(err)
		//	return err
		//}
		//ingress = ip + ":" + port
		log.Infof("Istio ingress: %s\n", ingress)
		return nil
	}
	_, err := retry.Retry(retryFn)
	return ingress, err
}

// GetIngressPod get istio ingress ip
func GetIngressPod(n string) (string, error) {
	retry := Retrier{
		BaseDelay: 5 * time.Second,
		MaxDelay:  5 * time.Minute,
		Retries:   20,
	}
	ipRegex := regexp.MustCompile(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`)
	portRegex := regexp.MustCompile(`^[0-9]+$`)
	var ingress string
	retryFn := func(i int) error {
		podIP, err := Shell("kubectl get pod -l istio=ingress "+
			"-n %s -o jsonpath='{.items[0].status.hostIP}'", n)
		if err != nil {
			return err
		}
		podPort, err := Shell("kubectl get svc istio-ingress "+
			"-n %s -o jsonpath='{.spec.ports[0].nodePort}'", n)
		if err != nil {
			return err
		}
		podIP = strings.Trim(podIP, "'")
		podPort = strings.Trim(podPort, "'")
		if ipRegex.FindString(podIP) == "" {
			err = errors.New("unable to find ingress pod ip")
			log.Warna(err)
			return err
		}
		if portRegex.FindString(podPort) == "" {
			err = errors.New("unable to find ingress pod port")
			log.Warna(err)
			return err
		}
		ingress = fmt.Sprintf("%s:%s", podIP, podPort)
		log.Infof("Istio ingress: %s\n", ingress)
		return nil
	}
	_, err := retry.Retry(retryFn)
	return ingress, err
}

// GetPodsName gets names of all pods in specific namespace and return in a slice
func GetPodsName(n string) (pods []string) {
	res, err := Shell("kubectl -n %s get pods -o jsonpath='{.items[*].metadata.name}'", n)
	if err != nil {
		log.Infof("Failed to get pods name in namespace %s: %s", n, err)
		return
	}
	res = strings.Trim(res, "'")
	pods = strings.Split(res, " ")
	log.Infof("Existing pods: %v", pods)
	return
}

// GetPodStatus gets status of a pod from a namespace
func GetPodStatus(n, pod string) string {
	status, err := Shell("kubectl -n %s get pods %s -o jsonpath='{.status.phase}'", n, pod)
	if err != nil {
		log.Infof("Failed to get status of pod %s in namespace %s: %s", pod, n, err)
		status = podFailedGet
	}
	return strings.Trim(status, "'")
}

// CheckPodsRunning return if all pods in a namespace are in "Running" status
func CheckPodsRunning(n string) (ready bool) {
	retry := Retrier{
		BaseDelay: 30 * time.Second,
		MaxDelay:  30 * time.Second,
		Retries:   6,
	}

	retryFn := func(i int) error {
		pods := GetPodsName(n)
		ready = true
		for _, p := range pods {
			if status := GetPodStatus(n, p); status != podRunning {
				log.Infof("%s in namespace %s is not running: %s", p, n, status)
				if desc, err := Shell("kubectl describe pods -n %s %s", n, p); err != nil {
					log.Infof("Pod description: %s", desc)
				}
				ready = false
			}
		}
		if !ready {
			_, err := Shell("kubectl -n %s get pods -o wide", n)
			if err != nil {
				log.Infof("Cannot get pods: %s", err)
			}
			return fmt.Errorf("some pods are not ready")
		}
		return nil
	}
	_, err := retry.Retry(retryFn)
	if err != nil {
		return false
	}
	log.Info("Get all pods running!")
	return true
}
<commit_msg>Check container status when checking pod status. (#2987)<commit_after>// Copyright 2017 Istio 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 util

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
	"time"
	// TODO(nmittler): Remove this
	_ "github.com/golang/glog"

	"istio.io/istio/pkg/log"
)

const (
	podRunning   = "Running"
	podFailedGet = "Failed_Get"
	// The index of STATUS field in kubectl CLI output.
	statusField = 2
)

// Fill complete a template with given values and generate a new output file
func Fill(outFile, inFile string, values interface{}) error {
	var bytes bytes.Buffer
	w := bufio.NewWriter(&bytes)
	tmpl, err := template.ParseFiles(inFile)
	if err != nil {
		return err
	}

	if err := tmpl.Execute(w, values); err != nil {
		return err
	}

	if err := w.Flush(); err != nil {
		return err
	}

	if err := ioutil.WriteFile(outFile, bytes.Bytes(), 0644); err != nil {
		return err
	}
	log.Infof("Created %s from template %s", outFile, inFile)
	return nil
}

// CreateNamespace create a kubernetes namespace
func CreateNamespace(n string) error {
	if _, err := Shell("kubectl create namespace %s", n); err != nil {
		return err
	}
	log.Infof("namespace %s created\n", n)
	return nil
}

// DeleteNamespace delete a kubernetes namespace
func DeleteNamespace(n string) error {
	_, err := Shell("kubectl delete namespace %s", n)
	return err
}

// NamespaceDeleted check if a kubernete namespace is deleted
func NamespaceDeleted(n string) (bool, error) {
	output, err := Shell("kubectl get namespace %s", n)
	if strings.Contains(output, "NotFound") {
		log.Infof("namespace %s deleted\n", n)
		return true, nil
	}
	log.Infof("namespace %s not deleted yet\n", n)
	return false, err
}

// KubeApplyContents kubectl apply from contents
func KubeApplyContents(namespace, yamlContents string) error {
	tmpfile, err := WriteTempfile(os.TempDir(), "kubeapply", ".yaml", yamlContents)
	if err != nil {
		return err
	}
	defer removeFile(tmpfile)
	return KubeApply(namespace, tmpfile)
}

// KubeApply kubectl apply from file
func KubeApply(namespace, yamlFileName string) error {
	_, err := Shell("kubectl apply -n %s -f %s", namespace, yamlFileName)
	return err
}

// KubeDeleteContents kubectl apply from contents
func KubeDeleteContents(namespace, yamlContents string) error {
	tmpfile, err := WriteTempfile(os.TempDir(), "kubedelete", ".yaml", yamlContents)
	if err != nil {
		return err
	}
	defer removeFile(tmpfile)
	return KubeDelete(namespace, tmpfile)
}

func removeFile(path string) {
	err := os.Remove(path)
	if err != nil {
		log.Errorf("Unable to remove %s: %v", path, err)
	}
}

// KubeDelete kubectl delete from file
func KubeDelete(namespace, yamlFileName string) error {
	_, err := Shell("kubectl delete -n %s -f %s", namespace, yamlFileName)
	return err
}

// GetIngress get istio ingress ip
func GetIngress(n string) (string, error) {
	retry := Retrier{
		BaseDelay: 5 * time.Second,
		MaxDelay:  20 * time.Second,
		Retries:   20,
	}
	ri := regexp.MustCompile(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`)
	//rp := regexp.MustCompile(`^[0-9]{1,5}$`) # Uncomment for minikube
	var ingress string
	retryFn := func(i int) error {
		ip, err := Shell("kubectl get svc istio-ingress -n %s -o jsonpath='{.status.loadBalancer.ingress[*].ip}'", n)
		// For minikube, comment out the previous line and uncomment the following line
		//ip, err := Shell("kubectl get po -l istio=ingress -n %s -o jsonpath='{.items[0].status.hostIP}'", n)
		if err != nil {
			return err
		}
		ip = strings.Trim(ip, "'")
		if ri.FindString(ip) == "" {
			pods, _ := Shell("kubectl get all -n %s -o wide", n)
			err = fmt.Errorf("unable to find ingress ip, state:\n%s", pods)
			log.Warna(err)
			return err
		}
		ingress = ip
		// For minikube, comment out the previous line and uncomment the following lines
		//port, e := Shell("kubectl get svc istio-ingress -n %s -o jsonpath='{.spec.ports[0].nodePort}'", n)
		//if e != nil {
		//	return e
		//}
		//port = strings.Trim(port, "'")
		//if rp.FindString(port) == "" {
		//	err = fmt.Errorf("unable to find ingress port")
		//	log.Warn(err)
		//	return err
		//}
		//ingress = ip + ":" + port
		log.Infof("Istio ingress: %s\n", ingress)
		return nil
	}
	_, err := retry.Retry(retryFn)
	return ingress, err
}

// GetIngressPod get istio ingress ip
func GetIngressPod(n string) (string, error) {
	retry := Retrier{
		BaseDelay: 5 * time.Second,
		MaxDelay:  5 * time.Minute,
		Retries:   20,
	}
	ipRegex := regexp.MustCompile(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`)
	portRegex := regexp.MustCompile(`^[0-9]+$`)
	var ingress string
	retryFn := func(i int) error {
		podIP, err := Shell("kubectl get pod -l istio=ingress "+
			"-n %s -o jsonpath='{.items[0].status.hostIP}'", n)
		if err != nil {
			return err
		}
		podPort, err := Shell("kubectl get svc istio-ingress "+
			"-n %s -o jsonpath='{.spec.ports[0].nodePort}'", n)
		if err != nil {
			return err
		}
		podIP = strings.Trim(podIP, "'")
		podPort = strings.Trim(podPort, "'")
		if ipRegex.FindString(podIP) == "" {
			err = errors.New("unable to find ingress pod ip")
			log.Warna(err)
			return err
		}
		if portRegex.FindString(podPort) == "" {
			err = errors.New("unable to find ingress pod port")
			log.Warna(err)
			return err
		}
		ingress = fmt.Sprintf("%s:%s", podIP, podPort)
		log.Infof("Istio ingress: %s\n", ingress)
		return nil
	}
	_, err := retry.Retry(retryFn)
	return ingress, err
}

// GetPodsName gets names of all pods in specific namespace and return in a slice
func GetPodsName(n string) (pods []string) {
	res, err := Shell("kubectl -n %s get pods -o jsonpath='{.items[*].metadata.name}'", n)
	if err != nil {
		log.Infof("Failed to get pods name in namespace %s: %s", n, err)
		return
	}
	res = strings.Trim(res, "'")
	pods = strings.Split(res, " ")
	log.Infof("Existing pods: %v", pods)
	return
}

// GetPodStatus gets status of a pod from a namespace
// Note: It is not enough to check pod phase, which only implies there is at
// least one container running. Use kubectl CLI to get status so that we can
// ensure that all containers are running.
func GetPodStatus(n, pod string) string {
	status, err := Shell("kubectl -n %s get pods %s --no-headers", n, pod)
	if err != nil {
		log.Infof("Failed to get status of pod %s in namespace %s: %s", pod, n, err)
		status = podFailedGet
	}
	f := strings.Fields(status)
	if len(f) > statusField {
		return f[statusField]
	}
	return ""
}

// CheckPodsRunning return if all pods in a namespace are in "Running" status
// Also check container status to be running.
func CheckPodsRunning(n string) (ready bool) {
	retry := Retrier{
		BaseDelay: 30 * time.Second,
		MaxDelay:  30 * time.Second,
		Retries:   6,
	}

	retryFn := func(i int) error {
		pods := GetPodsName(n)
		ready = true
		for _, p := range pods {
			if status := GetPodStatus(n, p); status != podRunning {
				log.Infof("%s in namespace %s is not running: %s", p, n, status)
				if desc, err := Shell("kubectl describe pods -n %s %s", n, p); err != nil {
					log.Infof("Pod description: %s", desc)
				}
				ready = false
			}
		}
		if !ready {
			_, err := Shell("kubectl -n %s get pods -o wide", n)
			if err != nil {
				log.Infof("Cannot get pods: %s", err)
			}
			return fmt.Errorf("some pods are not ready")
		}
		return nil
	}
	_, err := retry.Retry(retryFn)
	if err != nil {
		return false
	}
	log.Info("Get all pods running!")
	return true
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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 xform

import (
	"gopkg.in/spacemonkeygo/dbx.v1/ast"
	"gopkg.in/spacemonkeygo/dbx.v1/ir"
)

func transformRead(lookup *lookup, ast_read *ast.Read) (
	reads []*ir.Read, err error) {

	tmpl := new(ir.Read)

	if ast_read.Select == nil || len(ast_read.Select.Refs) == 0 {
		return nil, Error.New("%s: no fields defined to select", ast_read.Pos)
	}

	// Figure out which models are needed for the fields and that the field
	// references aren't repetetive.
	selected := map[string]map[string]*ast.FieldRef{}
	for _, ast_fieldref := range ast_read.Select.Refs {
		fields := selected[ast_fieldref.Model]
		if fields == nil {
			fields = map[string]*ast.FieldRef{}
			selected[ast_fieldref.Model] = fields
		}

		existing := fields[""]
		if existing == nil {
			existing = fields[ast_fieldref.Field]
		}
		if existing != nil {
			return nil, Error.New(
				"%s: field %s already selected by field %s",
				ast_fieldref.Pos, ast_fieldref, existing)
		}
		fields[ast_fieldref.Field] = ast_fieldref

		if ast_fieldref.Field == "" {
			model, err := lookup.FindModel(ast_fieldref.ModelRef())
			if err != nil {
				return nil, err
			}
			tmpl.Selectables = append(tmpl.Selectables, model)
		} else {
			field, err := lookup.FindField(ast_fieldref)
			if err != nil {
				return nil, err
			}
			tmpl.Selectables = append(tmpl.Selectables, field)
		}
	}

	// Figure out set of models that are included in the read. These come from
	// explicit joins, or implicitly if there is only a single model referenced
	// in the fields.
	models := map[string]*ast.FieldRef{}
	if len(ast_read.Joins) > 0 {
		next := ast_read.Joins[0].Left.Model
		models[next] = ast_read.Joins[0].Left
		for _, join := range ast_read.Joins {
			left, err := lookup.FindField(join.Left)
			if err != nil {
				return nil, err
			}
			if join.Left.Model != next {
				return nil, Error.New(
					"%s: model order must be consistent; expected %q; got %q",
					join.Left.Pos, next, join.Left.Model)
			}
			right, err := lookup.FindField(join.Right)
			if err != nil {
				return nil, err
			}
			next = join.Right.Model
			tmpl.Joins = append(tmpl.Joins, &ir.Join{
				Type:  join.Type,
				Left:  left,
				Right: right,
			})
			if existing := models[join.Right.Model]; existing != nil {
				return nil, Error.New("%s: model %q already joined at %s",
					join.Right.Pos, join.Right.Model, existing.Pos)
			}
			models[join.Right.Model] = join.Right
		}
	}

	// The from is either
	// 1) the only table referenced in the select fields
	// 2) the left side of the first join
	switch {
	case len(selected) == 1:
		from, err := lookup.FindModel(ast_read.Select.Refs[0].ModelRef())
		if err != nil {
			return nil, err
		}
		tmpl.From = from
		models[from.Name] = ast_read.Select.Refs[0]
	case len(ast_read.Joins) > 0:
		tmpl.From = tmpl.Joins[0].Left.Model
	default:
		return nil, Error.New(
			"%s: cannot select from multiple models without a join",
			ast_read.Select.Pos)
	}

	// Make sure all of the fields are accounted for in the set of models
	for _, ast_fieldref := range ast_read.Select.Refs {
		if models[ast_fieldref.Model] == nil {
			return nil, Error.New(
				"%s: cannot select field/model %q; model %q is not joined",
				ast_fieldref.Pos, ast_fieldref, ast_fieldref.Model)
		}
	}

	// Finalize the where conditions and make sure referenced models are part
	// of the select.
	for _, ast_where := range ast_read.Where {
		left, err := lookup.FindField(ast_where.Left)
		if err != nil {
			return nil, err
		}
		if models[ast_where.Left.Model] == nil {
			return nil, Error.New(
				"%s: invalid where condition %q; model %q is not joined",
				ast_where.Pos, ast_where, ast_where.Left.Model)
		}

		var right *ir.Field
		if ast_where.Right != nil {
			right, err = lookup.FindField(ast_where.Right)
			if err != nil {
				return nil, err
			}
			if models[ast_where.Right.Model] == nil {
				return nil, Error.New(
					"%s: invalid where condition %q; model %q is not joined",
					ast_where.Pos, ast_where, ast_where.Right.Model)
			}
		}

		tmpl.Where = append(tmpl.Where, &ir.Where{
			Op:    ast_where.Op,
			Left:  left,
			Right: right,
		})
	}

	// Finalize OrderBy and make sure referenced fields are part of the select
	if ast_read.OrderBy != nil {
		fields, err := resolveFieldRefs(lookup, ast_read.OrderBy.Fields.Refs)
		if err != nil {
			return nil, err
		}
		for _, order_by_field := range ast_read.OrderBy.Fields.Refs {
			if models[order_by_field.Model] == nil {
				return nil, Error.New(
					"%s: invalid orderby field %q; model %q is not joined",
					order_by_field.Pos, order_by_field, order_by_field.Model)
			}
		}

		tmpl.OrderBy = &ir.OrderBy{
			Fields:     fields,
			Descending: ast_read.OrderBy.Descending,
		}
	}

	// Now emit one select per view type (or one for all if unspecified)
	view := ast_read.View
	if view == nil {
		view = &ast.View{
			All: true,
		}
	}

	addView := func(v ir.View) {
		read_copy := *tmpl
		read_copy.View = v
		reads = append(reads, &read_copy)
	}

	if view.All {
		// template is already sufficient for "all"
		addView(ir.All)
	}
	if view.Count {
		addView(ir.Count)
	}
	if view.Has {
		addView(ir.Has)
	}
	if view.LimitOffset {
		if tmpl.One() {
			return nil, Error.New("%s: cannot limit/offset unique select",
				view.Pos)
		}
		addView(ir.LimitOffset)
	}
	if view.Paged {
		if tmpl.OrderBy != nil {
			return nil, Error.New(
				"%s: cannot page on table %s with order by",
				view.Pos, tmpl.From)
		}
		if tmpl.From.BasicPrimaryKey() == nil {
			return nil, Error.New(
				"%s: cannot page on table %s with composite primary key",
				view.Pos, tmpl.From)
		}
		if tmpl.One() {
			return nil, Error.New("%s: cannot page unique select",
				view.Pos)
		}
		addView(ir.Paged)
	}

	return reads, nil
}
<commit_msg>fix read transform<commit_after>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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 xform

import (
	"gopkg.in/spacemonkeygo/dbx.v1/ast"
	"gopkg.in/spacemonkeygo/dbx.v1/ir"
)

func transformRead(lookup *lookup, ast_read *ast.Read) (
	reads []*ir.Read, err error) {

	tmpl := new(ir.Read)

	if ast_read.Select == nil || len(ast_read.Select.Refs) == 0 {
		return nil, Error.New("%s: no fields defined to select", ast_read.Pos)
	}

	// Figure out which models are needed for the fields and that the field
	// references aren't repetetive.
	selected := map[string]map[string]*ast.FieldRef{}
	for _, ast_fieldref := range ast_read.Select.Refs {
		fields := selected[ast_fieldref.Model]
		if fields == nil {
			fields = map[string]*ast.FieldRef{}
			selected[ast_fieldref.Model] = fields
		}

		existing := fields[""]
		if existing == nil {
			existing = fields[ast_fieldref.Field]
		}
		if existing != nil {
			return nil, Error.New(
				"%s: field %s already selected by field %s",
				ast_fieldref.Pos, ast_fieldref, existing)
		}
		fields[ast_fieldref.Field] = ast_fieldref

		if ast_fieldref.Field == "" {
			model, err := lookup.FindModel(ast_fieldref.ModelRef())
			if err != nil {
				return nil, err
			}
			tmpl.Selectables = append(tmpl.Selectables, model)
		} else {
			field, err := lookup.FindField(ast_fieldref)
			if err != nil {
				return nil, err
			}
			tmpl.Selectables = append(tmpl.Selectables, field)
		}
	}

	// Make sure models referenced in joins have continuity
	models := map[string]*ast.FieldRef{}
	switch {
	case len(selected) == 1:
		from, err := lookup.FindModel(ast_read.Select.Refs[0].ModelRef())
		if err != nil {
			return nil, err
		}
		tmpl.From = from
		if len(ast_read.Joins) == 0 {
			models[ast_read.Select.Refs[0].Model] = ast_read.Select.Refs[0]
		}
	case len(ast_read.Joins) == 0:
		return nil, Error.New(
			"%s: cannot select from multiple models without a join",
			ast_read.Select.Pos)
	}

	var next string
	for _, join := range ast_read.Joins {
		left, err := lookup.FindField(join.Left)
		if err != nil {
			return nil, err
		}
		right, err := lookup.FindField(join.Right)
		if err != nil {
			return nil, err
		}

		tmpl.Joins = append(tmpl.Joins, &ir.Join{
			Type:  join.Type,
			Left:  left,
			Right: right,
		})

		switch {
		case next == "":
			if existing := models[join.Left.Model]; existing != nil {
				return nil, Error.New("%s: model %q already joined at %s",
					join.Left.Pos, join.Left.Model, existing.Pos)
			}
			models[join.Left.Model] = join.Left
			tmpl.From = left.Model
		case next != join.Left.Model:
			return nil, Error.New(
				"%s: model order must have continuity; expected %q; got %q",
				join.Left.Pos, next, join.Left.Model)
		}

		if existing := models[join.Right.Model]; existing != nil {
			return nil, Error.New("%s: model %q already joined at %s",
				join.Right.Pos, join.Right.Model, existing.Pos)
		}
		models[join.Right.Model] = join.Right

		next = join.Right.Model
	}

	// Make sure all of the fields are accounted for in the set of models
	for _, ast_fieldref := range ast_read.Select.Refs {
		if models[ast_fieldref.Model] == nil {
			return nil, Error.New(
				"%s: cannot select field/model %q; model %q is not joined",
				ast_fieldref.Pos, ast_fieldref, ast_fieldref.Model)
		}
	}

	// Finalize the where conditions and make sure referenced models are part
	// of the select.
	for _, ast_where := range ast_read.Where {
		left, err := lookup.FindField(ast_where.Left)
		if err != nil {
			return nil, err
		}
		if models[ast_where.Left.Model] == nil {
			return nil, Error.New(
				"%s: invalid where condition %q; model %q is not joined",
				ast_where.Pos, ast_where, ast_where.Left.Model)
		}

		var right *ir.Field
		if ast_where.Right != nil {
			right, err = lookup.FindField(ast_where.Right)
			if err != nil {
				return nil, err
			}
			if models[ast_where.Right.Model] == nil {
				return nil, Error.New(
					"%s: invalid where condition %q; model %q is not joined",
					ast_where.Pos, ast_where, ast_where.Right.Model)
			}
		}

		tmpl.Where = append(tmpl.Where, &ir.Where{
			Op:    ast_where.Op,
			Left:  left,
			Right: right,
		})
	}

	// Finalize OrderBy and make sure referenced fields are part of the select
	if ast_read.OrderBy != nil {
		fields, err := resolveFieldRefs(lookup, ast_read.OrderBy.Fields.Refs)
		if err != nil {
			return nil, err
		}
		for _, order_by_field := range ast_read.OrderBy.Fields.Refs {
			if models[order_by_field.Model] == nil {
				return nil, Error.New(
					"%s: invalid orderby field %q; model %q is not joined",
					order_by_field.Pos, order_by_field, order_by_field.Model)
			}
		}

		tmpl.OrderBy = &ir.OrderBy{
			Fields:     fields,
			Descending: ast_read.OrderBy.Descending,
		}
	}

	// Now emit one select per view type (or one for all if unspecified)
	view := ast_read.View
	if view == nil {
		view = &ast.View{
			All: true,
		}
	}

	addView := func(v ir.View) {
		read_copy := *tmpl
		read_copy.View = v
		reads = append(reads, &read_copy)
	}

	if view.All {
		// template is already sufficient for "all"
		addView(ir.All)
	}
	if view.Count {
		addView(ir.Count)
	}
	if view.Has {
		addView(ir.Has)
	}
	if view.LimitOffset {
		if tmpl.One() {
			return nil, Error.New("%s: cannot limit/offset unique select",
				view.Pos)
		}
		addView(ir.LimitOffset)
	}
	if view.Paged {
		if tmpl.OrderBy != nil {
			return nil, Error.New(
				"%s: cannot page on table %s with order by",
				view.Pos, tmpl.From)
		}
		if tmpl.From.BasicPrimaryKey() == nil {
			return nil, Error.New(
				"%s: cannot page on table %s with composite primary key",
				view.Pos, tmpl.From)
		}
		if tmpl.One() {
			return nil, Error.New("%s: cannot page unique select",
				view.Pos)
		}
		addView(ir.Paged)
	}

	return reads, nil
}
<|endoftext|>


<commit_before>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package ircbnc

import (
	"database/sql"
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/DanielOaks/girc-go/client"
	"github.com/DanielOaks/girc-go/eventmgr"
	"github.com/DanielOaks/girc-go/ircfmt"
	"github.com/DanielOaks/girc-go/ircmsg"
)

// ServerConnectionAddress represents an address a ServerConnection can join.
type ServerConnectionAddress struct {
	Address string
	Port    int
	UseTLS  bool
}

// ServerConnection represents a connection to an IRC server.
type ServerConnection struct {
	Name      string
	User      User
	Connected bool

	Nickname   string
	FbNickname string
	Username   string
	Realname   string
	Channels   map[string]string

	storingConnectMessages bool
	connectMessages        []ircmsg.IrcMessage
	Listeners              []Listener

	Password  string
	Addresses []ServerConnectionAddress
}

// LoadServerConnection loads the given server connection from our database.
func LoadServerConnection(name string, user User, db *sql.DB) (*ServerConnection, error) {
	var sc ServerConnection
	sc.Name = name
	sc.User = user

	row := db.QueryRow(`SELECT nickname, fallback_nickname, username, realname, password FROM server_connections WHERE user_id = ? AND name = ?`,
		user.ID, name)
	err := row.Scan(&sc.Nickname, &sc.FbNickname, &sc.Username, &sc.Realname, &sc.Password)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading sc details from db): %s", err.Error())
	}

	// set default values
	if sc.Nickname == "" {
		sc.Nickname = user.DefaultNick
	}
	if sc.FbNickname == "" {
		sc.FbNickname = user.DefaultFbNick
	}
	if sc.Username == "" {
		sc.Username = user.DefaultUser
	}
	if sc.Realname == "" {
		sc.Realname = user.DefaultReal
	}

	// load channels
	sc.Channels = make(map[string]string)
	rows, err := db.Query(`SELECT name, key FROM server_connection_channels WHERE user_id = ? AND sc_name = ?`,
		user.ID, name)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading address details from db): %s", err.Error())
	}
	for rows.Next() {
		var name, key string
		rows.Scan(&name, &key)

		sc.Channels[name] = key
	}

	// load addresses
	rows, err = db.Query(`SELECT address, port, use_tls FROM server_connection_addresses WHERE user_id = ? AND sc_name = ?`,
		user.ID, name)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading address details from db): %s", err.Error())
	}
	for rows.Next() {
		var address, portString string
		var useTLS bool

		rows.Scan(&address, &portString, &useTLS)

		port, err := strconv.Atoi(portString)
		if err != nil {
			return nil, fmt.Errorf("Could not create new ServerConnection (port did not load correctly): %s", err.Error())
		} else if port < 1 || port > 65535 {
			return nil, fmt.Errorf("Could not create new ServerConnection (port %d is not valid)", port)
		}

		var newAddress ServerConnectionAddress
		newAddress.Address = address
		newAddress.Port = port
		newAddress.UseTLS = useTLS
		sc.Addresses = append(sc.Addresses, newAddress)
	}

	return &sc, nil
}

var storedConnectLines = map[string]bool{
	"001": true,
	"002": true,
	"003": true,
	"004": true,
	"005": true,
	"250": true,
	"251": true,
	"252": true,
	"254": true,
	"255": true,
	"265": true,
	"266": true,
	"372": true,
	"375": true,
	"376": true,
	"422": true,
}

// connectLinesHandler extracts and stores the connection lines.
func (sc *ServerConnection) connectLinesHandler(event string, info eventmgr.InfoMap) {
	if !sc.storingConnectMessages {
		return
	}

	line := info["data"].(string)
	message, err := ircmsg.ParseLine(line)
	if err != nil {
		return
	}

	_, storeMessage := storedConnectLines[message.Command]
	if storeMessage {
		// fmt.Println("IN:", message)
		sc.connectMessages = append(sc.connectMessages, message)
	}

	if message.Command == "376" {
		sc.storingConnectMessages = false
	}
}

// DumpRegistration dumps the registration messages of this server to the given Listener.
func (sc *ServerConnection) DumpRegistration(listener *Listener) {
	listener.Send(nil, listener.Bouncer.StatusSource, "NOTICE", listener.ClientNick, "We should be dumping the startup info from the serverconnection!")
	for _, message := range sc.connectMessages {
		message.Params[0] = listener.ClientNick
		listener.Send(&message.Tags, message.Prefix, message.Command, message.Params...)
	}
}

// rawHandler prints raw messages to and from the server.
//TODO(dan): This is only VERY INITIAL, for use while we are debugging.
func rawHandler(event string, info eventmgr.InfoMap) {
	server := info["server"].(*gircclient.ServerConnection)
	direction := info["direction"].(string)
	line := info["data"].(string)

	var arrow string
	if direction == "in" {
		arrow = "<- "
	} else {
		arrow = " ->"
	}

	fmt.Println(server.Name, arrow, ircfmt.Escape(strings.Trim(line, "\r\n")))
}

// Start opens and starts connecting to the server.
func (sc *ServerConnection) Start(reactor gircclient.Reactor) {
	name := fmt.Sprintf("%s %s", sc.User.ID, sc.Name)
	server := reactor.CreateServer(name)

	server.InitialNick = sc.Nickname
	server.InitialUser = sc.Username
	server.InitialRealName = sc.Realname
	server.ConnectionPass = sc.Password
	server.FallbackNicks = append(server.FallbackNicks, sc.FbNickname)

	server.RegisterEvent("in", "raw", sc.connectLinesHandler, 0)
	server.RegisterEvent("in", "raw", rawHandler, 0)
	server.RegisterEvent("out", "raw", rawHandler, 0)

	var err error
	for _, address := range sc.Addresses {
		fullAddress := net.JoinHostPort(address.Address, strconv.Itoa(address.Port))

		err = server.Connect(fullAddress, address.UseTLS, nil)
		if err == nil {
			break
		}
	}

	if err != nil {
		fmt.Println("ERROR: Could not connect to", name, err.Error())
		return
	}

	go server.ReceiveLoop()
}
<commit_msg>sc: Also stop pulling info on ERR_NOMOTD<commit_after>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package ircbnc

import (
	"database/sql"
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/DanielOaks/girc-go/client"
	"github.com/DanielOaks/girc-go/eventmgr"
	"github.com/DanielOaks/girc-go/ircfmt"
	"github.com/DanielOaks/girc-go/ircmsg"
)

// ServerConnectionAddress represents an address a ServerConnection can join.
type ServerConnectionAddress struct {
	Address string
	Port    int
	UseTLS  bool
}

// ServerConnection represents a connection to an IRC server.
type ServerConnection struct {
	Name      string
	User      User
	Connected bool

	Nickname   string
	FbNickname string
	Username   string
	Realname   string
	Channels   map[string]string

	storingConnectMessages bool
	connectMessages        []ircmsg.IrcMessage
	Listeners              []Listener

	Password  string
	Addresses []ServerConnectionAddress
}

// LoadServerConnection loads the given server connection from our database.
func LoadServerConnection(name string, user User, db *sql.DB) (*ServerConnection, error) {
	var sc ServerConnection
	sc.Name = name
	sc.User = user

	row := db.QueryRow(`SELECT nickname, fallback_nickname, username, realname, password FROM server_connections WHERE user_id = ? AND name = ?`,
		user.ID, name)
	err := row.Scan(&sc.Nickname, &sc.FbNickname, &sc.Username, &sc.Realname, &sc.Password)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading sc details from db): %s", err.Error())
	}

	// set default values
	if sc.Nickname == "" {
		sc.Nickname = user.DefaultNick
	}
	if sc.FbNickname == "" {
		sc.FbNickname = user.DefaultFbNick
	}
	if sc.Username == "" {
		sc.Username = user.DefaultUser
	}
	if sc.Realname == "" {
		sc.Realname = user.DefaultReal
	}

	// load channels
	sc.Channels = make(map[string]string)
	rows, err := db.Query(`SELECT name, key FROM server_connection_channels WHERE user_id = ? AND sc_name = ?`,
		user.ID, name)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading address details from db): %s", err.Error())
	}
	for rows.Next() {
		var name, key string
		rows.Scan(&name, &key)

		sc.Channels[name] = key
	}

	// load addresses
	rows, err = db.Query(`SELECT address, port, use_tls FROM server_connection_addresses WHERE user_id = ? AND sc_name = ?`,
		user.ID, name)
	if err != nil {
		return nil, fmt.Errorf("Could not create new ServerConnection (loading address details from db): %s", err.Error())
	}
	for rows.Next() {
		var address, portString string
		var useTLS bool

		rows.Scan(&address, &portString, &useTLS)

		port, err := strconv.Atoi(portString)
		if err != nil {
			return nil, fmt.Errorf("Could not create new ServerConnection (port did not load correctly): %s", err.Error())
		} else if port < 1 || port > 65535 {
			return nil, fmt.Errorf("Could not create new ServerConnection (port %d is not valid)", port)
		}

		var newAddress ServerConnectionAddress
		newAddress.Address = address
		newAddress.Port = port
		newAddress.UseTLS = useTLS
		sc.Addresses = append(sc.Addresses, newAddress)
	}

	return &sc, nil
}

var storedConnectLines = map[string]bool{
	"001": true,
	"002": true,
	"003": true,
	"004": true,
	"005": true,
	"250": true,
	"251": true,
	"252": true,
	"254": true,
	"255": true,
	"265": true,
	"266": true,
	"372": true,
	"375": true,
	"376": true,
	"422": true,
}

// connectLinesHandler extracts and stores the connection lines.
func (sc *ServerConnection) connectLinesHandler(event string, info eventmgr.InfoMap) {
	if !sc.storingConnectMessages {
		return
	}

	line := info["data"].(string)
	message, err := ircmsg.ParseLine(line)
	if err != nil {
		return
	}

	_, storeMessage := storedConnectLines[message.Command]
	if storeMessage {
		// fmt.Println("IN:", message)
		sc.connectMessages = append(sc.connectMessages, message)
	}

	if message.Command == "376" || message.Command == "422" {
		sc.storingConnectMessages = false
	}
}

// DumpRegistration dumps the registration messages of this server to the given Listener.
func (sc *ServerConnection) DumpRegistration(listener *Listener) {
	for _, message := range sc.connectMessages {
		message.Params[0] = listener.ClientNick
		listener.Send(&message.Tags, message.Prefix, message.Command, message.Params...)
	}
}

// rawHandler prints raw messages to and from the server.
//TODO(dan): This is only VERY INITIAL, for use while we are debugging.
func rawHandler(event string, info eventmgr.InfoMap) {
	server := info["server"].(*gircclient.ServerConnection)
	direction := info["direction"].(string)
	line := info["data"].(string)

	var arrow string
	if direction == "in" {
		arrow = "<- "
	} else {
		arrow = " ->"
	}

	fmt.Println(server.Name, arrow, ircfmt.Escape(strings.Trim(line, "\r\n")))
}

// Start opens and starts connecting to the server.
func (sc *ServerConnection) Start(reactor gircclient.Reactor) {
	name := fmt.Sprintf("%s %s", sc.User.ID, sc.Name)
	server := reactor.CreateServer(name)

	server.InitialNick = sc.Nickname
	server.InitialUser = sc.Username
	server.InitialRealName = sc.Realname
	server.ConnectionPass = sc.Password
	server.FallbackNicks = append(server.FallbackNicks, sc.FbNickname)

	server.RegisterEvent("in", "raw", sc.connectLinesHandler, 0)
	server.RegisterEvent("in", "raw", rawHandler, 0)
	server.RegisterEvent("out", "raw", rawHandler, 0)

	var err error
	for _, address := range sc.Addresses {
		fullAddress := net.JoinHostPort(address.Address, strconv.Itoa(address.Port))

		err = server.Connect(fullAddress, address.UseTLS, nil)
		if err == nil {
			break
		}
	}

	if err != nil {
		fmt.Println("ERROR: Could not connect to", name, err.Error())
		return
	}

	go server.ReceiveLoop()
}
<|endoftext|>