<commit_before>// Package leaderboard
package leaderboard

import (
	"errors"
	"sort"
)

type Player struct {
	Username string
	Team     struct {
		R float32
		G float32
		B float32
	}
	HomePlanet string
	Planets    uint32
}

type Leaderboard struct {
	board  []*Player
	places map[string]int
}

func New() *Leaderboard {
	l := new(Leaderboard)
	l.places = make(map[string]int)
	l.board = make([]*Player, 0)
	return l
}

func (l *Leaderboard) Add(player *Player) {
	l.board = append(l.board, player)
	l.places[player.Username] = len(l.board) - 1
}

func (l *Leaderboard) Len() int {
	return len(l.board)
}

func (l *Leaderboard) Swap(i, j int) {
	l.board[i], l.board[j] = l.board[j], l.board[i]
}

func (l *Leaderboard) Less(i, j int) bool {
	return l.board[i].Planets > l.board[j].Planets
}

func (l *Leaderboard) Sort() {
	sort.Sort(l)
}

func (l *Leaderboard) Transfer(from_username, to_username string) {
	from, hasOwner := l.places[from_username]
	to := l.places[to_username]

	if hasOwner {
		l.board[from].Planets--
		l.moveDown(from_username)
	}

	l.board[to].Planets++
	l.moveUp(to_username)
}

func (l *Leaderboard) Page(page int64) ([]*Player, error) {
	from := (page - 1) * 10
	to := ((page - 1) * 10) + 10
	if len(l.board) < int(from) {
		return []*Player{}, errors.New("Not such page")
	}

	if len(l.board) < int(to) {
		to = int64(len(l.board))
	}
	return l.board[from:to], nil
}

func (l *Leaderboard) move(username string, modificator int) bool {
	firstBlood := true
	isMoved := false
	i := l.places[username]

	for isMoved || firstBlood {
		firstBlood = false
		if modificator < 0 && i == 0 || modificator > 0 && i == len(l.board)-1 {
			return false
		}

		if modificator < 0 {
			isMoved = l.Less(i, i+modificator)
		} else {
			isMoved = l.Less(i+modificator, i)
		}

		if isMoved {
			l.Swap(i, i+modificator)
			l.places[l.board[i].Username], l.places[l.board[i+modificator].Username] = i+modificator, i
		}
		i += modificator
	}
	return isMoved
}

func (l *Leaderboard) moveUp(username string) bool {
	return l.move(username, -1)
}

func (l *Leaderboard) moveDown(username string) bool {
	return l.move(username, 1)
}
<commit_msg>Add Leaderboard.Place and fix places on sort<commit_after>// Package leaderboard
package leaderboard

import (
	"errors"
	"sort"
)

type Player struct {
	Username string
	Team     struct {
		R float32
		G float32
		B float32
	}
	HomePlanet string
	Planets    uint32
}

type Leaderboard struct {
	board  []*Player
	places map[string]int
}

func New() *Leaderboard {
	l := new(Leaderboard)
	l.places = make(map[string]int)
	l.board = make([]*Player, 0)
	return l
}

func (l *Leaderboard) Add(player *Player) {
	l.board = append(l.board, player)
	l.places[player.Username] = len(l.board) - 1
}

func (l *Leaderboard) Len() int {
	return len(l.board)
}

func (l *Leaderboard) Swap(i, j int) {
	l.board[i], l.board[j] = l.board[j], l.board[i]
}

func (l *Leaderboard) Less(i, j int) bool {
	return l.board[i].Planets > l.board[j].Planets
}

func (l *Leaderboard) Sort() {
	sort.Sort(l)
	for index, player := range l.board {
		l.places[player.Username] = index + 1
	}
}

func (l *Leaderboard) Transfer(from_username, to_username string) {
	from, hasOwner := l.places[from_username]
	to := l.places[to_username]

	if hasOwner {
		l.board[from].Planets--
		l.moveDown(from_username)
	}

	l.board[to].Planets++
	l.moveUp(to_username)
}

func (l *Leaderboard) Page(page int64) ([]*Player, error) {
	from := (page - 1) * 10
	to := ((page - 1) * 10) + 10
	if len(l.board) < int(from) {
		return []*Player{}, errors.New("Not such page")
	}

	if len(l.board) < int(to) {
		to = int64(len(l.board))
	}
	return l.board[from:to], nil
}

func (l *Leaderboard) Place(username string) int {
	place, ok := l.places[username]
	if !ok {
		return 0
	}

	return place
}

func (l *Leaderboard) move(username string, modificator int) bool {
	firstBlood := true
	isMoved := false
	i := l.places[username]

	for isMoved || firstBlood {
		firstBlood = false
		if modificator < 0 && i == 0 || modificator > 0 && i == len(l.board)-1 {
			return false
		}

		if modificator < 0 {
			isMoved = l.Less(i, i+modificator)
		} else {
			isMoved = l.Less(i+modificator, i)
		}

		if isMoved {
			l.Swap(i, i+modificator)
			l.places[l.board[i].Username], l.places[l.board[i+modificator].Username] = i+modificator, i
		}
		i += modificator
	}
	return isMoved
}

func (l *Leaderboard) moveUp(username string) bool {
	return l.move(username, -1)
}

func (l *Leaderboard) moveDown(username string) bool {
	return l.move(username, 1)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type Vertex struct {
	X, Y float64
}

/**给stuct 定义方法*/
func (v *Vertex) abs() float64 {
	return v.X + v.Y
}
type mystring string
func (f mystring) Abs() mystring {

	return f
}

func main() {
	fmt.Println("hello--->>")
	var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
	for index, ok := range pow {
		fmt.Println(index, ok)
	}
	m := make(map[string]Vertex)
	m["baijing"] = Vertex{40.68433, -74.39967}
	m["jingsu"] = Vertex{40.68433, -74.39967}
	m["haiNan"] = Vertex{40.68433, -74.39967}
	m["tianJing"] = Vertex{40.68433, -74.39967}
	fmt.Println(m)
	delete(m, "baijing")
	fmt.Println(m)
	m1, has := m["haiNan"]
	fmt.Println(m1, has)
	fmt.Println(m1.abs())

	f :=mystring("hello")
	fmt.Println(f.Abs())
}

<commit_msg>update v<commit_after>package main

import (
	"fmt"
)

type Vertex struct {
	X, Y float64
}

/**给stuct 定义方法*/
func (v *Vertex) abs() float64 {
	return v.X + v.Y
}
type mystring string

func (f mystring) Abs() mystring {
	return f
}

func main() {
	fmt.Println("hello--->>")
	var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
	for index, ok := range pow {
		fmt.Println(index, ok)
	}
	m := make(map[string]Vertex)
	m["baijing"] = Vertex{40.68433, -74.39967}
	m["jingsu"] = Vertex{40.68433, -74.39967}
	m["haiNan"] = Vertex{40.68433, -74.39967}
	m["tianJing"] = Vertex{40.68433, -74.39967}
	fmt.Println(m)
	delete(m, "baijing")
	fmt.Println(m)
	m1, has := m["haiNan"]
	fmt.Println(m1, has)
	fmt.Println(m1.abs())

	f :=mystring("hello")
	fmt.Println(f.Abs())
}

<|endoftext|>


<commit_before>package text

import "strings"

type Mutation interface {
	Apply(text string) string
}

type Replace struct {
	Replacement string
}

type Sub struct {
	Search string
	Sub    string
}

type SubWord struct {
	Search Word
	Sub    string
}

type Append struct {
	Appendix string
}

type Prepend struct {
	Prefix string
}

func (r *Replace) Apply(text string) string {
	return r.Replacement
}

func (s *Sub) Apply(text string) string {
	return strings.Replace(text, s.Search, s.Sub, -1)
}

func (s *SubWord) Apply(text string) string {
	if text == s.Search.W {
		return s.Sub
	}

	for {
		i := s.Search.Match(text)

		if i < 0 {
			return text
		}

		beginning := text[:i]
		end := text[i+len(s.Search.W):]

		text = beginning + s.Sub + end
	}

}

func (am *Append) Apply(text string) string {
	if len(am.Appendix) == 0 {
		return text
	}

	return text + am.Appendix
}

func (pm *Prepend) Apply(text string) string {
	if len(pm.Prefix) == 0 {
		return text
	}

	return pm.Prefix + text
}
<commit_msg>Improve variable naming<commit_after>package text

import "strings"

type Mutation interface {
	Apply(text string) string
}

type Replace struct {
	Replacement string
}

type Sub struct {
	Search string
	Sub    string
}

type SubWord struct {
	Search Word
	Sub    string
}

type Append struct {
	Appendix string
}

type Prepend struct {
	Prefix string
}

func (r *Replace) Apply(text string) string {
	return r.Replacement
}

func (s *Sub) Apply(text string) string {
	return strings.Replace(text, s.Search, s.Sub, -1)
}

func (s *SubWord) Apply(text string) string {
	if text == s.Search.W {
		return s.Sub
	}

	for {
		i := s.Search.Match(text)

		if i < 0 {
			return text
		}

		beginning := text[:i]
		end := text[i+len(s.Search.W):]

		text = beginning + s.Sub + end
	}

}

func (a *Append) Apply(text string) string {
	if len(a.Appendix) == 0 {
		return text
	}

	return text + a.Appendix
}

func (p *Prepend) Apply(text string) string {
	if len(p.Prefix) == 0 {
		return text
	}

	return p.Prefix + text
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func TestAccAWSInstance_normal(t *testing.T) {
	var v ec2.Instance

	testCheck := func(*terraform.State) error {
		if len(v.SecurityGroups) == 0 {
			return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
		}
		if v.SecurityGroups[0].Name != "tf_test_foo" {
			return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
		}

		return nil
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck,
					resource.TestCheckResourceAttr(
						"aws_instance.foo",
						"user_data",
						"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
				),
			},

			// We repeat the exact same test so that we can be sure
			// that the user data hash stuff is working without generating
			// an incorrect diff.
			resource.TestStep{
				Config: testAccInstanceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck,
					resource.TestCheckResourceAttr(
						"aws_instance.foo",
						"user_data",
						"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
				),
			},
		},
	})
}

func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
	var v ec2.Instance

	testCheck := func(enabled bool) resource.TestCheckFunc {
		return func(*terraform.State) error {
			if v.SourceDestCheck != enabled {
				return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck)
			}

			return nil
		}
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfigSourceDest,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck(true),
				),
			},

			resource.TestStep{
				Config: testAccInstanceConfigSourceDestDisable,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck(false),
				),
			},
		},
	})
}

func TestAccAWSInstance_vpc(t *testing.T) {
	var v ec2.Instance

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfigVPC,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
				),
			},
		},
	})
}

func testAccCheckInstanceDestroy(s *terraform.State) error {
	conn := testAccProvider.ec2conn

	for _, rs := range s.Resources {
		if rs.Type != "aws_instance" {
			continue
		}

		// Try to find the resource
		resp, err := conn.Instances(
			[]string{rs.ID}, ec2.NewFilter())
		if err == nil {
			if len(resp.Reservations) > 0 {
				return fmt.Errorf("still exist.")
			}

			return nil
		}

		// Verify the error is what we want
		ec2err, ok := err.(*ec2.Error)
		if !ok {
			return err
		}
		if ec2err.Code != "InvalidInstanceID.NotFound" {
			return err
		}
	}

	return nil
}

func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		conn := testAccProvider.ec2conn
		resp, err := conn.Instances(
			[]string{rs.ID}, ec2.NewFilter())
		if err != nil {
			return err
		}
		if len(resp.Reservations) == 0 {
			return fmt.Errorf("Instance not found")
		}

		*i = resp.Reservations[0].Instances[0]

		return nil
	}
}

const testAccInstanceConfig = `
resource "aws_security_group" "tf_test_foo" {
	name = "tf_test_foo"
	description = "foo"

	ingress {
		protocol = "icmp"
		from_port = -1
		to_port = -1
		cidr_blocks = ["0.0.0.0/0"]
	}
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	security_groups = ["${aws_security_group.tf_test_foo.name}"]
	user_data = "foo"
}
`

const testAccInstanceConfigSourceDest = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	source_dest_check = true
}
`

const testAccInstanceConfigSourceDestDisable = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	source_dest_check = false
}
`

const testAccInstanceConfigVPC = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	associate_public_ip_address = true
}
`
<commit_msg>providers/aws: add tests for instance availability zone<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func TestAccAWSInstance_normal(t *testing.T) {
	var v ec2.Instance

	testCheck := func(*terraform.State) error {
		if v.AvailZone != "us-west-2a" {
			return fmt.Errorf("bad availability zone: %#v", v.AvailZone)
		}

		if len(v.SecurityGroups) == 0 {
			return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
		}
		if v.SecurityGroups[0].Name != "tf_test_foo" {
			return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
		}

		return nil
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck,
					resource.TestCheckResourceAttr(
						"aws_instance.foo",
						"user_data",
						"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
				),
			},

			// We repeat the exact same test so that we can be sure
			// that the user data hash stuff is working without generating
			// an incorrect diff.
			resource.TestStep{
				Config: testAccInstanceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck,
					resource.TestCheckResourceAttr(
						"aws_instance.foo",
						"user_data",
						"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
				),
			},
		},
	})
}

func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
	var v ec2.Instance

	testCheck := func(enabled bool) resource.TestCheckFunc {
		return func(*terraform.State) error {
			if v.SourceDestCheck != enabled {
				return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck)
			}

			return nil
		}
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfigSourceDest,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck(true),
				),
			},

			resource.TestStep{
				Config: testAccInstanceConfigSourceDestDisable,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
					testCheck(false),
				),
			},
		},
	})
}

func TestAccAWSInstance_vpc(t *testing.T) {
	var v ec2.Instance

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckInstanceDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccInstanceConfigVPC,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists(
						"aws_instance.foo", &v),
				),
			},
		},
	})
}

func testAccCheckInstanceDestroy(s *terraform.State) error {
	conn := testAccProvider.ec2conn

	for _, rs := range s.Resources {
		if rs.Type != "aws_instance" {
			continue
		}

		// Try to find the resource
		resp, err := conn.Instances(
			[]string{rs.ID}, ec2.NewFilter())
		if err == nil {
			if len(resp.Reservations) > 0 {
				return fmt.Errorf("still exist.")
			}

			return nil
		}

		// Verify the error is what we want
		ec2err, ok := err.(*ec2.Error)
		if !ok {
			return err
		}
		if ec2err.Code != "InvalidInstanceID.NotFound" {
			return err
		}
	}

	return nil
}

func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		conn := testAccProvider.ec2conn
		resp, err := conn.Instances(
			[]string{rs.ID}, ec2.NewFilter())
		if err != nil {
			return err
		}
		if len(resp.Reservations) == 0 {
			return fmt.Errorf("Instance not found")
		}

		*i = resp.Reservations[0].Instances[0]

		return nil
	}
}

const testAccInstanceConfig = `
resource "aws_security_group" "tf_test_foo" {
	name = "tf_test_foo"
	description = "foo"

	ingress {
		protocol = "icmp"
		from_port = -1
		to_port = -1
		cidr_blocks = ["0.0.0.0/0"]
	}
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	availability_zone = "us-west-2a"

	instance_type = "m1.small"
	security_groups = ["${aws_security_group.tf_test_foo.name}"]
	user_data = "foo"
}
`

const testAccInstanceConfigSourceDest = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	source_dest_check = true
}
`

const testAccInstanceConfigSourceDestDisable = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	source_dest_check = false
}
`

const testAccInstanceConfigVPC = `
resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "foo" {
	# us-west-2
	ami = "ami-4fccb37f"
	instance_type = "m1.small"
	subnet_id = "${aws_subnet.foo.id}"
	associate_public_ip_address = true
}
`
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"os/exec"
	"runtime"
	log "github.com/Sirupsen/logrus"
)

// StructuredOuput ...
type StructuredOuput struct {
	planet       string
	output       string
	maxOutLength int
}

func main() {
	opts := Opts{}
	opts.procArgs(os.Args)

	if opts.helpFlag {
		printUsage()
		os.Exit(0)
	}
	if opts.versionFlag {
		printVersion()
		os.Exit(0)
	}

	level := log.InfoLevel
	if opts.debugFlag {
		level = log.DebugLevel
	}
	// Default logfile path
	logDir := path.Join(os.Getenv("ORBIT_HOME"), "logs")
	createLogDirIfNecessary(logDir)
	logFile := path.Join(logDir, "ski.log")
	setupLogger(logFile, level)

	log.Infof("Started with args: %v", os.Args)
	log.Debug(&opts)
	exec := makeExecutor(&opts)
	exec.execMain(&opts)
	log.Infof("Ended with args: %v", os.Args)
}

func createLogDirIfNecessary(dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0775|os.ModeDir); err != nil {
			// can't do anything
			os.Stderr.WriteString(fmt.Sprintf("%v", err))
		}
	}
}

func makeExecutor(opts *Opts) Executor {
	executor := Executor{}
	var planet Planet
	for _, entry := range opts.planets {
		var user, host, dbID, connDet string
		dbID, connDet = getFullConnectionDetails(entry)
		planetType := getType(entry)
		planet.outputStruct.planet = entry
		id := entry
		if isSupported(entry) {
			user, host = getUserAndHost(connDet)
		} else {
			continue
		}
		executor.planets = append(executor.planets, Planet{id, user, host, planetType, dbID, StructuredOuput{id, "", 0}})
	}
	return executor
}

/**
*	Prints the current Version of the ski application
 */
func printVersion() {
	runtimeOS := getOS()
	progArch := getArch()
	archOS := getOSArch()
	version := fmt.Sprintf("ski version %s %s %s (%s)", version, progArch, runtimeOS, archOS)
	os.Stdout.WriteString(version + "\n")
}

/**
*	Prints the help dialog
 */
func printUsage() {
	usage := `usage: ski [options...] <planets>...
	Options:
	-s="<scriptname>"   Execute script and return result
	-c="<command>"      Execute script and return result
	-t=<"templatename>" Templatefile to be applied
	-p    Pretty print output as a table
	-l    Load bash profiles on Server
	-h    Display this help text
	-v    Show version number
	-d    Show extended debug informations, set logging level to debug
`
	fmt.Println(usage)
}

func getOS() string {
	switch runtime.GOOS {
	case "windows":
		return "Windows"
	case "linux":
		return "Linux"
	case "darwin":
		return "MacOS"
	default:
		return "could not determine OS"
	}
}

func getArch() string {
	switch runtime.GOARCH {
	case "amd64":
		return "64bit"
	case "386":
		return "32bit"
	default:
		return "could not determine architecture"
	}
}

func getOSArch() string {
	switch runtime.GOOS {
	case "linux":
		out, err := exec.Command("uname", "-m").Output()
		if err != nil {
			fmt.Println("error occured")
			fmt.Printf("%s", err)
		}
		return string(out)
	case "windows":
		out, err := exec.Command("if exist \"%ProgramFiles(x86)%\" echo 64-bit").Output()
		if err != nil {
			fmt.Println("error occured")
			fmt.Printf("%s", err)
		}
		if string(out) == "64-bit" {
			return "x86_64"
		}
		return "i686"
	default:
		return "could not determine Operating system"
	}
}
<commit_msg>rebase + variable shadowing problem fix.<commit_after>package main

import (
	"fmt"

	"os"
	"os/exec"
	"path"
	"runtime"

	log "github.com/Sirupsen/logrus"
)

// StructuredOuput ...
type StructuredOuput struct {
	planet       string
	output       string
	maxOutLength int
}

func main() {
	opts := Opts{}
	opts.procArgs(os.Args)

	if opts.helpFlag {
		printUsage()
		os.Exit(0)
	}
	if opts.versionFlag {
		printVersion()
		os.Exit(0)
	}

	level := log.InfoLevel
	if opts.debugFlag {
		level = log.DebugLevel
	}
	// Default logfile path
	logDir := path.Join(os.Getenv("ORBIT_HOME"), "logs")
	createLogDirIfNecessary(logDir)
	logFile := path.Join(logDir, "ski.log")
	setupLogger(logFile, level)

	log.Infof("Started with args: %v", os.Args)
	log.Debug(&opts)
	exec := makeExecutor(&opts)
	exec.execMain(&opts)
	log.Infof("Ended with args: %v", os.Args)
}

func createLogDirIfNecessary(dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0775|os.ModeDir); err != nil {
			// can't do anything
			os.Stderr.WriteString(fmt.Sprintf("%v", err))
		}
	}
}

func makeExecutor(opts *Opts) Executor {
	executor := Executor{}
	var planet Planet
	for _, entry := range opts.planets {
		var user, host, dbID, connDet string
		dbID, connDet = getFullConnectionDetails(entry)
		planetType := getType(entry)
		planet.outputStruct.planet = entry
		id := entry
		if isSupported(entry) {
			user, host = getUserAndHost(connDet)
		} else {
			continue
		}
		executor.planets = append(executor.planets, Planet{id, user, host, planetType, dbID, StructuredOuput{id, "", 0}})
	}
	return executor
}

/**
*	Prints the current Version of the ski application
 */
func printVersion() {
	runtimeOS := getOS()
	progArch := getArch()
	archOS := getOSArch()
	vers := fmt.Sprintf("ski version %s %s %s (%s)", version, progArch, runtimeOS, archOS)
	os.Stdout.WriteString(vers + "\n")
}

/**
*	Prints the help dialog
 */
func printUsage() {
	usage := `usage: ski [options...] <planets>...
	Options:
	-s="<scriptname>"   Execute script and return result
	-c="<command>"      Execute script and return result
	-t=<"templatename>" Templatefile to be applied
	-p    Pretty print output as a table
	-l    Load bash profiles on Server
	-h    Display this help text
	-v    Show version number
	-d    Show extended debug informations, set logging level to debug
`
	fmt.Println(usage)
}

func getOS() string {
	switch runtime.GOOS {
	case "windows":
		return "Windows"
	case "linux":
		return "Linux"
	case "darwin":
		return "MacOS"
	default:
		return "could not determine OS"
	}
}

func getArch() string {
	switch runtime.GOARCH {
	case "amd64":
		return "64bit"
	case "386":
		return "32bit"
	default:
		return "could not determine architecture"
	}
}

func getOSArch() string {
	switch runtime.GOOS {
	case "linux":
		out, err := exec.Command("uname", "-m").Output()
		if err != nil {
			fmt.Println("error occured")
			fmt.Printf("%s", err)
		}
		return string(out)
	case "windows":
		out, err := exec.Command("if exist \"%ProgramFiles(x86)%\" echo 64-bit").Output()
		if err != nil {
			fmt.Println("error occured")
			fmt.Printf("%s", err)
		}
		if string(out) == "64-bit" {
			return "x86_64"
		}
		return "i686"
	default:
		return "could not determine Operating system"
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The LUCI 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 lib

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

	"github.com/danjacques/gofslock/fslock"
	"github.com/maruel/subcommands"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
)

// LockFileEnvVariable specifies the directory of the lock file.
const LockFileEnvVariable = "MMUTEX_LOCK_DIR"

// LockFileName specifies the name of the lock file within $MMUTEX_LOCK_DIR.
const LockFileName = "mmutex.lock"

// DrainFileName specifies the name of the drain file within $MMUTEX_LOCK_DIR.
const DrainFileName = "mmutex.drain"

// DefaultCommandTimeout is the total amount of time, including lock acquisition
// and command runtime, allotted to running a command through mmutex.
const DefaultCommandTimeout = 2 * time.Hour

// lockAcquisitionAttempts is the number of times that the blocker should attempt to
// acquire the lock. This is approximate, especially when making a high number of
// attempts over a short period of time.
const lockAcquisitionAttempts = 100

// defaultLockPollingInterval is the polling interval used if no command
// timeout is specified.
const defaultLockPollingInterval = time.Millisecond

// computeMutexPaths returns the lock and drain file paths based on the environment,
// or empty strings if no lock files should be used.
func computeMutexPaths(env subcommands.Env) (lockFilePath string, drainFilePath string, err error) {
	envVar := env[LockFileEnvVariable]
	if !envVar.Exists {
		return "", "", nil
	}

	lockFileDir := envVar.Value
	if !filepath.IsAbs(lockFileDir) {
		return "", "", errors.Reason("Lock file directory %s must be an absolute path", lockFileDir).Err()
	}

	if _, err := os.Stat(lockFileDir); os.IsNotExist(err) {
		fmt.Printf("Lock file directory %s does not exist, mmutex acting as a passthrough.", lockFileDir)
		return "", "", nil
	}

	return filepath.Join(lockFileDir, LockFileName), filepath.Join(lockFileDir, DrainFileName), nil
}

func createLockBlocker(ctx context.Context) fslock.Blocker {
	pollingInterval := defaultLockPollingInterval
	if deadline, ok := ctx.Deadline(); ok {
		pollingInterval = clock.Until(ctx, deadline) / (lockAcquisitionAttempts - 1)
	}

	return func() error {
		if clock.Sleep(ctx, pollingInterval).Err != nil {
			return fslock.ErrLockHeld
		}

		// Returning nil signals that the lock should be retried.
		return nil
	}
}

// blockWhileFileExists blocks until the file located at path no longer exists.
// For convenience, this method reuses the Blocker interface exposed by fslock
// and used elsewhere in this package.
func blockWhileFileExists(path string, blocker fslock.Blocker) error {
	for {
		if _, err := os.Stat(path); os.IsNotExist(err) {
			break
		} else if err != nil {
			return errors.Annotate(err, "failed to stat %s", path).Err()
		}

		if err := blocker(); err != nil {
			return errors.New("timed out waiting for drain file to disappear")
		}
	}

	return nil
}
<commit_msg>[mmutex] Add \n to the end of printf<commit_after>// Copyright 2017 The LUCI 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 lib

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

	"github.com/danjacques/gofslock/fslock"
	"github.com/maruel/subcommands"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
)

// LockFileEnvVariable specifies the directory of the lock file.
const LockFileEnvVariable = "MMUTEX_LOCK_DIR"

// LockFileName specifies the name of the lock file within $MMUTEX_LOCK_DIR.
const LockFileName = "mmutex.lock"

// DrainFileName specifies the name of the drain file within $MMUTEX_LOCK_DIR.
const DrainFileName = "mmutex.drain"

// DefaultCommandTimeout is the total amount of time, including lock acquisition
// and command runtime, allotted to running a command through mmutex.
const DefaultCommandTimeout = 2 * time.Hour

// lockAcquisitionAttempts is the number of times that the blocker should attempt to
// acquire the lock. This is approximate, especially when making a high number of
// attempts over a short period of time.
const lockAcquisitionAttempts = 100

// defaultLockPollingInterval is the polling interval used if no command
// timeout is specified.
const defaultLockPollingInterval = time.Millisecond

// computeMutexPaths returns the lock and drain file paths based on the environment,
// or empty strings if no lock files should be used.
func computeMutexPaths(env subcommands.Env) (lockFilePath string, drainFilePath string, err error) {
	envVar := env[LockFileEnvVariable]
	if !envVar.Exists {
		return "", "", nil
	}

	lockFileDir := envVar.Value
	if !filepath.IsAbs(lockFileDir) {
		return "", "", errors.Reason("Lock file directory %s must be an absolute path", lockFileDir).Err()
	}

	if _, err := os.Stat(lockFileDir); os.IsNotExist(err) {
		fmt.Printf("Lock file directory %s does not exist, mmutex acting as a passthrough.\n", lockFileDir)
		return "", "", nil
	}

	return filepath.Join(lockFileDir, LockFileName), filepath.Join(lockFileDir, DrainFileName), nil
}

func createLockBlocker(ctx context.Context) fslock.Blocker {
	pollingInterval := defaultLockPollingInterval
	if deadline, ok := ctx.Deadline(); ok {
		pollingInterval = clock.Until(ctx, deadline) / (lockAcquisitionAttempts - 1)
	}

	return func() error {
		if clock.Sleep(ctx, pollingInterval).Err != nil {
			return fslock.ErrLockHeld
		}

		// Returning nil signals that the lock should be retried.
		return nil
	}
}

// blockWhileFileExists blocks until the file located at path no longer exists.
// For convenience, this method reuses the Blocker interface exposed by fslock
// and used elsewhere in this package.
func blockWhileFileExists(path string, blocker fslock.Blocker) error {
	for {
		if _, err := os.Stat(path); os.IsNotExist(err) {
			break
		} else if err != nil {
			return errors.Annotate(err, "failed to stat %s", path).Err()
		}

		if err := blocker(); err != nil {
			return errors.New("timed out waiting for drain file to disappear")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"log/syslog"
)

var (
	syslogMap = map[int]syslog.Priority{
		LevelError:   syslog.LOG_ERR,
		LevelWarning: syslog.LOG_WARNING,
		LevelNotice:  syslog.LOG_NOTICE,
		LevelInfo:    syslog.LOG_INFO,
		LevelDebug:   syslog.LOG_DEBUG,
	}

	syslogFacilities = map[string]syslog.Priority{
		"kern":     syslog.LOG_KERN,
		"user":     syslog.LOG_USER,
		"mail":     syslog.LOG_MAIL,
		"daemon":   syslog.LOG_DAEMON,
		"auth":     syslog.LOG_AUTH,
		"syslog":   syslog.LOG_SYSLOG,
		"lpr":      syslog.LOG_LPR,
		"news":     syslog.LOG_NEWS,
		"uucp":     syslog.LOG_UUCP,
		"cron":     syslog.LOG_CRON,
		"authpriv": syslog.LOG_AUTHPRIV,
		"ftp":      syslog.LOG_FTP,
		"local0":   syslog.LOG_LOCAL0,
		"local1":   syslog.LOG_LOCAL1,
		"local2":   syslog.LOG_LOCAL2,
		"local3":   syslog.LOG_LOCAL3,
		"local4":   syslog.LOG_LOCAL4,
		"local5":   syslog.LOG_LOCAL5,
		"local6":   syslog.LOG_LOCAL6,
		"local7":   syslog.LOG_LOCAL7,
	}
)

type syslogBackend struct {
	logger *Logger
	writer *syslog.Writer
	level  int
}

func newSyslogBackend(config SyslogConfig, logger *Logger) (backend, error) {
	var (
		writer *syslog.Writer
		err    error
	)

	if config.Level == "" {
		config.Level = defaultLevel
	} else if _, ok := levelMap[config.Level]; !ok {
		return nil, ErrInvalidLevel
	}

	// Check for syslog facility
	facility, ok := syslogFacilities[config.Facility]
	if !ok {
		return nil, ErrInvalidFacility
	}

	if config.Address != "" {
		network := config.Transport
		if network == "" {
			network = "udp"
		}

		writer, err = syslog.Dial(network, config.Address, facility, config.Tag)
	} else {
		writer, err = syslog.New(facility, config.Tag)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to initialize syslog: %s", err)
	}

	return &syslogBackend{
		logger: logger,
		writer: writer,
		level:  levelMap[config.Level],
	}, nil
}

func (b syslogBackend) Close() {
	b.writer.Close()
}

func (b syslogBackend) Write(level int, mesg string) {
	if level > b.level {
		return
	}

	switch level {
	case LevelError:
		b.writer.Err(mesg)

	case LevelWarning:
		b.writer.Warning(mesg)

	case LevelNotice:
		b.writer.Notice(mesg)

	case LevelInfo:
		b.writer.Info(mesg)

	case LevelDebug:
		b.writer.Debug(mesg)
	}
}
<commit_msg>Remove dead code in syslog.go<commit_after>package logger

import (
	"fmt"
	"log/syslog"
)

var (
	syslogFacilities = map[string]syslog.Priority{
		"kern":     syslog.LOG_KERN,
		"user":     syslog.LOG_USER,
		"mail":     syslog.LOG_MAIL,
		"daemon":   syslog.LOG_DAEMON,
		"auth":     syslog.LOG_AUTH,
		"syslog":   syslog.LOG_SYSLOG,
		"lpr":      syslog.LOG_LPR,
		"news":     syslog.LOG_NEWS,
		"uucp":     syslog.LOG_UUCP,
		"cron":     syslog.LOG_CRON,
		"authpriv": syslog.LOG_AUTHPRIV,
		"ftp":      syslog.LOG_FTP,
		"local0":   syslog.LOG_LOCAL0,
		"local1":   syslog.LOG_LOCAL1,
		"local2":   syslog.LOG_LOCAL2,
		"local3":   syslog.LOG_LOCAL3,
		"local4":   syslog.LOG_LOCAL4,
		"local5":   syslog.LOG_LOCAL5,
		"local6":   syslog.LOG_LOCAL6,
		"local7":   syslog.LOG_LOCAL7,
	}
)

type syslogBackend struct {
	logger *Logger
	writer *syslog.Writer
	level  int
}

func newSyslogBackend(config SyslogConfig, logger *Logger) (backend, error) {
	var (
		writer *syslog.Writer
		err    error
	)

	if config.Level == "" {
		config.Level = defaultLevel
	} else if _, ok := levelMap[config.Level]; !ok {
		return nil, ErrInvalidLevel
	}

	// Check for syslog facility
	facility, ok := syslogFacilities[config.Facility]
	if !ok {
		return nil, ErrInvalidFacility
	}

	if config.Address != "" {
		network := config.Transport
		if network == "" {
			network = "udp"
		}

		writer, err = syslog.Dial(network, config.Address, facility, config.Tag)
	} else {
		writer, err = syslog.New(facility, config.Tag)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to initialize syslog: %s", err)
	}

	return &syslogBackend{
		logger: logger,
		writer: writer,
		level:  levelMap[config.Level],
	}, nil
}

func (b syslogBackend) Close() {
	b.writer.Close()
}

func (b syslogBackend) Write(level int, mesg string) {
	if level > b.level {
		return
	}

	switch level {
	case LevelError:
		b.writer.Err(mesg)

	case LevelWarning:
		b.writer.Warning(mesg)

	case LevelNotice:
		b.writer.Notice(mesg)

	case LevelInfo:
		b.writer.Info(mesg)

	case LevelDebug:
		b.writer.Debug(mesg)
	}
}
<|endoftext|>


<commit_before>package timing

import "time"

// Controls whether library calls should be mocked, or whether we should use the standard Go time library.
// If we're in Mock Mode, then time does not pass as normal, but only progresses when Elapse is called.
// False by default, indicating that we just call through to standard Go functions.
var MockMode bool = false
var currentTimeMock time.Time = time.Unix(0, 0)
var mockTimers []*mockTimer = make([]*mockTimer, 0)

// Interface over Golang's built-in Timers, allowing them to be swapped out for mocked timers.
type Timer interface {
	// Returns a channel which sends the current time immediately when the timer expires.
	// Equivalent to time.Timer.C; however, we have to use a method here instead of a member since this is an interface.
	C() <-chan time.Time

	// Resets the timer such that it will expire in duration 'd' after the current time.
	// Returns true if the timer had been active, and false if it had expired or been stopped.
	Reset(d time.Duration) bool

	// Stops the timer, preventing it from firing.
	// Returns true if the timer had been active, and false if it had expired or been stopped.
	Stop() bool
}

// Implementation of Timer that just wraps time.Timer.
type realTimer struct {
	*time.Timer
}

func (t *realTimer) C() <-chan time.Time {
	return t.Timer.C
}

func (t *realTimer) Reset(d time.Duration) bool {
	return t.Timer.Reset(d)
}

func (t *realTimer) Stop() bool {
	return t.Timer.Stop()
}

// Implementation of Timer that mocks time.Timer, firing when the total elapsed time (as controlled by Elapse)
// exceeds the duration specified when the timer was constructed.
type mockTimer struct {
	EndTime time.Time
	Chan    chan time.Time
}

func (t *mockTimer) C() <-chan time.Time {
	return t.Chan
}

func (t *mockTimer) Reset(d time.Duration) bool {
	wasActive := removeMockTimer(t)

	t.EndTime = currentTimeMock.Add(d)
	if d > 0 {
        mockTimers = append(mockTimers, t)
	} else {
        // The new timer has an expiry time of 0.
        // Fire it right away, and don't bother tracking it.
		t.Chan <- currentTimeMock
    }

	return wasActive
}

func (t *mockTimer) Stop() bool {
	return removeMockTimer(t)
}

// Creates a new Timer; either a wrapper around a standard Go time.Timer, or a mocked-out Timer,
// depending on whether MockMode is set.
func NewTimer(d time.Duration) Timer {
	if MockMode {
		t := mockTimer{currentTimeMock.Add(d), make(chan time.Time)}
		if d == 0 {
			t.Chan <- currentTimeMock
		} else {
			mockTimers = append(mockTimers, &t)
		}
		return &t
	} else {
		return &realTimer{time.NewTimer(d)}
	}
}

// See built-in time.After() function.
func After(d time.Duration) <-chan time.Time {
	return NewTimer(d).C()
}

// See built-in time.Sleep() function.
func Sleep(d time.Duration) {
	<-After(d)
}

// Increment the current time by the given Duration.
// This function can only be called in Mock Mode, otherwise we will panic.
func Elapse(d time.Duration) {
	requireMockMode()
	fired := make([]*mockTimer, 0)
	currentTimeMock = currentTimeMock.Add(d)

	// Fire any timers whose time has come up.
	for _, t := range mockTimers {
		if currentTimeMock.After(t.EndTime) {
			t.Chan <- currentTimeMock
			fired = append(fired, t)
		}
	}

	// Stop tracking any fired timers.
	remainingTimers := make([]*mockTimer, 0)
loop:
	for _, t := range mockTimers {
		for _, fired_t := range fired {
			if t == fired_t {
				continue loop
			}
		}

		remainingTimers = append(remainingTimers, t)
	}

	mockTimers = remainingTimers
}

// Returns the current time.
// If Mock Mode is set, this will be the sum of all Durations passed into Elapse calls;
// otherwise it will be the true system time.
func Now() time.Time {
	if MockMode {
		return currentTimeMock
	} else {
		return time.Now()
	}
}

// Shortcut method to enforce that Mock Mode is enabled.
func requireMockMode() {
	if !MockMode {
		panic("This method requires MockMode to be enabled")
	}
}

// Utility method to remove a mockTimer from the list of outstanding timers.
func removeMockTimer(t *mockTimer) bool {
	// First, find the index of the timer in our list.
	found := false
	var idx int
	var elt *mockTimer
	for idx, elt = range mockTimers {
		if elt == t {
			found = true
			break
		}
	}

	if found {
		// We found the given timer. Remove it.
		if idx == len(mockTimers)-1 {
			mockTimers = mockTimers[:idx]
		} else {
			mockTimers = append(mockTimers[:idx], mockTimers[idx+1])
		}
		return true
	} else {
		// The timer was not present, indicating that it was already expired.
		return false
	}
}
<commit_msg>Implement AfterFunc in mock timing module.<commit_after>package timing

import "time"

// Controls whether library calls should be mocked, or whether we should use the standard Go time library.
// If we're in Mock Mode, then time does not pass as normal, but only progresses when Elapse is called.
// False by default, indicating that we just call through to standard Go functions.
var MockMode bool = false
var currentTimeMock time.Time = time.Unix(0, 0)
var mockTimers []*mockTimer = make([]*mockTimer, 0)

// Interface over Golang's built-in Timers, allowing them to be swapped out for mocked timers.
type Timer interface {
	// Returns a channel which sends the current time immediately when the timer expires.
	// Equivalent to time.Timer.C; however, we have to use a method here instead of a member since this is an interface.
	C() <-chan time.Time

	// Resets the timer such that it will expire in duration 'd' after the current time.
	// Returns true if the timer had been active, and false if it had expired or been stopped.
	Reset(d time.Duration) bool

	// Stops the timer, preventing it from firing.
	// Returns true if the timer had been active, and false if it had expired or been stopped.
	Stop() bool
}

// Implementation of Timer that just wraps time.Timer.
type realTimer struct {
	*time.Timer
}

func (t *realTimer) C() <-chan time.Time {
	return t.Timer.C
}

func (t *realTimer) Reset(d time.Duration) bool {
	return t.Timer.Reset(d)
}

func (t *realTimer) Stop() bool {
	return t.Timer.Stop()
}

// Implementation of Timer that mocks time.Timer, firing when the total elapsed time (as controlled by Elapse)
// exceeds the duration specified when the timer was constructed.
type mockTimer struct {
	EndTime time.Time
	Chan    chan time.Time
}

func (t *mockTimer) C() <-chan time.Time {
	return t.Chan
}

func (t *mockTimer) Reset(d time.Duration) bool {
	wasActive := removeMockTimer(t)

	t.EndTime = currentTimeMock.Add(d)
	if d > 0 {
		mockTimers = append(mockTimers, t)
	} else {
		// The new timer has an expiry time of 0.
		// Fire it right away, and don't bother tracking it.
		t.Chan <- currentTimeMock
	}

	return wasActive
}

func (t *mockTimer) Stop() bool {
	return removeMockTimer(t)
}

// Creates a new Timer; either a wrapper around a standard Go time.Timer, or a mocked-out Timer,
// depending on whether MockMode is set.
func NewTimer(d time.Duration) Timer {
	if MockMode {
		t := mockTimer{currentTimeMock.Add(d), make(chan time.Time)}
		if d == 0 {
			t.Chan <- currentTimeMock
		} else {
			mockTimers = append(mockTimers, &t)
		}
		return &t
	} else {
		return &realTimer{time.NewTimer(d)}
	}
}

// See built-in time.After() function.
func After(d time.Duration) <-chan time.Time {
	return NewTimer(d).C()
}

// See built-in time.AfterFunc() function.
func AfterFunc(d time.Duration, f func()) Timer {
	t := NewTimer(d)
	go func() {
		if _, ok := <-t.C(); ok {
			f()
		}
	}()
	return t
}

// See built-in time.Sleep() function.
func Sleep(d time.Duration) {
	<-After(d)
}

// Increment the current time by the given Duration.
// This function can only be called in Mock Mode, otherwise we will panic.
func Elapse(d time.Duration) {
	requireMockMode()
	fired := make([]*mockTimer, 0)
	currentTimeMock = currentTimeMock.Add(d)

	// Fire any timers whose time has come up.
	for _, t := range mockTimers {
		if currentTimeMock.After(t.EndTime) {
			t.Chan <- currentTimeMock
			fired = append(fired, t)
		}
	}

	// Stop tracking any fired timers.
	remainingTimers := make([]*mockTimer, 0)
loop:
	for _, t := range mockTimers {
		for _, fired_t := range fired {
			if t == fired_t {
				continue loop
			}
		}

		remainingTimers = append(remainingTimers, t)
	}

	mockTimers = remainingTimers
}

// Returns the current time.
// If Mock Mode is set, this will be the sum of all Durations passed into Elapse calls;
// otherwise it will be the true system time.
func Now() time.Time {
	if MockMode {
		return currentTimeMock
	} else {
		return time.Now()
	}
}

// Shortcut method to enforce that Mock Mode is enabled.
func requireMockMode() {
	if !MockMode {
		panic("This method requires MockMode to be enabled")
	}
}

// Utility method to remove a mockTimer from the list of outstanding timers.
func removeMockTimer(t *mockTimer) bool {
	// First, find the index of the timer in our list.
	found := false
	var idx int
	var elt *mockTimer
	for idx, elt = range mockTimers {
		if elt == t {
			found = true
			break
		}
	}

	if found {
		// We found the given timer. Remove it.
		if idx == len(mockTimers)-1 {
			mockTimers = mockTimers[:idx]
		} else {
			mockTimers = append(mockTimers[:idx], mockTimers[idx+1])
		}
		return true
	} else {
		// The timer was not present, indicating that it was already expired.
		return false
	}
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/beego/beego"

	"github.com/goharbor/harbor/src/common/dao"
	common_http "github.com/goharbor/harbor/src/common/http"
	configCtl "github.com/goharbor/harbor/src/controller/config"
	_ "github.com/goharbor/harbor/src/controller/event/handler"
	"github.com/goharbor/harbor/src/controller/health"
	"github.com/goharbor/harbor/src/controller/registry"
	"github.com/goharbor/harbor/src/controller/systemartifact"
	"github.com/goharbor/harbor/src/core/api"
	_ "github.com/goharbor/harbor/src/core/auth/authproxy"
	_ "github.com/goharbor/harbor/src/core/auth/db"
	_ "github.com/goharbor/harbor/src/core/auth/ldap"
	_ "github.com/goharbor/harbor/src/core/auth/oidc"
	_ "github.com/goharbor/harbor/src/core/auth/uaa"
	"github.com/goharbor/harbor/src/core/middlewares"
	"github.com/goharbor/harbor/src/core/service/token"
	"github.com/goharbor/harbor/src/core/session"
	"github.com/goharbor/harbor/src/lib/cache"
	_ "github.com/goharbor/harbor/src/lib/cache/memory" // memory cache
	_ "github.com/goharbor/harbor/src/lib/cache/redis"  // redis cache
	"github.com/goharbor/harbor/src/lib/config"
	"github.com/goharbor/harbor/src/lib/log"
	"github.com/goharbor/harbor/src/lib/metric"
	"github.com/goharbor/harbor/src/lib/orm"
	"github.com/goharbor/harbor/src/lib/retry"
	tracelib "github.com/goharbor/harbor/src/lib/trace"
	"github.com/goharbor/harbor/src/migration"
	_ "github.com/goharbor/harbor/src/pkg/accessory/model/base"
	_ "github.com/goharbor/harbor/src/pkg/accessory/model/cosign"
	"github.com/goharbor/harbor/src/pkg/audit"
	dbCfg "github.com/goharbor/harbor/src/pkg/config/db"
	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
	"github.com/goharbor/harbor/src/pkg/notification"
	_ "github.com/goharbor/harbor/src/pkg/notifier/topic"
	"github.com/goharbor/harbor/src/pkg/oidc"
	"github.com/goharbor/harbor/src/pkg/scan"
	"github.com/goharbor/harbor/src/pkg/scan/dao/scanner"
	pkguser "github.com/goharbor/harbor/src/pkg/user"
	"github.com/goharbor/harbor/src/pkg/version"
	"github.com/goharbor/harbor/src/server"
)

const (
	adminUserID = 1
)

func updateInitPassword(ctx context.Context, userID int, password string) error {
	userMgr := pkguser.Mgr
	user, err := userMgr.Get(ctx, userID)
	if err != nil {
		return fmt.Errorf("failed to get user, userID: %d %v", userID, err)
	}
	if user.Salt == "" {
		err = userMgr.UpdatePassword(ctx, userID, password)
		if err != nil {
			return fmt.Errorf("failed to update user encrypted password, userID: %d, err: %v", userID, err)
		}

		log.Infof("User id: %d updated its encrypted password successfully.", userID)
	} else {
		log.Infof("User id: %d already has its encrypted password.", userID)
	}
	return nil
}

func gracefulShutdown(closing, done chan struct{}, shutdowns ...func()) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	log.Infof("capture system signal %s, to close \"closing\" channel", <-signals)
	close(closing)
	shutdownChan := make(chan struct{}, 1)
	go func() {
		for _, s := range shutdowns {
			s()
		}
		<-done
		log.Infof("Goroutines exited normally")
		shutdownChan <- struct{}{}
	}()
	select {
	case <-shutdownChan:
		log.Infof("all shutdown jobs done")
	case <-time.After(time.Second * 3):
		log.Infof("Timeout waiting goroutines to exit")
	}

	os.Exit(0)
}

func main() {
	runMode := flag.String("mode", "normal", "The harbor-core container run mode, it could be normal, migrate or skip-migrate, default is normal")
	flag.Parse()

	beego.BConfig.WebConfig.Session.SessionOn = true
	beego.BConfig.WebConfig.Session.SessionName = config.SessionCookieName

	redisURL := os.Getenv("_REDIS_URL_CORE")
	if len(redisURL) > 0 {
		u, err := url.Parse(redisURL)
		if err != nil {
			panic("bad _REDIS_URL")
		}

		beego.BConfig.WebConfig.Session.SessionProvider = session.HarborProviderName
		beego.BConfig.WebConfig.Session.SessionProviderConfig = redisURL

		log.Info("initializing cache ...")
		if err := cache.Initialize(u.Scheme, redisURL); err != nil {
			log.Fatalf("failed to initialize cache: %v", err)
		}
		// when config/db init function is called, the cache is not ready,
		// enable config cache explicitly when the cache is ready
		dbCfg.EnableConfigCache()
	}
	beego.AddTemplateExt("htm")

	log.Info("initializing configurations...")
	config.Init()
	log.Info("configurations initialization completed")
	metricCfg := config.Metric()
	if metricCfg.Enabled {
		metric.RegisterCollectors()
		go metric.ServeProm(metricCfg.Path, metricCfg.Port)
	}
	ctx := context.Background()
	config.InitTraceConfig(ctx)
	shutdownTracerProvider := tracelib.InitGlobalTracer(ctx)
	token.InitCreators()
	database, err := config.Database()
	if err != nil {
		log.Fatalf("failed to get database configuration: %v", err)
	}
	if err := dao.InitDatabase(database); err != nil {
		log.Fatalf("failed to initialize database: %v", err)
	}
	if strings.EqualFold(*runMode, "migrate") {
		// Used by Harbor helm preinstall, preupgrade hook container
		if err = migration.Migrate(database); err != nil {
			log.Fatalf("failed to migrate the database, error: %v", err)
		}
		log.Info("the database migrate success")
		os.Exit(0)
	} else if strings.EqualFold(*runMode, "skip-migrate") {
		log.Info("skip the database migrate")
	} else {
		// Run migrator as normal
		if err = migration.Migrate(database); err != nil {
			log.Fatalf("failed to migrate the database, error: %v", err)
		}
	}

	ctx = orm.Clone(ctx)
	if err := config.Load(ctx); err != nil {
		log.Fatalf("failed to load config: %v", err)
	}
	if err := configCtl.Ctl.OverwriteConfig(ctx); err != nil {
		log.Fatalf("failed to init config from CONFIG_OVERWRITE_JSON, error %v", err)
	}
	password, err := config.InitialAdminPassword()
	if err != nil {
		log.Fatalf("failed to get admin's initial password: %v", err)
	}
	if err := updateInitPassword(ctx, adminUserID, password); err != nil {
		log.Error(err)
	}

	// Init API handler
	if err := api.Init(); err != nil {
		log.Fatalf("Failed to initialize API handlers with error: %s", err.Error())
	}

	health.RegisterHealthCheckers()
	registerScanners(orm.Context())

	closing := make(chan struct{})
	done := make(chan struct{})
	go gracefulShutdown(closing, done, shutdownTracerProvider)
	// Start health checker for registries
	go registry.Ctl.StartRegularHealthCheck(orm.Context(), closing, done)
	// Init audit log
	auditEP := config.AuditLogForwardEndpoint(ctx)
	audit.LogMgr.Init(ctx, auditEP)

	log.Info("initializing notification...")
	notification.Init()

	server.RegisterRoutes()

	if common_http.InternalTLSEnabled() {
		log.Info("internal TLS enabled, Init TLS ...")
		iTLSKeyPath := os.Getenv("INTERNAL_TLS_KEY_PATH")
		iTLSCertPath := os.Getenv("INTERNAL_TLS_CERT_PATH")

		log.Infof("load client key: %s client cert: %s", iTLSKeyPath, iTLSCertPath)
		beego.BConfig.Listen.EnableHTTP = false
		beego.BConfig.Listen.EnableHTTPS = true
		beego.BConfig.Listen.HTTPSPort = 8443
		beego.BConfig.Listen.HTTPSKeyFile = iTLSKeyPath
		beego.BConfig.Listen.HTTPSCertFile = iTLSCertPath
		beego.BeeApp.Server.TLSConfig = common_http.NewServerTLSConfig()
	}

	log.Infof("Version: %s, Git commit: %s", version.ReleaseVersion, version.GitCommit)

	log.Info("Fix empty subiss for meta info data.")
	_, err = oidc.FixEmptySubIss(orm.Context())
	if err != nil {
		log.Warningf("oidc.FixEmptySubIss() errors out, error: %v", err)
	}
	// Scheduling of system artifact depends on the jobservice, where gorountine is used to avoid the circular
	// dependencies between core and jobservice.
	go func() {
		url := config.InternalJobServiceURL() + "/api/v1/stats"
		checker := health.HTTPStatusCodeHealthChecker(http.MethodGet, url, nil, 60*time.Second, http.StatusOK)
		options := []retry.Option{
			retry.InitialInterval(time.Millisecond * 500),
			retry.MaxInterval(time.Second * 10),
			retry.Timeout(time.Minute),
			retry.Callback(func(err error, sleep time.Duration) {
				log.Debugf("failed to ping %s, retry after %s : %v", url, sleep, err)
			}),
		}
		if err := retry.Retry(checker.Check, options...); err != nil {
			log.Errorf("failed to check the jobservice health status: timeout, error: %v", err)
			return
		}
		systemartifact.ScheduleCleanupTask(ctx)
	}()
	beego.RunWithMiddleWares("", middlewares.MiddleWares()...)
}

const (
	trivyScanner = "Trivy"
)

func registerScanners(ctx context.Context) {
	wantedScanners := make([]scanner.Registration, 0)
	uninstallScannerNames := make([]string, 0)

	if config.WithTrivy() {
		log.Info("Registering Trivy scanner")
		wantedScanners = append(wantedScanners, scanner.Registration{
			Name:            trivyScanner,
			Description:     "The Trivy scanner adapter",
			URL:             config.TrivyAdapterURL(),
			UseInternalAddr: true,
			Immutable:       true,
		})
	} else {
		log.Info("Removing Trivy scanner")
		uninstallScannerNames = append(uninstallScannerNames, trivyScanner)
	}

	if err := scan.RemoveImmutableScanners(ctx, uninstallScannerNames); err != nil {
		log.Warningf("failed to remove scanners: %v", err)
	}

	if err := scan.EnsureScanners(ctx, wantedScanners); err != nil {
		log.Fatalf("failed to register scanners: %v", err)
	}

	if defaultScannerName := getDefaultScannerName(); defaultScannerName != "" {
		log.Infof("Setting %s as default scanner", defaultScannerName)
		if err := scan.EnsureDefaultScanner(ctx, defaultScannerName); err != nil {
			log.Fatalf("failed to set default scanner: %v", err)
		}
	}
}

func getDefaultScannerName() string {
	if config.WithTrivy() {
		return trivyScanner
	}
	return ""
}
<commit_msg>feat: log completion message when database migrated (#17684)<commit_after>//  Copyright Project Harbor Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/beego/beego"

	"github.com/goharbor/harbor/src/common/dao"
	common_http "github.com/goharbor/harbor/src/common/http"
	configCtl "github.com/goharbor/harbor/src/controller/config"
	_ "github.com/goharbor/harbor/src/controller/event/handler"
	"github.com/goharbor/harbor/src/controller/health"
	"github.com/goharbor/harbor/src/controller/registry"
	"github.com/goharbor/harbor/src/controller/systemartifact"
	"github.com/goharbor/harbor/src/core/api"
	_ "github.com/goharbor/harbor/src/core/auth/authproxy"
	_ "github.com/goharbor/harbor/src/core/auth/db"
	_ "github.com/goharbor/harbor/src/core/auth/ldap"
	_ "github.com/goharbor/harbor/src/core/auth/oidc"
	_ "github.com/goharbor/harbor/src/core/auth/uaa"
	"github.com/goharbor/harbor/src/core/middlewares"
	"github.com/goharbor/harbor/src/core/service/token"
	"github.com/goharbor/harbor/src/core/session"
	"github.com/goharbor/harbor/src/lib/cache"
	_ "github.com/goharbor/harbor/src/lib/cache/memory" // memory cache
	_ "github.com/goharbor/harbor/src/lib/cache/redis"  // redis cache
	"github.com/goharbor/harbor/src/lib/config"
	"github.com/goharbor/harbor/src/lib/log"
	"github.com/goharbor/harbor/src/lib/metric"
	"github.com/goharbor/harbor/src/lib/orm"
	"github.com/goharbor/harbor/src/lib/retry"
	tracelib "github.com/goharbor/harbor/src/lib/trace"
	"github.com/goharbor/harbor/src/migration"
	_ "github.com/goharbor/harbor/src/pkg/accessory/model/base"
	_ "github.com/goharbor/harbor/src/pkg/accessory/model/cosign"
	"github.com/goharbor/harbor/src/pkg/audit"
	dbCfg "github.com/goharbor/harbor/src/pkg/config/db"
	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
	"github.com/goharbor/harbor/src/pkg/notification"
	_ "github.com/goharbor/harbor/src/pkg/notifier/topic"
	"github.com/goharbor/harbor/src/pkg/oidc"
	"github.com/goharbor/harbor/src/pkg/scan"
	"github.com/goharbor/harbor/src/pkg/scan/dao/scanner"
	pkguser "github.com/goharbor/harbor/src/pkg/user"
	"github.com/goharbor/harbor/src/pkg/version"
	"github.com/goharbor/harbor/src/server"
)

const (
	adminUserID = 1
)

func updateInitPassword(ctx context.Context, userID int, password string) error {
	userMgr := pkguser.Mgr
	user, err := userMgr.Get(ctx, userID)
	if err != nil {
		return fmt.Errorf("failed to get user, userID: %d %v", userID, err)
	}
	if user.Salt == "" {
		err = userMgr.UpdatePassword(ctx, userID, password)
		if err != nil {
			return fmt.Errorf("failed to update user encrypted password, userID: %d, err: %v", userID, err)
		}

		log.Infof("User id: %d updated its encrypted password successfully.", userID)
	} else {
		log.Infof("User id: %d already has its encrypted password.", userID)
	}
	return nil
}

func gracefulShutdown(closing, done chan struct{}, shutdowns ...func()) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	log.Infof("capture system signal %s, to close \"closing\" channel", <-signals)
	close(closing)
	shutdownChan := make(chan struct{}, 1)
	go func() {
		for _, s := range shutdowns {
			s()
		}
		<-done
		log.Infof("Goroutines exited normally")
		shutdownChan <- struct{}{}
	}()
	select {
	case <-shutdownChan:
		log.Infof("all shutdown jobs done")
	case <-time.After(time.Second * 3):
		log.Infof("Timeout waiting goroutines to exit")
	}

	os.Exit(0)
}

func main() {
	runMode := flag.String("mode", "normal", "The harbor-core container run mode, it could be normal, migrate or skip-migrate, default is normal")
	flag.Parse()

	beego.BConfig.WebConfig.Session.SessionOn = true
	beego.BConfig.WebConfig.Session.SessionName = config.SessionCookieName

	redisURL := os.Getenv("_REDIS_URL_CORE")
	if len(redisURL) > 0 {
		u, err := url.Parse(redisURL)
		if err != nil {
			panic("bad _REDIS_URL")
		}

		beego.BConfig.WebConfig.Session.SessionProvider = session.HarborProviderName
		beego.BConfig.WebConfig.Session.SessionProviderConfig = redisURL

		log.Info("initializing cache ...")
		if err := cache.Initialize(u.Scheme, redisURL); err != nil {
			log.Fatalf("failed to initialize cache: %v", err)
		}
		// when config/db init function is called, the cache is not ready,
		// enable config cache explicitly when the cache is ready
		dbCfg.EnableConfigCache()
	}
	beego.AddTemplateExt("htm")

	log.Info("initializing configurations...")
	config.Init()
	log.Info("configurations initialization completed")
	metricCfg := config.Metric()
	if metricCfg.Enabled {
		metric.RegisterCollectors()
		go metric.ServeProm(metricCfg.Path, metricCfg.Port)
	}
	ctx := context.Background()
	config.InitTraceConfig(ctx)
	shutdownTracerProvider := tracelib.InitGlobalTracer(ctx)
	token.InitCreators()
	database, err := config.Database()
	if err != nil {
		log.Fatalf("failed to get database configuration: %v", err)
	}
	if err := dao.InitDatabase(database); err != nil {
		log.Fatalf("failed to initialize database: %v", err)
	}
	if strings.EqualFold(*runMode, "migrate") {
		// Used by Harbor helm preinstall, preupgrade hook container
		if err = migration.Migrate(database); err != nil {
			log.Fatalf("failed to migrate the database, error: %v", err)
		}
		log.Info("the database migrate success")
		os.Exit(0)
	} else if strings.EqualFold(*runMode, "skip-migrate") {
		log.Info("skip the database migrate")
	} else {
		// Run migrator as normal
		if err = migration.Migrate(database); err != nil {
			log.Fatalf("failed to migrate the database, error: %v", err)
		}

		log.Info("The database has been migrated successfully")
	}

	ctx = orm.Clone(ctx)
	if err := config.Load(ctx); err != nil {
		log.Fatalf("failed to load config: %v", err)
	}
	if err := configCtl.Ctl.OverwriteConfig(ctx); err != nil {
		log.Fatalf("failed to init config from CONFIG_OVERWRITE_JSON, error %v", err)
	}
	password, err := config.InitialAdminPassword()
	if err != nil {
		log.Fatalf("failed to get admin's initial password: %v", err)
	}
	if err := updateInitPassword(ctx, adminUserID, password); err != nil {
		log.Error(err)
	}

	// Init API handler
	if err := api.Init(); err != nil {
		log.Fatalf("Failed to initialize API handlers with error: %s", err.Error())
	}

	health.RegisterHealthCheckers()
	registerScanners(orm.Context())

	closing := make(chan struct{})
	done := make(chan struct{})
	go gracefulShutdown(closing, done, shutdownTracerProvider)
	// Start health checker for registries
	go registry.Ctl.StartRegularHealthCheck(orm.Context(), closing, done)
	// Init audit log
	auditEP := config.AuditLogForwardEndpoint(ctx)
	audit.LogMgr.Init(ctx, auditEP)

	log.Info("initializing notification...")
	notification.Init()

	server.RegisterRoutes()

	if common_http.InternalTLSEnabled() {
		log.Info("internal TLS enabled, Init TLS ...")
		iTLSKeyPath := os.Getenv("INTERNAL_TLS_KEY_PATH")
		iTLSCertPath := os.Getenv("INTERNAL_TLS_CERT_PATH")

		log.Infof("load client key: %s client cert: %s", iTLSKeyPath, iTLSCertPath)
		beego.BConfig.Listen.EnableHTTP = false
		beego.BConfig.Listen.EnableHTTPS = true
		beego.BConfig.Listen.HTTPSPort = 8443
		beego.BConfig.Listen.HTTPSKeyFile = iTLSKeyPath
		beego.BConfig.Listen.HTTPSCertFile = iTLSCertPath
		beego.BeeApp.Server.TLSConfig = common_http.NewServerTLSConfig()
	}

	log.Infof("Version: %s, Git commit: %s", version.ReleaseVersion, version.GitCommit)

	log.Info("Fix empty subiss for meta info data.")
	_, err = oidc.FixEmptySubIss(orm.Context())
	if err != nil {
		log.Warningf("oidc.FixEmptySubIss() errors out, error: %v", err)
	}
	// Scheduling of system artifact depends on the jobservice, where gorountine is used to avoid the circular
	// dependencies between core and jobservice.
	go func() {
		url := config.InternalJobServiceURL() + "/api/v1/stats"
		checker := health.HTTPStatusCodeHealthChecker(http.MethodGet, url, nil, 60*time.Second, http.StatusOK)
		options := []retry.Option{
			retry.InitialInterval(time.Millisecond * 500),
			retry.MaxInterval(time.Second * 10),
			retry.Timeout(time.Minute),
			retry.Callback(func(err error, sleep time.Duration) {
				log.Debugf("failed to ping %s, retry after %s : %v", url, sleep, err)
			}),
		}
		if err := retry.Retry(checker.Check, options...); err != nil {
			log.Errorf("failed to check the jobservice health status: timeout, error: %v", err)
			return
		}
		systemartifact.ScheduleCleanupTask(ctx)
	}()
	beego.RunWithMiddleWares("", middlewares.MiddleWares()...)
}

const (
	trivyScanner = "Trivy"
)

func registerScanners(ctx context.Context) {
	wantedScanners := make([]scanner.Registration, 0)
	uninstallScannerNames := make([]string, 0)

	if config.WithTrivy() {
		log.Info("Registering Trivy scanner")
		wantedScanners = append(wantedScanners, scanner.Registration{
			Name:            trivyScanner,
			Description:     "The Trivy scanner adapter",
			URL:             config.TrivyAdapterURL(),
			UseInternalAddr: true,
			Immutable:       true,
		})
	} else {
		log.Info("Removing Trivy scanner")
		uninstallScannerNames = append(uninstallScannerNames, trivyScanner)
	}

	if err := scan.RemoveImmutableScanners(ctx, uninstallScannerNames); err != nil {
		log.Warningf("failed to remove scanners: %v", err)
	}

	if err := scan.EnsureScanners(ctx, wantedScanners); err != nil {
		log.Fatalf("failed to register scanners: %v", err)
	}

	if defaultScannerName := getDefaultScannerName(); defaultScannerName != "" {
		log.Infof("Setting %s as default scanner", defaultScannerName)
		if err := scan.EnsureDefaultScanner(ctx, defaultScannerName); err != nil {
			log.Fatalf("failed to set default scanner: %v", err)
		}
	}
}

func getDefaultScannerName() string {
	if config.WithTrivy() {
		return trivyScanner
	}
	return ""
}
<|endoftext|>


<commit_before>package test161

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	yaml "gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
)

// For simple cases, it is annoying to have to specify the points for the test
// and the command.  So, if the test is made up of only one command, there is
// no reason to specify the command.  So, some rules and convention:
//
// 1) Points must always be specified in the Target (verification)
// 2) Points must always be specified in the Test   (verification)
// 3) Points can be ommitted from the commands, provided that:
//		a) test points - sum(assigned points) % (remaining points) == 0
//		   (i.e. no fractional points per test)

const (
	TARGET_ASST = "asst"
	TARGET_PERF = "perf"
)

const (
	TEST_SCORING_ENTIRE  = "entire"
	TEST_SCORING_PARTIAL = "partial"
)

// A test161 Target is the sepcification for group of related tests. Currently,
// we support two types of Targets with special meaning: asst and perf. The main
// difference between Targets and TestGroups is that Targets can have a
// scoring component, either points or performance. The test161 submission system
// operates in terms of Targets.
type Target struct {
	// Make sure to update isChangeAllowed with any new fields that need to be versioned.
	ID               string        `yaml:"-" bson:"_id"`
	Name             string        `yaml:"name"`
	PrintName        string        `yaml:"print_name" bson:"print_name"`
	Description      string        `yaml:"description"`
	Active           string        `yaml:"active"`
	Version          uint          `yaml:"version"`
	Type             string        `yaml:"type"`
	Points           uint          `yaml:"points"`
	KConfig          string        `yaml:"kconfig"`
	RequiredCommit   string        `yaml:"required_commit" bson:"required_commit"`
	RequiresUserland bool          `yaml:"userland" bson:"userland"`
	Tests            []*TargetTest `yaml:"tests"`
	FileHash         string        `yaml:"-" bson:"file_hash"`
	FileName         string        `yaml:"-" bson:"file_name"`
}

// A TargetTest is the specification for a single Test contained in the Target.
// Currently, the Test can only appear in the Target once.
type TargetTest struct {
	Id            string           `yaml:"id" bson:"test_id"`
	Scoring       string           `yaml:"scoring"`
	Points        uint             `yaml:"points"`
	MemLeakPoints uint             `yaml:"mem_leak_points"`
	Commands      []*TargetCommand `yaml:"commands"`
}

// TargetCommands (optionally) specify information about the commands contained
// in TargetTests. TargetCommands allow you to the points for an individual command
// or override the input arguments.
type TargetCommand struct {
	Id     string   `yaml:"id" bson:cmd_id"` // ID, must match ID in test file
	Index  int      `yaml:"index"`           // Index > 0 => match to index in test
	Points uint     `yaml:"points"`          // Points for this command
	Args   []string `yaml:"args"`            // Argument overrides
}

// TargetListItem is the target detail we send to remote clients about a target
type TargetListItem struct {
	Name        string
	PrintName   string
	Description string
	Active      string
	Type        string
	Version     uint
	Points      uint
	FileName    string
	FileHash    string
	CollabMsg   string
}

// TargetList is the JSON blob sent to clients
type TargetList struct {
	Targets []*TargetListItem
}

// NewTarget creates a new, empty Target with the default type of "asst"
func NewTarget() *Target {
	t := &Target{
		Type: TARGET_ASST,
	}
	return t
}

func (t *Target) fixDefaults() {
	// Ugly, but we need to merge defaults within inner structs
	for _, test := range t.Tests {
		if test.Scoring != TEST_SCORING_PARTIAL {
			test.Scoring = TEST_SCORING_ENTIRE
		}
	}
	if t.Active != "false" {
		t.Active = "true"
	}
}

// TargetFromFile creates a Target object from a yaml file
func TargetFromFile(file string) (*Target, error) {
	var err error

	var info os.FileInfo
	if info, err = os.Stat(file); err != nil {
		return nil, err
	}

	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("Error reading file %v: %v", file, err)
	}

	if t, err := TargetFromString(string(data)); err != nil {
		return t, fmt.Errorf("Error loading target file %v: %v", file, err)
	} else {
		// Save file version and hash
		t.FileName = info.Name()
		raw := md5.Sum(data)
		t.FileHash = strings.ToLower(hex.EncodeToString(raw[:]))
		return t, nil
	}
}

// TargetFromString creates a Target object from a yaml string
func TargetFromString(text string) (*Target, error) {
	t := NewTarget()
	err := yaml.Unmarshal([]byte(text), t)

	if err != nil {
		return nil, err
	}

	t.fixDefaults()

	return t, nil
}

// Map the target test points onto the runnable test
func (tt *TargetTest) applyTo(test *Test) error {
	test.PointsAvailable = tt.Points
	test.ScoringMethod = tt.Scoring
	test.MemLeakPoints = tt.MemLeakPoints

	// We may need to apply arguments and points to each command. In the simplest
	// case, the Target doesn't override command behavior or points and maps all
	// points to the entire test ("entire" scoring). With this method of scoring,
	// all commands must complete successfully in order to gain any (and all)
	// points. In this case, we still need to apply the args, but that's it. For
	// partial scoring, points must be specified for each command that receives any.
	//
	// Before we do that, we need to be able to find the commands. Moreover, we
	// need to make sure the input is sane.  We allow a single instance of a
	// command to apply to multiple command instances, and also a per-instance
	// 1-1 mapping. For example, we may have a test that consists of:
	//		/testbin/forktest
	//		/testbin/forktest
	//		/testbin/forktest
	// If /testbin/forktest is specified once in the TargetTest, with no index,
	// then its point mapping applies to all 3 instances. But, one can also
	// specify different point values for each test.  In this case, we require
	// a 1-1 mapping.

	// Store a mapping of id -> list of command instances so we can (1) verify
	// all instances have been accounted for if indexes are specified and (2)
	// find the command instance to apply points and args to.
	type cmdData struct {
		command *Command
		done    bool
	}

	// id -> list of command instances (there could be more than 1)
	commandInstances := make(map[string][]*cmdData)

	for _, cmd := range test.Commands {
		id := cmd.Id()
		if _, ok := commandInstances[id]; !ok {
			commandInstances[id] = make([]*cmdData, 0)
		}
		commandInstances[id] = append(commandInstances[id], &cmdData{cmd, false})
	}

	// If partial scoring, this eventually needs to match the test points
	pointsAssigned := uint(0)

	// First pass - apply the arguments and command points if partial scoring.
	for _, cmd := range tt.Commands {
		instances, ok := commandInstances[cmd.Id]
		if !ok {
			return errors.New("Cannot find command instance: " + cmd.Id)
		}

		// This only applies to a certain index
		if cmd.Index > 0 {
			if cmd.Index > len(instances) {
				return errors.New("Invalid command index for " + cmd.Id)
			}
			instances = []*cmdData{instances[cmd.Index-1]}
		}

		for _, instance := range instances {
			if instance.done {
				return errors.New("Command instance already instantiated. Command: " + cmd.Id)
			} else {
				instance.done = true
				if len(cmd.Args) > 0 {
					instance.command.Input.replaceArgs(cmd.Args)
				}

				if tt.Scoring == TEST_SCORING_PARTIAL {
					instance.command.PointsAvailable = cmd.Points
					pointsAssigned += cmd.Points
				}
			}
		}
	}

	// Next, verify the following:
	//  1) Exactly all the points were assigned
	//	2) If indexes were specified, all instances were covered

	// (1)
	if tt.Scoring == TEST_SCORING_PARTIAL && pointsAssigned != tt.Points {
		return errors.New(fmt.Sprintf("Invalid partial command point assignment: available (%v) != assigned (%v)",
			tt.Points, pointsAssigned))
	}

	// (2) Verify all instances of specified commands are covered
	for _, cmd := range tt.Commands {
		instances := commandInstances[cmd.Id]
		for _, instance := range instances {
			if !instance.done {
				return errors.New("Unassigned command instance: " + cmd.Id)
			}
		}
		// We only need to check a command id once
		delete(commandInstances, cmd.Id)
	}

	return nil
}

// Instance creates a runnable TestGroup from this Target
func (t *Target) Instance(env *TestEnvironment) (*TestGroup, []error) {

	// First, create a group config and convert it to a TestGroup.
	config := &GroupConfig{
		Name:    t.Name,
		UseDeps: true,
		Env:     env,
	}

	config.Tests = make([]string, 0, len(t.Tests))

	for _, tt := range t.Tests {
		config.Tests = append(config.Tests, tt.Id)
	}

	group, errs := GroupFromConfig(config)
	if len(errs) > 0 {
		return nil, errs
	}

	// We have a runnable group with dependencies.  Next, we need
	// to assign points, scoring method, args, etc.
	total := uint(0)
	for _, tt := range t.Tests {
		test, ok := group.Tests[tt.Id]
		if !ok {
			return nil, []error{errors.New("Cannot find " + tt.Id + " in the TestGroup")}
		}
		if err := tt.applyTo(test); err != nil {
			return nil, []error{err}
		}
		total += tt.Points
	}

	if total != t.Points {
		return nil, []error{fmt.Errorf("Target points (%v) do not match sum(test points) (%v)", t.Points, total)}
	}

	return group, nil
}

// Determine whether or not we'll allow the target to replaced in the DB. If we change
// things like the print name, active flag, etc. we should just update it in the DB.
// But, if we chahnge the tests or points, we should be creating a new version.
func (old *Target) isChangeAllowed(other *Target) error {

	if old.Version != other.Version {
		return errors.New("Mismatched target versions. isChangeAllowed only applies to targets with the same version number.")
	}

	// The ID will be different, and that's OK, as long as we update the right one in the DB.
	if old.Name != other.Name {
		return errors.New("Changing the target name requires a version change")
	}
	if old.Type != other.Type {
		return errors.New("Changing the target type requires a version change")
	}
	if old.Points != other.Points {
		return errors.New("Changing the target points requires a version change")
	}

	// TODO: Relying on no duplicate tests

	// We do care about the tests, just not the order
	if len(old.Tests) != len(other.Tests) {
		return errors.New("Changing the number of tests requiers a version change")
	}

	oldMap := make(map[string]*TargetTest)

	for _, t := range old.Tests {
		oldMap[t.Id] = t
	}

	for _, t := range other.Tests {
		if oldVer, ok := oldMap[t.Id]; !ok {
			return fmt.Errorf("Test %v was removed in the new target, which requires a version change", t.Id)
		} else if oldVer.Points != t.Points {
			return fmt.Errorf("The point distribution for %v changed in the new target, which requires a version change", t.Id)
		} else if oldVer.Scoring != t.Scoring {
			return fmt.Errorf("The scoring method for %v changed in the new target, which requires a version change", t.Id)
		} else if oldVer.MemLeakPoints != t.MemLeakPoints {
			return errors.New("The memory leak points for %v changed in the new target, which requires a version change")
		}
	}

	// Fields we don't care about:
	//
	// PrintName, Description, Active, RequiredCommit
	// KConfig is set based on the Name
	// RequiresUserland: if this was broken, tests would have failed
	// FileHash: this will change
	// FileName: OK if if moves

	return nil
}
<commit_msg>Added leaderboard fields to the target<commit_after>package test161

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	yaml "gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
)

// For simple cases, it is annoying to have to specify the points for the test
// and the command.  So, if the test is made up of only one command, there is
// no reason to specify the command.  So, some rules and convention:
//
// 1) Points must always be specified in the Target (verification)
// 2) Points must always be specified in the Test   (verification)
// 3) Points can be ommitted from the commands, provided that:
//		a) test points - sum(assigned points) % (remaining points) == 0
//		   (i.e. no fractional points per test)

const (
	TARGET_ASST = "asst"
	TARGET_PERF = "perf"
)

const (
	TEST_SCORING_ENTIRE  = "entire"
	TEST_SCORING_PARTIAL = "partial"
)

// A test161 Target is the sepcification for group of related tests. Currently,
// we support two types of Targets with special meaning: asst and perf. The main
// difference between Targets and TestGroups is that Targets can have a
// scoring component, either points or performance. The test161 submission system
// operates in terms of Targets.
type Target struct {
	// Make sure to update isChangeAllowed with any new fields that need to be versioned.
	ID               string        `yaml:"-" bson:"_id"`
	Name             string        `yaml:"name"`
	PrintName        string        `yaml:"print_name" bson:"print_name"`
	Description      string        `yaml:"description"`
	Active           string        `yaml:"active"`
	Version          uint          `yaml:"version"`
	Type             string        `yaml:"type"`
	Points           uint          `yaml:"points"`
	KConfig          string        `yaml:"kconfig"`
	RequiredCommit   string        `yaml:"required_commit" bson:"required_commit"`
	RequiresUserland bool          `yaml:"userland" bson:"userland"`
	Leaderboard      string        `yaml:"leaderboard" bson:"leaderboard"`
	Tests            []*TargetTest `yaml:"tests"`
	FileHash         string        `yaml:"-" bson:"file_hash"`
	FileName         string        `yaml:"-" bson:"file_name"`
}

// A TargetTest is the specification for a single Test contained in the Target.
// Currently, the Test can only appear in the Target once.
type TargetTest struct {
	Id            string           `yaml:"id" bson:"test_id"`
	Scoring       string           `yaml:"scoring"`
	Points        uint             `yaml:"points"`
	MemLeakPoints uint             `yaml:"mem_leak_points"`
	Commands      []*TargetCommand `yaml:"commands"`
}

// TargetCommands (optionally) specify information about the commands contained
// in TargetTests. TargetCommands allow you to the points for an individual command
// or override the input arguments.
type TargetCommand struct {
	Id     string   `yaml:"id" bson:cmd_id"` // ID, must match ID in test file
	Index  int      `yaml:"index"`           // Index > 0 => match to index in test
	Points uint     `yaml:"points"`          // Points for this command
	Args   []string `yaml:"args"`            // Argument overrides
}

// TargetListItem is the target detail we send to remote clients about a target
type TargetListItem struct {
	Name        string
	PrintName   string
	Description string
	Active      string
	Type        string
	Version     uint
	Points      uint
	FileName    string
	FileHash    string
	CollabMsg   string
}

// TargetList is the JSON blob sent to clients
type TargetList struct {
	Targets []*TargetListItem
}

// NewTarget creates a new, empty Target with the default type of "asst"
func NewTarget() *Target {
	t := &Target{
		Type: TARGET_ASST,
	}
	return t
}

func (t *Target) fixDefaults() {
	// Ugly, but we need to merge defaults within inner structs
	for _, test := range t.Tests {
		if test.Scoring != TEST_SCORING_PARTIAL {
			test.Scoring = TEST_SCORING_ENTIRE
		}
	}

	if t.Active != "false" {
		t.Active = "true"
	}

	if t.Leaderboard != "false" {
		t.Leaderboard = "true"
	}
}

// TargetFromFile creates a Target object from a yaml file
func TargetFromFile(file string) (*Target, error) {
	var err error

	var info os.FileInfo
	if info, err = os.Stat(file); err != nil {
		return nil, err
	}

	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("Error reading file %v: %v", file, err)
	}

	if t, err := TargetFromString(string(data)); err != nil {
		return t, fmt.Errorf("Error loading target file %v: %v", file, err)
	} else {
		// Save file version and hash
		t.FileName = info.Name()
		raw := md5.Sum(data)
		t.FileHash = strings.ToLower(hex.EncodeToString(raw[:]))
		return t, nil
	}
}

// TargetFromString creates a Target object from a yaml string
func TargetFromString(text string) (*Target, error) {
	t := NewTarget()
	err := yaml.Unmarshal([]byte(text), t)

	if err != nil {
		return nil, err
	}

	t.fixDefaults()

	return t, nil
}

// Map the target test points onto the runnable test
func (tt *TargetTest) applyTo(test *Test) error {
	test.PointsAvailable = tt.Points
	test.ScoringMethod = tt.Scoring
	test.MemLeakPoints = tt.MemLeakPoints

	// We may need to apply arguments and points to each command. In the simplest
	// case, the Target doesn't override command behavior or points and maps all
	// points to the entire test ("entire" scoring). With this method of scoring,
	// all commands must complete successfully in order to gain any (and all)
	// points. In this case, we still need to apply the args, but that's it. For
	// partial scoring, points must be specified for each command that receives any.
	//
	// Before we do that, we need to be able to find the commands. Moreover, we
	// need to make sure the input is sane.  We allow a single instance of a
	// command to apply to multiple command instances, and also a per-instance
	// 1-1 mapping. For example, we may have a test that consists of:
	//		/testbin/forktest
	//		/testbin/forktest
	//		/testbin/forktest
	// If /testbin/forktest is specified once in the TargetTest, with no index,
	// then its point mapping applies to all 3 instances. But, one can also
	// specify different point values for each test.  In this case, we require
	// a 1-1 mapping.

	// Store a mapping of id -> list of command instances so we can (1) verify
	// all instances have been accounted for if indexes are specified and (2)
	// find the command instance to apply points and args to.
	type cmdData struct {
		command *Command
		done    bool
	}

	// id -> list of command instances (there could be more than 1)
	commandInstances := make(map[string][]*cmdData)

	for _, cmd := range test.Commands {
		id := cmd.Id()
		if _, ok := commandInstances[id]; !ok {
			commandInstances[id] = make([]*cmdData, 0)
		}
		commandInstances[id] = append(commandInstances[id], &cmdData{cmd, false})
	}

	// If partial scoring, this eventually needs to match the test points
	pointsAssigned := uint(0)

	// First pass - apply the arguments and command points if partial scoring.
	for _, cmd := range tt.Commands {
		instances, ok := commandInstances[cmd.Id]
		if !ok {
			return errors.New("Cannot find command instance: " + cmd.Id)
		}

		// This only applies to a certain index
		if cmd.Index > 0 {
			if cmd.Index > len(instances) {
				return errors.New("Invalid command index for " + cmd.Id)
			}
			instances = []*cmdData{instances[cmd.Index-1]}
		}

		for _, instance := range instances {
			if instance.done {
				return errors.New("Command instance already instantiated. Command: " + cmd.Id)
			} else {
				instance.done = true
				if len(cmd.Args) > 0 {
					instance.command.Input.replaceArgs(cmd.Args)
				}

				if tt.Scoring == TEST_SCORING_PARTIAL {
					instance.command.PointsAvailable = cmd.Points
					pointsAssigned += cmd.Points
				}
			}
		}
	}

	// Next, verify the following:
	//  1) Exactly all the points were assigned
	//	2) If indexes were specified, all instances were covered

	// (1)
	if tt.Scoring == TEST_SCORING_PARTIAL && pointsAssigned != tt.Points {
		return errors.New(fmt.Sprintf("Invalid partial command point assignment: available (%v) != assigned (%v)",
			tt.Points, pointsAssigned))
	}

	// (2) Verify all instances of specified commands are covered
	for _, cmd := range tt.Commands {
		instances := commandInstances[cmd.Id]
		for _, instance := range instances {
			if !instance.done {
				return errors.New("Unassigned command instance: " + cmd.Id)
			}
		}
		// We only need to check a command id once
		delete(commandInstances, cmd.Id)
	}

	return nil
}

// Instance creates a runnable TestGroup from this Target
func (t *Target) Instance(env *TestEnvironment) (*TestGroup, []error) {

	// First, create a group config and convert it to a TestGroup.
	config := &GroupConfig{
		Name:    t.Name,
		UseDeps: true,
		Env:     env,
	}

	config.Tests = make([]string, 0, len(t.Tests))

	for _, tt := range t.Tests {
		config.Tests = append(config.Tests, tt.Id)
	}

	group, errs := GroupFromConfig(config)
	if len(errs) > 0 {
		return nil, errs
	}

	// We have a runnable group with dependencies.  Next, we need
	// to assign points, scoring method, args, etc.
	total := uint(0)
	for _, tt := range t.Tests {
		test, ok := group.Tests[tt.Id]
		if !ok {
			return nil, []error{errors.New("Cannot find " + tt.Id + " in the TestGroup")}
		}
		if err := tt.applyTo(test); err != nil {
			return nil, []error{err}
		}
		total += tt.Points
	}

	if total != t.Points {
		return nil, []error{fmt.Errorf("Target points (%v) do not match sum(test points) (%v)", t.Points, total)}
	}

	return group, nil
}

// Determine whether or not we'll allow the target to replaced in the DB. If we change
// things like the print name, active flag, etc. we should just update it in the DB.
// But, if we chahnge the tests or points, we should be creating a new version.
func (old *Target) isChangeAllowed(other *Target) error {

	if old.Version != other.Version {
		return errors.New("Mismatched target versions. isChangeAllowed only applies to targets with the same version number.")
	}

	// The ID will be different, and that's OK, as long as we update the right one in the DB.
	if old.Name != other.Name {
		return errors.New("Changing the target name requires a version change")
	}
	if old.Type != other.Type {
		return errors.New("Changing the target type requires a version change")
	}
	if old.Points != other.Points {
		return errors.New("Changing the target points requires a version change")
	}

	// TODO: Relying on no duplicate tests

	// We do care about the tests, just not the order
	if len(old.Tests) != len(other.Tests) {
		return errors.New("Changing the number of tests requiers a version change")
	}

	oldMap := make(map[string]*TargetTest)

	for _, t := range old.Tests {
		oldMap[t.Id] = t
	}

	for _, t := range other.Tests {
		if oldVer, ok := oldMap[t.Id]; !ok {
			return fmt.Errorf("Test %v was removed in the new target, which requires a version change", t.Id)
		} else if oldVer.Points != t.Points {
			return fmt.Errorf("The point distribution for %v changed in the new target, which requires a version change", t.Id)
		} else if oldVer.Scoring != t.Scoring {
			return fmt.Errorf("The scoring method for %v changed in the new target, which requires a version change", t.Id)
		} else if oldVer.MemLeakPoints != t.MemLeakPoints {
			return errors.New("The memory leak points for %v changed in the new target, which requires a version change")
		}
	}

	// Fields we don't care about:
	//
	// PrintName, Description, Active, RequiredCommit
	// KConfig is set based on the Name
	// RequiresUserland: if this was broken, tests would have failed
	// FileHash: this will change
	// FileName: OK if if moves

	return nil
}
<|endoftext|>