<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	supersort "sort"

	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/parser"
	"github.com/spf13/cobra"
)

var (
	listCmd = &cobra.Command{
		Use:     "list [flags] [args]",
		Short:   "List specifications, scenarios or tags for a gauge project",
		Long:    `List specifications, scenarios or tags for a gauge project`,
		Example: `  gauge list --tags specs`,
		Run: func(cmd *cobra.Command, args []string) {
			loadEnvAndInitLogger(cmd)
			specs, failed := parser.ParseSpecs(getSpecsDir(args), gauge.NewConceptDictionary(), gauge.NewBuildErrors())
			if failed {
				return
			}
			if specsFlag {
				logger.Info(true, "[Specifications]")
				listSpecifications(specs, print)
			}
			if scenariosFlag {
				logger.Info(true, "[Scenarios]")
				listScenarios(specs, print)
			}
			if tagsFlag {
				logger.Info(true, "[Tags]")
				listTags(specs, print)
			}
			if !specsFlag && !scenariosFlag && !tagsFlag {
				exit(fmt.Errorf("Missing flag, nothing to list"), cmd.UsageString())
			}
		},
		DisableAutoGenTag: true,
	}
	tagsFlag      bool
	specsFlag     bool
	scenariosFlag bool
)

func init() {
	GaugeCmd.AddCommand(listCmd)
	listCmd.Flags().BoolVarP(&tagsFlag, "tags", "", false, "List the tags in projects")
	listCmd.Flags().BoolVarP(&specsFlag, "specs", "", false, "List the specifications in projects")
	listCmd.Flags().BoolVarP(&scenariosFlag, "scenarios", "", false, "List the scenarios in projects")
}

type handleResult func([]string)

func print(res []string) {
	for _, element := range res {
		logger.Infof(true, element)
	}
}

func listTags(s []*gauge.Specification, f handleResult) {
	allTags := []string{}
	for _, spec := range s {
		allTags = appendTags(allTags, spec.Tags)
		for _, scenario := range spec.Scenarios {
			allTags = appendTags(allTags, scenario.Tags)
		}
	}
	f(sortedDistinctElements(allTags))
}

func listScenarios(s []*gauge.Specification, f handleResult) {
	allScenarios := filter.GetAllScenarios(s)
	f(sortedDistinctElements(allScenarios))
}

func listSpecifications(s []*gauge.Specification, f handleResult) {
	allSpecs := []string{}
	for _, spec := range s {
		allSpecs = append(allSpecs, spec.Heading.Value)
	}
	f(sortedDistinctElements(allSpecs))
}

func sortedDistinctElements(s []string) []string {
	unique := uniqueNonEmptyElementsOf(s)
	supersort.Strings(unique)
	return unique
}

func appendTags(s []string, tags *gauge.Tags) []string {
	if tags != nil {
		s = append(s, tags.Values()...)
	}
	return s
}

func uniqueNonEmptyElementsOf(input []string) []string {
	unique := make(map[string]bool, len(input))
	us := make([]string, len(unique))
	for _, elem := range input {
		if len(elem) != 0 && !unique[elem] {
			us = append(us, elem)
			unique[elem] = true
		}
	}

	return us

}
<commit_msg>Fixed typo in function name. #1389<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	supersort "sort"

	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/parser"
	"github.com/spf13/cobra"
)

var (
	listCmd = &cobra.Command{
		Use:     "list [flags] [args]",
		Short:   "List specifications, scenarios or tags for a gauge project",
		Long:    `List specifications, scenarios or tags for a gauge project`,
		Example: `  gauge list --tags specs`,
		Run: func(cmd *cobra.Command, args []string) {
			loadEnvAndReinitLogger(cmd)
			specs, failed := parser.ParseSpecs(getSpecsDir(args), gauge.NewConceptDictionary(), gauge.NewBuildErrors())
			if failed {
				return
			}
			if specsFlag {
				logger.Info(true, "[Specifications]")
				listSpecifications(specs, print)
			}
			if scenariosFlag {
				logger.Info(true, "[Scenarios]")
				listScenarios(specs, print)
			}
			if tagsFlag {
				logger.Info(true, "[Tags]")
				listTags(specs, print)
			}
			if !specsFlag && !scenariosFlag && !tagsFlag {
				exit(fmt.Errorf("Missing flag, nothing to list"), cmd.UsageString())
			}
		},
		DisableAutoGenTag: true,
	}
	tagsFlag      bool
	specsFlag     bool
	scenariosFlag bool
)

func init() {
	GaugeCmd.AddCommand(listCmd)
	listCmd.Flags().BoolVarP(&tagsFlag, "tags", "", false, "List the tags in projects")
	listCmd.Flags().BoolVarP(&specsFlag, "specs", "", false, "List the specifications in projects")
	listCmd.Flags().BoolVarP(&scenariosFlag, "scenarios", "", false, "List the scenarios in projects")
}

type handleResult func([]string)

func print(res []string) {
	for _, element := range res {
		logger.Infof(true, element)
	}
}

func listTags(s []*gauge.Specification, f handleResult) {
	allTags := []string{}
	for _, spec := range s {
		allTags = appendTags(allTags, spec.Tags)
		for _, scenario := range spec.Scenarios {
			allTags = appendTags(allTags, scenario.Tags)
		}
	}
	f(sortedDistinctElements(allTags))
}

func listScenarios(s []*gauge.Specification, f handleResult) {
	allScenarios := filter.GetAllScenarios(s)
	f(sortedDistinctElements(allScenarios))
}

func listSpecifications(s []*gauge.Specification, f handleResult) {
	allSpecs := []string{}
	for _, spec := range s {
		allSpecs = append(allSpecs, spec.Heading.Value)
	}
	f(sortedDistinctElements(allSpecs))
}

func sortedDistinctElements(s []string) []string {
	unique := uniqueNonEmptyElementsOf(s)
	supersort.Strings(unique)
	return unique
}

func appendTags(s []string, tags *gauge.Tags) []string {
	if tags != nil {
		s = append(s, tags.Values()...)
	}
	return s
}

func uniqueNonEmptyElementsOf(input []string) []string {
	unique := make(map[string]bool, len(input))
	us := make([]string, len(unique))
	for _, elem := range input {
		if len(elem) != 0 && !unique[elem] {
			us = append(us, elem)
			unique[elem] = true
		}
	}

	return us

}
<|endoftext|>


<commit_before>// Copyright © 2016 Nick Klauer <klauer@gmail.com>
//
// 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 cmd

import (
	"github.com/klauern/trackello"
	"github.com/spf13/cobra"
)

// listCmd represents the list command
var listCmd = &cobra.Command{
	Use:   "list [board id]",
	Short: "List activities on a board",
	Long: `List will pull all the activities for a particular
Trello board and list them in descending order.  This is useful
if you find yourself having to see what you've been working on`,
	Run: ListActivity,
}

var boardId string

func init() {
	RootCmd.AddCommand(listCmd)
}

func ListActivity(cmd *cobra.Command, args []string) {
	switch {
	case len(args) > 0:
		trackello.Track(args[0])
	case boardId != "":
		trackello.Track(boardId)
	default:
		panic("No board id specified in either boardId or on command-line.")
	}

	// pseudocode for listing things
	//
	// 1. create connection
	// 2. get board
	// 3. get lists on board
	// 6. map cards to lists
	// 4. get actions on board
	// 5. map actions to cards
	// 7. add statistics to card
}
<commit_msg>document steps to list cards<commit_after>// Copyright © 2016 Nick Klauer <klauer@gmail.com>
//
// 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 cmd

import (
	"github.com/klauern/trackello"
	"github.com/spf13/cobra"
)

// listCmd represents the list command
var listCmd = &cobra.Command{
	Use:   "list [board id]",
	Short: "List activities on a board",
	Long: `List will pull all the activities for a particular
Trello board and list them in descending order.  This is useful
if you find yourself having to see what you've been working on`,
	Run: ListActivity,
}

var boardId string

func init() {
	RootCmd.AddCommand(listCmd)
}

func ListActivity(cmd *cobra.Command, args []string) {
	switch {
	case len(args) > 0:
		trackello.Track(args[0])
	case boardId != "":
		trackello.Track(boardId)
	default:
		panic("No board id specified in either boardId or on command-line.")
	}

	// pseudocode for listing things
	//
	// 1. create connection
	// 2. get board
	// 4. get actions on board
	// -- in parallel
	//    #. map action to card
	//		 - add calculation to statistics
	//    #. map action to list
	//		 - add calculation to statistics
	// 6. map cards to lists
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"strconv"

	"time"

	"github.com/jmoiron/sqlx"
	"github.com/vincentserpoul/playwithsql/dbhandler"
	"github.com/vincentserpoul/playwithsql/status"
)

func main() {

	dbName := "playwithsql"
	dbType := flag.String("db", "mysql", "type of db to bench: mysql, cockroachdb, postgres")
	dbHost := flag.String("host", "localhost", "host IP")
	loopsStr := flag.String("loops", "10000", "number of loops")
	flag.Parse()

	loops, err := strconv.ParseInt(*loopsStr, 10, 64)
	if err != nil {
		log.Fatalf("%v", err)
	}

	db, err := dbhandler.Get(*dbType, *dbHost, dbName)
	if err != nil {
		log.Fatalf("%v", err)
	}

	islatestSQLLink := status.GetSQLLinkContainer(*dbType)
	err = islatestSQLLink.MigrateDown(db)
	if err != nil {
		log.Fatalf("%v", err)
	}
	err = islatestSQLLink.MigrateUp(db)
	if err != nil {
		log.Fatalf("%v", err)
	}

	createTimeTaken, testEntityoneIDs, err := BenchmarkCreate(int(loops), db, islatestSQLLink)
	if err != nil {
		log.Fatalf("%v", err)
	}

	log.Printf("%s;create;%d;%d;%d", *dbType, loops, createTimeTaken.Nanoseconds(), len(testEntityoneIDs))
}

// BenchmarkCreate will loop a loops number of time and give the resulting time taken
func BenchmarkCreate(loops int, dbConn *sqlx.DB, benchSQLLink *status.SQLLinkContainer) (
	timeTaken time.Duration,
	testEntityoneIDs []int64,
	err error,
) {
	before := time.Now()
	for i := 0; i < loops; i++ {
		var e status.Entityone
		errCr := e.Create(dbConn, benchSQLLink)
		if errCr != nil {
			return timeTaken, testEntityoneIDs, errCr
		}
		i++
		// limit the number of entities to store
		if len(testEntityoneIDs) < 500 {
			testEntityoneIDs = append(testEntityoneIDs, e.ID)
		}
	}

	after := time.Now()
	timeTaken = after.Sub(before)

	return timeTaken, testEntityoneIDs, err
}

// func BenchmarkUpdateStatus(b *testing.B) {
// 	var e Entityone
// 	for i := 0; i < b.N; i++ {
// 		e.ID = testEntityoneIDs[b.N%len(testEntityoneIDs)]
// 		_ = e.UpdateStatus(testDBConn, testSQLLink, ActionCancel, StatusCancelled)
// 	}
// }

// func BenchmarkSelectEntityoneByStatus(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		_, err := SelectEntityoneByStatus(testDBConn, testSQLLink, StatusCreated)
// 		if err != nil {
// 			b.Errorf("Select entityone by status: %v", err)
// 			return
// 		}
// 	}
// }

// func BenchmarkSelectEntityoneOneByPK(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		_, err := SelectEntityoneOneByPK(testDBConn, testSQLLink, testEntityoneIDs[b.N%len(testEntityoneIDs)])
// 		if err != nil {
// 			b.Errorf("Select entityone by status: %v", err)
// 			return
// 		}
// 	}
// }
<commit_msg>add update, selectbystatus, selectbypk<commit_after>package main

import (
	"flag"
	"log"
	"strconv"

	"time"

	"github.com/jmoiron/sqlx"
	"github.com/vincentserpoul/playwithsql/dbhandler"
	"github.com/vincentserpoul/playwithsql/status"
)

func main() {

	dbName := "playwithsql"
	dbType := flag.String("db", "mysql", "type of db to bench: mysql, cockroachdb, postgres")
	dbHost := flag.String("host", "localhost", "host IP")
	loopsStr := flag.String("loops", "10000", "number of loops")
	flag.Parse()

	loops, err := strconv.ParseInt(*loopsStr, 10, 64)
	if err != nil {
		log.Fatalf("%v", err)
	}

	db, err := dbhandler.Get(*dbType, *dbHost, dbName)
	if err != nil {
		log.Fatalf("%v", err)
	}

	islatestSQLLink := status.GetSQLLinkContainer(*dbType)
	err = islatestSQLLink.MigrateDown(db)
	if err != nil {
		log.Fatalf("%v", err)
	}
	err = islatestSQLLink.MigrateUp(db)
	if err != nil {
		log.Fatalf("%v", err)
	}

	// Create
	createTimeTaken, testEntityoneIDs, err := BenchmarkCreate(int(loops), db, islatestSQLLink)
	if err != nil {
		log.Fatalf("%v", err)
	}
	log.Printf("%s;create;%d;%d\n", *dbType, loops, createTimeTaken.Nanoseconds())

	// Update
	updateTimeTaken, err := BenchmarkUpdateStatus(int(loops), db, islatestSQLLink, testEntityoneIDs)
	if err != nil {
		log.Fatalf("%v", err)
	}
	log.Printf("%s;update;%d;%d\n", *dbType, loops, updateTimeTaken.Nanoseconds())

	// Select by status
	selectByStatusTimeTaken, err := BenchmarkSelectEntityoneByStatus(int(loops), db, islatestSQLLink)
	if err != nil {
		log.Fatalf("%v", err)
	}
	log.Printf("%s;selectByStatus;%d;%d\n", *dbType, loops, selectByStatusTimeTaken.Nanoseconds())

	// Select by PK
	selectByPKTimeTaken, err := BenchmarkSelectEntityoneOneByPK(int(loops), db, islatestSQLLink, testEntityoneIDs)
	if err != nil {
		log.Fatalf("%v", err)
	}
	log.Printf("%s;selectByPK;%d;%d\n", *dbType, loops, selectByPKTimeTaken.Nanoseconds())
}

// BenchmarkCreate will loop a loops number of time and give the resulting time taken
func BenchmarkCreate(loops int, dbConn *sqlx.DB, benchSQLLink *status.SQLLinkContainer) (
	timeTaken time.Duration,
	testEntityoneIDs []int64,
	err error,
) {
	before := time.Now()
	for i := 0; i < loops; i++ {
		var e status.Entityone
		errCr := e.Create(dbConn, benchSQLLink)
		if errCr != nil {
			return timeTaken, testEntityoneIDs, errCr
		}
		i++
		// limit the number of entities to store
		if len(testEntityoneIDs) < 500 {
			testEntityoneIDs = append(testEntityoneIDs, e.ID)
		}
	}

	after := time.Now()
	timeTaken = after.Sub(before)

	return timeTaken, testEntityoneIDs, err
}

// BenchmarkUpdateStatus benchmark for status updates (include deletes)
func BenchmarkUpdateStatus(loops int, dbConn *sqlx.DB, benchSQLLink *status.SQLLinkContainer, testEntityoneIDs []int64) (
	timeTaken time.Duration,
	err error,
) {
	var e status.Entityone
	before := time.Now()

	for i := 0; i < loops; i++ {
		e.ID = testEntityoneIDs[i%len(testEntityoneIDs)]
		_ = e.UpdateStatus(dbConn, benchSQLLink, status.ActionCancel, status.StatusCancelled)
	}

	after := time.Now()
	timeTaken = after.Sub(before)

	return timeTaken, err
}

// BenchmarkSelectEntityoneByStatus benchmark with select by status
func BenchmarkSelectEntityoneByStatus(loops int, dbConn *sqlx.DB, benchSQLLink *status.SQLLinkContainer) (
	timeTaken time.Duration,
	err error,
) {
	before := time.Now()
	for i := 0; i < loops; i++ {
		_, errSel := status.SelectEntityoneByStatus(dbConn, benchSQLLink, status.StatusCancelled)
		if errSel != nil {
			return timeTaken, errSel
		}
	}

	after := time.Now()
	timeTaken = after.Sub(before)

	return timeTaken, nil
}

// BenchmarkSelectEntityoneOneByPK benchmark with select by primary key
func BenchmarkSelectEntityoneOneByPK(loops int, dbConn *sqlx.DB, benchSQLLink *status.SQLLinkContainer, testEntityoneIDs []int64) (
	timeTaken time.Duration,
	err error,
) {
	before := time.Now()
	for i := 0; i < loops; i++ {
		_, errSel := status.SelectEntityoneOneByPK(dbConn, benchSQLLink, testEntityoneIDs[i%len(testEntityoneIDs)])
		if err != nil {
			return timeTaken, errSel
		}
	}
	after := time.Now()
	timeTaken = after.Sub(before)

	return timeTaken, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/cbednarski/hostess"
	"github.com/codegangsta/cli"
	"os"
)

func getCommand() string {
	return os.Args[1]
}

func getArgs() []string {
	return os.Args[2:]
}

const help = `Hostess: an idempotent tool for managing /etc/hosts

Commands will exit 0 or 1 in a sensible way so you can use the exit code for
bash and make scripting. Add -h to any command to learn more about it.

To preview a hostess-managed hostsfile run ` + "`" + `hostess fix -n` + "`" + `

WARNING: This program is BETA and not all commands are implemented.

Commands:

   add     Add (or update) a hosts entry
   del     Delete a hosts entry
   has     Exit 0 if entry exists, 1 if not
   off     Disable a hosts entry (don't delete it)
   on      Enable a hosts entry (if if exists)
   ls      List entries in the hosts file
   list    Alias for ls
   fix     Reformat the hosts file based on hostess' rules
   dump    Dump the hosts file as JSON
   apply   Apply a JSON hosts dict to your hosts file

Options:

   -f   Force write to the hostsfile even if there are errors or conflicts
   -n   No-op. Show changes but don't write them.
   -q   Supress error messages
   -s   Supress success messages (implies -q)
   -h   Show help for a command

Note: You can specify the HOSTESS_FILE environment variable to operate on a
file other than /etc/hosts

Report bugs at https://github.com/cbednarski/hostess
`

func main() {
	app := cli.NewApp()
	app.Name = "hostess"
	app.Usage = help
	app.Version = "0.1.0"

	var err error = nil

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "f",
			Usage: "Force",
		},
		cli.BoolFlag{
			Name:  "n",
			Usage: "Noop",
		},
		cli.BoolFlag{
			Name:  "q",
			Usage: "Quiet",
		},
		cli.BoolFlag{
			Name:  "s",
			Usage: "Silent",
		},
	}

	app.Commands = []cli.Command{
		{
			Name: "add",
			// Usage: "add a task to the list",
			Action: hostess.Add,
			Flags:  app.Flags,
		},
		{
			Name: "del",
			// Usage: "add a task to the list",
			Action: hostess.Del,
			Flags:  app.Flags,
		},
		{
			Name: "has",
			// Usage: "add a task to the list",
			Action: hostess.Has,
			Flags:  app.Flags,
		},
		{
			Name: "off",
			// Usage: "add a task to the list",
			Action: hostess.Off,
			Flags:  app.Flags,
		},
		{
			Name: "on",
			// Usage: "add a task to the list",
			Action: hostess.On,
			Flags:  app.Flags,
		},
		{
			Name: "ls",
			// Usage: "add a task to the list",
			Action: hostess.Ls,
			Flags:  app.Flags,
		},
		{
			Name: "fix",
			// Usage: "add a task to the list",
			Action: hostess.Fix,
			Flags:  app.Flags,
		},
		{
			Name: "dump",
			// Usage: "add a task to the list",
			Action: hostess.Dump,
			Flags:  app.Flags,
		},
		{
			Name: "apply",
			// Usage: "add a task to the list",
			Action: hostess.Apply,
			Flags:  app.Flags,
		},
	}

	// switch flag.Arg(0) {
	// case "add":
	// 	err = hostess.Add(args, flags)
	// case "del":
	// 	err = hostess.Del(args, flags)
	// case "has":
	// 	err = hostess.Has(args, flags)
	// case "off":
	// 	err = hostess.Off(args, flags)
	// case "on":
	// 	err = hostess.On(args, flags)
	// case "ls":
	// 	err = hostess.Ls(args, flags)
	// case "fix":
	// 	err = hostess.Fix(args, flags)
	// case "dump":
	// 	err = hostess.Dump(args, flags)
	// case "apply":
	// 	err = hostess.Apply(args, flags)

	// default:
	// 	fmt.Print(help)
	// }

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	app.Run(os.Args)

	os.Exit(0)
}
<commit_msg>Cleanup old code<commit_after>package main

import (
	"github.com/cbednarski/hostess"
	"github.com/codegangsta/cli"
	"os"
)

func getCommand() string {
	return os.Args[1]
}

func getArgs() []string {
	return os.Args[2:]
}

const help = `Hostess: an idempotent tool for managing /etc/hosts

Commands will exit 0 or 1 in a sensible way so you can use the exit code for
bash and make scripting. Add -h to any command to learn more about it.

To preview a hostess-managed hostsfile run ` + "`" + `hostess fix -n` + "`" + `

WARNING: This program is BETA and not all commands are implemented.

Commands:

   add     Add (or update) a hosts entry
   del     Delete a hosts entry
   has     Exit 0 if entry exists, 1 if not
   off     Disable a hosts entry (don't delete it)
   on      Enable a hosts entry (if if exists)
   ls      List entries in the hosts file
   list    Alias for ls
   fix     Reformat the hosts file based on hostess' rules
   dump    Dump the hosts file as JSON
   apply   Apply a JSON hosts dict to your hosts file

Options:

   -f   Force write to the hostsfile even if there are errors or conflicts
   -n   No-op. Show changes but don't write them.
   -q   Supress error messages
   -s   Supress success messages (implies -q)
   -h   Show help for a command

Note: You can specify the HOSTESS_FILE environment variable to operate on a
file other than /etc/hosts

Report bugs at https://github.com/cbednarski/hostess
`

func main() {
	app := cli.NewApp()
	app.Name = "hostess"
	app.Usage = help
	app.Version = "0.1.0"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "f",
			Usage: "Force",
		},
		cli.BoolFlag{
			Name:  "n",
			Usage: "Noop",
		},
		cli.BoolFlag{
			Name:  "q",
			Usage: "Quiet",
		},
		cli.BoolFlag{
			Name:  "s",
			Usage: "Silent",
		},
	}

	app.Commands = []cli.Command{
		{
			Name: "add",
			// Usage: "add a task to the list",
			Action: hostess.Add,
			Flags:  app.Flags,
		},
		{
			Name: "del",
			// Usage: "add a task to the list",
			Action: hostess.Del,
			Flags:  app.Flags,
		},
		{
			Name: "has",
			// Usage: "add a task to the list",
			Action: hostess.Has,
			Flags:  app.Flags,
		},
		{
			Name: "off",
			// Usage: "add a task to the list",
			Action: hostess.Off,
			Flags:  app.Flags,
		},
		{
			Name: "on",
			// Usage: "add a task to the list",
			Action: hostess.On,
			Flags:  app.Flags,
		},
		{
			Name: "ls",
			// Usage: "add a task to the list",
			Action: hostess.Ls,
			Flags:  app.Flags,
		},
		{
			Name: "fix",
			// Usage: "add a task to the list",
			Action: hostess.Fix,
			Flags:  app.Flags,
		},
		{
			Name: "dump",
			// Usage: "add a task to the list",
			Action: hostess.Dump,
			Flags:  app.Flags,
		},
		{
			Name: "apply",
			// Usage: "add a task to the list",
			Action: hostess.Apply,
			Flags:  app.Flags,
		},
	}

	app.Run(os.Args)
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "drone"
	app.Version = "1.0"
	app.Usage = "command line utility"
	app.EnableBashCompletion = true

	app.Commands = []cli.Command{
		NewBuildCommand(),
		NewEnableCommand(),
		NewDisableCommand(),
		NewRestartCommand(),
		NewWhoamiCommand(),
	}

	app.Run(os.Args)
}
<commit_msg>remove bash completion<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "drone"
	app.Version = "1.0"
	app.Usage = "command line utility"

	app.Commands = []cli.Command{
		NewBuildCommand(),
		NewEnableCommand(),
		NewDisableCommand(),
		NewRestartCommand(),
		NewWhoamiCommand(),
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/alexei-led/pumba/pkg/chaos"
	"github.com/alexei-led/pumba/pkg/chaos/docker/cmd"
	netemCmd "github.com/alexei-led/pumba/pkg/chaos/netem/cmd"
	"github.com/alexei-led/pumba/pkg/container"
	"github.com/alexei-led/pumba/pkg/logger"

	log "github.com/sirupsen/logrus"

	"github.com/urfave/cli"

	"github.com/johntdyer/slackrus"
)

var (
	topContext context.Context
)

var (
	// Version that is passed on compile time through -ldflags
	Version = "built locally"

	// GitCommit that is passed on compile time through -ldflags
	GitCommit = "none"

	// GitBranch that is passed on compile time through -ldflags
	GitBranch = "none"

	// BuildTime that is passed on compile time through -ldflags
	BuildTime = "none"

	// HumanVersion is a human readable app version
	HumanVersion = fmt.Sprintf("%s - %.7s (%s) %s", Version, GitCommit, GitBranch, BuildTime)
)

const (
	// Re2Prefix re2 regexp string prefix
	Re2Prefix = "re2:"
	// DefaultInterface default network interface
	DefaultInterface = "eth0"
)

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

func init() {
	// set log level
	log.SetLevel(log.WarnLevel)
	log.SetFormatter(&log.TextFormatter{})
	// handle termination signal
	topContext = handleSignals()
}

func main() {
	rootCertPath := "/etc/ssl/docker"

	if os.Getenv("DOCKER_CERT_PATH") != "" {
		rootCertPath = os.Getenv("DOCKER_CERT_PATH")
	}

	app := cli.NewApp()
	app.Name = "Pumba"
	app.Version = HumanVersion
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		{
			Name:  "Alexei Ledenev",
			Email: "alexei.led@gmail.com",
		},
	}
	app.EnableBashCompletion = true
	app.Usage = "Pumba is a resilience testing tool, that helps applications tolerate random Docker container failures: process, network and performance."
	app.ArgsUsage = fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q)", Re2Prefix)
	app.Before = before
	app.Commands = []cli.Command{
		*cmd.NewKillCLICommand(topContext),
		*cmd.NewStopCLICommand(topContext),
		*cmd.NewPauseCLICommand(topContext),
		*cmd.NewRemoveCLICommand(topContext),
		{
			Name: "netem",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "duration, d",
					Usage: "network emulation duration; should be smaller than recurrent interval; use with optional unit suffix: 'ms/s/m/h'",
				},
				cli.StringFlag{
					Name:  "interface, i",
					Usage: "network interface to apply delay on",
					Value: DefaultInterface,
				},
				cli.StringSliceFlag{
					Name:  "target, t",
					Usage: "target IP filter; supports multiple IPs",
				},
				cli.StringFlag{
					Name:  "tc-image",
					Usage: "Docker image with tc (iproute2 package); try 'gaiadocker/iproute2'",
				},
			},
			Usage:       "emulate the properties of wide area networks",
			ArgsUsage:   fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q", Re2Prefix),
			Description: "delay, loss, duplicate and re-order (run 'netem') packets, and limit the bandwidth, to emulate different network problems",
			Subcommands: []cli.Command{
				*netemCmd.NewDelayCLICommand(topContext),
				*netemCmd.NewLossCLICommand(topContext),
				*netemCmd.NewLossStateCLICommand(topContext),
				*netemCmd.NewLossGECLICommand(topContext),
				*netemCmd.NewRateCLICommand(topContext),
				*netemCmd.NewDuplicateCLICommand(topContext),
				*netemCmd.NewCorruptCLICommand(topContext),
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "host, H",
			Usage:  "daemon socket to connect to",
			Value:  "unix:///var/run/docker.sock",
			EnvVar: "DOCKER_HOST",
		},
		cli.BoolFlag{
			Name:  "tls",
			Usage: "use TLS; implied by --tlsverify",
		},
		cli.BoolFlag{
			Name:   "tlsverify",
			Usage:  "use TLS and verify the remote",
			EnvVar: "DOCKER_TLS_VERIFY",
		},
		cli.StringFlag{
			Name:  "tlscacert",
			Usage: "trust certs signed only by this CA",
			Value: fmt.Sprintf("%s/ca.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlscert",
			Usage: "client certificate for TLS authentication",
			Value: fmt.Sprintf("%s/cert.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlskey",
			Usage: "client key for TLS authentication",
			Value: fmt.Sprintf("%s/key.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:   "log-level, l",
			Usage:  "set log level (debug, info, warning(*), error, fatal, panic)",
			Value:  "warning",
			EnvVar: "LOG_LEVEL",
		},
		cli.BoolFlag{
			Name:   "json, j",
			Usage:  "produce log in JSON format: Logstash and Splunk friendly",
			EnvVar: "LOG_JSON",
		},
		cli.StringFlag{
			Name:  "slackhook",
			Usage: "web hook url; send Pumba log events to Slack",
		},
		cli.StringFlag{
			Name:  "slackchannel",
			Usage: "Slack channel (default #pumba)",
			Value: "#pumba",
		},
		cli.StringFlag{
			Name:  "interval, i",
			Usage: "recurrent interval for chaos command; use with optional unit suffix: 'ms/s/m/h'",
		},
		cli.BoolFlag{
			Name:  "random, r",
			Usage: "randomly select single matching container from list of target containers",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "dry run does not create chaos, only logs planned chaos commands",
			EnvVar: "DRY-RUN",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func before(c *cli.Context) error {
	// set debug log level
	switch level := c.GlobalString("log-level"); level {
	case "debug", "DEBUG":
		log.SetLevel(log.DebugLevel)
	case "info", "INFO":
		log.SetLevel(log.InfoLevel)
	case "warning", "WARNING":
		log.SetLevel(log.WarnLevel)
	case "error", "ERROR":
		log.SetLevel(log.ErrorLevel)
	case "fatal", "FATAL":
		log.SetLevel(log.FatalLevel)
	case "panic", "PANIC":
		log.SetLevel(log.PanicLevel)
	default:
		log.SetLevel(log.WarnLevel)
	}
	// set log formatter to JSON
	if c.GlobalBool("json") {
		log.SetFormatter(&log.JSONFormatter{})
	}
	// set Slack log channel
	if c.GlobalString("slackhook") != "" {
		log.AddHook(&slackrus.SlackrusHook{
			HookURL:        c.GlobalString("slackhook"),
			AcceptedLevels: slackrus.LevelThreshold(log.GetLevel()),
			Channel:        c.GlobalString("slackchannel"),
			IconEmoji:      ":boar:",
			Username:       "pumba_bot",
		})
	}
	// trace function calls
	traceHook := logger.NewHook()
	traceHook.AppName = "pumba"
	log.AddHook(traceHook)
	// Set-up container client
	tls, err := tlsConfig(c)
	if err != nil {
		return err
	}
	// create new Docker client
	chaos.DockerClient = container.NewClient(c.GlobalString("host"), tls)
	return nil
}

func handleSignals() context.Context {
	// Graceful shut-down on SIGINT/SIGTERM
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

	// create cancelable context
	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		defer cancel()
		sid := <-sig
		log.Debugf("Received signal: %d", sid)
		log.Debug("Canceling running chaos commands ...")
		log.Debug("Gracefully exiting after some cleanup ...")
	}()

	return ctx
}

// tlsConfig translates the command-line options into a tls.Config struct
func tlsConfig(c *cli.Context) (*tls.Config, error) {
	var tlsConfig *tls.Config
	var err error
	caCertFlag := c.GlobalString("tlscacert")
	certFlag := c.GlobalString("tlscert")
	keyFlag := c.GlobalString("tlskey")

	if c.GlobalBool("tls") || c.GlobalBool("tlsverify") {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: !c.GlobalBool("tlsverify"),
		}

		// Load CA cert
		if caCertFlag != "" {
			var caCert []byte
			if strings.HasPrefix(caCertFlag, "/") {
				caCert, err = ioutil.ReadFile(caCertFlag)
				if err != nil {
					return nil, err
				}
			} else {
				caCert = []byte(caCertFlag)
			}
			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)
			tlsConfig.RootCAs = caCertPool
		}

		// Load client certificate
		if certFlag != "" && keyFlag != "" {
			var cert tls.Certificate
			if strings.HasPrefix(certFlag, "/") && strings.HasPrefix(keyFlag, "/") {
				cert, err = tls.LoadX509KeyPair(certFlag, keyFlag)
				if err != nil {
					return nil, err
				}
			} else {
				cert, err = tls.X509KeyPair([]byte(certFlag), []byte(keyFlag))
				if err != nil {
					return nil, err
				}
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}
	}
	return tlsConfig, nil
}
<commit_msg>Refactor: Initialize CLI Commands in a separate func. (#99)<commit_after>package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/alexei-led/pumba/pkg/chaos"
	"github.com/alexei-led/pumba/pkg/chaos/docker/cmd"
	netemCmd "github.com/alexei-led/pumba/pkg/chaos/netem/cmd"
	"github.com/alexei-led/pumba/pkg/container"
	"github.com/alexei-led/pumba/pkg/logger"

	log "github.com/sirupsen/logrus"

	"github.com/urfave/cli"

	"github.com/johntdyer/slackrus"
)

var (
	topContext context.Context
)

var (
	// Version that is passed on compile time through -ldflags
	Version = "built locally"

	// GitCommit that is passed on compile time through -ldflags
	GitCommit = "none"

	// GitBranch that is passed on compile time through -ldflags
	GitBranch = "none"

	// BuildTime that is passed on compile time through -ldflags
	BuildTime = "none"

	// HumanVersion is a human readable app version
	HumanVersion = fmt.Sprintf("%s - %.7s (%s) %s", Version, GitCommit, GitBranch, BuildTime)
)

const (
	// Re2Prefix re2 regexp string prefix
	Re2Prefix = "re2:"
	// DefaultInterface default network interface
	DefaultInterface = "eth0"
)

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

func init() {
	// set log level
	log.SetLevel(log.WarnLevel)
	log.SetFormatter(&log.TextFormatter{})
	// handle termination signal
	topContext = handleSignals()
}

func main() {
	rootCertPath := "/etc/ssl/docker"

	if os.Getenv("DOCKER_CERT_PATH") != "" {
		rootCertPath = os.Getenv("DOCKER_CERT_PATH")
	}

	app := cli.NewApp()
	app.Name = "Pumba"
	app.Version = HumanVersion
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		{
			Name:  "Alexei Ledenev",
			Email: "alexei.led@gmail.com",
		},
	}
	app.EnableBashCompletion = true
	app.Usage = "Pumba is a resilience testing tool, that helps applications tolerate random Docker container failures: process, network and performance."
	app.ArgsUsage = fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q)", Re2Prefix)
	app.Before = before
	app.Commands = initializeCLICommands()
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "host, H",
			Usage:  "daemon socket to connect to",
			Value:  "unix:///var/run/docker.sock",
			EnvVar: "DOCKER_HOST",
		},
		cli.BoolFlag{
			Name:  "tls",
			Usage: "use TLS; implied by --tlsverify",
		},
		cli.BoolFlag{
			Name:   "tlsverify",
			Usage:  "use TLS and verify the remote",
			EnvVar: "DOCKER_TLS_VERIFY",
		},
		cli.StringFlag{
			Name:  "tlscacert",
			Usage: "trust certs signed only by this CA",
			Value: fmt.Sprintf("%s/ca.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlscert",
			Usage: "client certificate for TLS authentication",
			Value: fmt.Sprintf("%s/cert.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlskey",
			Usage: "client key for TLS authentication",
			Value: fmt.Sprintf("%s/key.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:   "log-level, l",
			Usage:  "set log level (debug, info, warning(*), error, fatal, panic)",
			Value:  "warning",
			EnvVar: "LOG_LEVEL",
		},
		cli.BoolFlag{
			Name:   "json, j",
			Usage:  "produce log in JSON format: Logstash and Splunk friendly",
			EnvVar: "LOG_JSON",
		},
		cli.StringFlag{
			Name:  "slackhook",
			Usage: "web hook url; send Pumba log events to Slack",
		},
		cli.StringFlag{
			Name:  "slackchannel",
			Usage: "Slack channel (default #pumba)",
			Value: "#pumba",
		},
		cli.StringFlag{
			Name:  "interval, i",
			Usage: "recurrent interval for chaos command; use with optional unit suffix: 'ms/s/m/h'",
		},
		cli.BoolFlag{
			Name:  "random, r",
			Usage: "randomly select single matching container from list of target containers",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "dry run does not create chaos, only logs planned chaos commands",
			EnvVar: "DRY-RUN",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func before(c *cli.Context) error {
	// set debug log level
	switch level := c.GlobalString("log-level"); level {
	case "debug", "DEBUG":
		log.SetLevel(log.DebugLevel)
	case "info", "INFO":
		log.SetLevel(log.InfoLevel)
	case "warning", "WARNING":
		log.SetLevel(log.WarnLevel)
	case "error", "ERROR":
		log.SetLevel(log.ErrorLevel)
	case "fatal", "FATAL":
		log.SetLevel(log.FatalLevel)
	case "panic", "PANIC":
		log.SetLevel(log.PanicLevel)
	default:
		log.SetLevel(log.WarnLevel)
	}
	// set log formatter to JSON
	if c.GlobalBool("json") {
		log.SetFormatter(&log.JSONFormatter{})
	}
	// set Slack log channel
	if c.GlobalString("slackhook") != "" {
		log.AddHook(&slackrus.SlackrusHook{
			HookURL:        c.GlobalString("slackhook"),
			AcceptedLevels: slackrus.LevelThreshold(log.GetLevel()),
			Channel:        c.GlobalString("slackchannel"),
			IconEmoji:      ":boar:",
			Username:       "pumba_bot",
		})
	}
	// trace function calls
	traceHook := logger.NewHook()
	traceHook.AppName = "pumba"
	log.AddHook(traceHook)
	// Set-up container client
	tls, err := tlsConfig(c)
	if err != nil {
		return err
	}
	// create new Docker client
	chaos.DockerClient = container.NewClient(c.GlobalString("host"), tls)
	return nil
}

func handleSignals() context.Context {
	// Graceful shut-down on SIGINT/SIGTERM
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

	// create cancelable context
	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		defer cancel()
		sid := <-sig
		log.Debugf("Received signal: %d", sid)
		log.Debug("Canceling running chaos commands ...")
		log.Debug("Gracefully exiting after some cleanup ...")
	}()

	return ctx
}

// tlsConfig translates the command-line options into a tls.Config struct
func tlsConfig(c *cli.Context) (*tls.Config, error) {
	var tlsConfig *tls.Config
	var err error
	caCertFlag := c.GlobalString("tlscacert")
	certFlag := c.GlobalString("tlscert")
	keyFlag := c.GlobalString("tlskey")

	if c.GlobalBool("tls") || c.GlobalBool("tlsverify") {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: !c.GlobalBool("tlsverify"),
		}

		// Load CA cert
		if caCertFlag != "" {
			var caCert []byte
			if strings.HasPrefix(caCertFlag, "/") {
				caCert, err = ioutil.ReadFile(caCertFlag)
				if err != nil {
					return nil, err
				}
			} else {
				caCert = []byte(caCertFlag)
			}
			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)
			tlsConfig.RootCAs = caCertPool
		}

		// Load client certificate
		if certFlag != "" && keyFlag != "" {
			var cert tls.Certificate
			if strings.HasPrefix(certFlag, "/") && strings.HasPrefix(keyFlag, "/") {
				cert, err = tls.LoadX509KeyPair(certFlag, keyFlag)
				if err != nil {
					return nil, err
				}
			} else {
				cert, err = tls.X509KeyPair([]byte(certFlag), []byte(keyFlag))
				if err != nil {
					return nil, err
				}
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}
	}
	return tlsConfig, nil
}

func initializeCLICommands() []cli.Command {
	return []cli.Command{
		*cmd.NewKillCLICommand(topContext),
		*cmd.NewStopCLICommand(topContext),
		*cmd.NewPauseCLICommand(topContext),
		*cmd.NewRemoveCLICommand(topContext),
		{
			Name: "netem",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "duration, d",
					Usage: "network emulation duration; should be smaller than recurrent interval; use with optional unit suffix: 'ms/s/m/h'",
				},
				cli.StringFlag{
					Name:  "interface, i",
					Usage: "network interface to apply delay on",
					Value: DefaultInterface,
				},
				cli.StringSliceFlag{
					Name:  "target, t",
					Usage: "target IP filter; supports multiple IPs",
				},
				cli.StringFlag{
					Name:  "tc-image",
					Usage: "Docker image with tc (iproute2 package); try 'gaiadocker/iproute2'",
				},
			},
			Usage:       "emulate the properties of wide area networks",
			ArgsUsage:   fmt.Sprintf("containers (name, list of names, or RE2 regex if prefixed with %q", Re2Prefix),
			Description: "delay, loss, duplicate and re-order (run 'netem') packets, and limit the bandwidth, to emulate different network problems",
			Subcommands: []cli.Command{
				*netemCmd.NewDelayCLICommand(topContext),
				*netemCmd.NewLossCLICommand(topContext),
				*netemCmd.NewLossStateCLICommand(topContext),
				*netemCmd.NewLossGECLICommand(topContext),
				*netemCmd.NewRateCLICommand(topContext),
				*netemCmd.NewDuplicateCLICommand(topContext),
				*netemCmd.NewCorruptCLICommand(topContext),
			},
		},
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"time"

	"github.com/mobingi/mobingi-cli/client/timeout"
	"github.com/mobingi/mobingi-cli/pkg/cli"
	"github.com/mobingi/mobingi-cli/pkg/cli/confmap"
	d "github.com/mobingilabs/mobingi-sdk-go/pkg/debug"
	"github.com/mobingilabs/mobingi-sdk-go/pkg/pretty"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	// main parent (root) command
	rootCmd = &cobra.Command{
		Use:   "mobingi-cli",
		Short: "Mobingi API command line interface.",
		Long:  `Command line interface for Mobingi API and services.`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			startTime = time.Now()
		},
		PersistentPostRun: func(cmd *cobra.Command, args []string) {
			if viper.GetBool(confmap.ConfigKey("verbose")) {
				delta := int64(time.Now().Sub(startTime) / time.Millisecond)
				d.Info(fmt.Sprintf("Elapsed time: %vms", delta))
			}
		},
	}

	startTime time.Time
)

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		d.ErrorExit(err, 1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	rootCmd.Flags().SortFlags = false
	rootCmd.PersistentFlags().SortFlags = false
	rootCmd.PersistentFlags().String("token", "", "access token")
	rootCmd.PersistentFlags().String("url", "", "base url for API")
	rootCmd.PersistentFlags().String("rurl", "", "base url for Docker Registry")
	rootCmd.PersistentFlags().String("apiver", "v3", "API version")
	rootCmd.PersistentFlags().StringP("fmt", "f", "", "output format (values depends on command)")
	rootCmd.PersistentFlags().StringP("out", "o", "", "full file path to write the output")
	rootCmd.PersistentFlags().IntVar(&pretty.Pad, "indent", 2, "indent padding when fmt is 'json'")
	rootCmd.PersistentFlags().Int64Var(&timeout.Timeout, "timeout", 120, "timeout in seconds")
	rootCmd.PersistentFlags().BoolVar(&cli.Verbose, "verbose", false, "verbose output")
	rootCmd.PersistentFlags().BoolVar(&cli.Debug, "debug", false, "debug mode when error occurs")
	rootCmd.SetHelpCommand(HelpCmd())

	viper.BindPFlag(confmap.ConfigKey("token"), rootCmd.PersistentFlags().Lookup("token"))
	viper.BindPFlag(confmap.ConfigKey("url"), rootCmd.PersistentFlags().Lookup("url"))
	viper.BindPFlag(confmap.ConfigKey("rurl"), rootCmd.PersistentFlags().Lookup("rurl"))
	viper.BindPFlag(confmap.ConfigKey("apiver"), rootCmd.PersistentFlags().Lookup("apiver"))
	viper.BindPFlag(confmap.ConfigKey("indent"), rootCmd.PersistentFlags().Lookup("indent"))
	viper.BindPFlag(confmap.ConfigKey("timeout"), rootCmd.PersistentFlags().Lookup("timeout"))
	viper.BindPFlag(confmap.ConfigKey("verbose"), rootCmd.PersistentFlags().Lookup("verbose"))
	viper.BindPFlag(confmap.ConfigKey("debug"), rootCmd.PersistentFlags().Lookup("debug"))

	rootCmd.AddCommand(
		LoginCmd(),
		StackCmd(),
		TemplateCmd(),
		ServerConfigCmd(),
		CredentialsCmd(),
		RegistryCmd(),
		VersionCmd(),
		ResetCmd(),
	)
}

func initConfig() {
	cnf := cli.CliConfig{}
	f := cnf.ConfigFile()
	viper.SetConfigType("yaml")
	viper.SetConfigFile(f)

	err := viper.ReadInConfig()
	if err != nil {
		d.Info("No config file found. Creating default.")
		err = cli.SetDefaultCliConfig()
		d.ErrorExit(err, 1)

		viper.SetConfigFile(f)
	}

	err = viper.ReadInConfig()
	d.ErrorExit(err, 1)
}
<commit_msg>Remove redundant error check.<commit_after>package cmd

import (
	"fmt"
	"time"

	"github.com/mobingi/mobingi-cli/client/timeout"
	"github.com/mobingi/mobingi-cli/pkg/cli"
	"github.com/mobingi/mobingi-cli/pkg/cli/confmap"
	d "github.com/mobingilabs/mobingi-sdk-go/pkg/debug"
	"github.com/mobingilabs/mobingi-sdk-go/pkg/pretty"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	// main parent (root) command
	rootCmd = &cobra.Command{
		Use:   "mobingi-cli",
		Short: "Mobingi API command line interface.",
		Long:  `Command line interface for Mobingi API and services.`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			startTime = time.Now()
		},
		PersistentPostRun: func(cmd *cobra.Command, args []string) {
			if viper.GetBool(confmap.ConfigKey("verbose")) {
				delta := int64(time.Now().Sub(startTime) / time.Millisecond)
				d.Info(fmt.Sprintf("Elapsed time: %vms", delta))
			}
		},
	}

	startTime time.Time
)

func Execute() {
	err := rootCmd.Execute()
	d.ErrorExit(err, 1)
}

func init() {
	cobra.OnInitialize(initConfig)
	rootCmd.Flags().SortFlags = false
	rootCmd.PersistentFlags().SortFlags = false
	rootCmd.PersistentFlags().String("token", "", "access token")
	rootCmd.PersistentFlags().String("url", "", "base url for API")
	rootCmd.PersistentFlags().String("rurl", "", "base url for Docker Registry")
	rootCmd.PersistentFlags().String("apiver", "v3", "API version")
	rootCmd.PersistentFlags().StringP("fmt", "f", "", "output format (values depends on command)")
	rootCmd.PersistentFlags().StringP("out", "o", "", "full file path to write the output")
	rootCmd.PersistentFlags().IntVar(&pretty.Pad, "indent", 2, "indent padding when fmt is 'json'")
	rootCmd.PersistentFlags().Int64Var(&timeout.Timeout, "timeout", 120, "timeout in seconds")
	rootCmd.PersistentFlags().BoolVar(&cli.Verbose, "verbose", false, "verbose output")
	rootCmd.PersistentFlags().BoolVar(&cli.Debug, "debug", false, "debug mode when error occurs")
	rootCmd.SetHelpCommand(HelpCmd())

	viper.BindPFlag(confmap.ConfigKey("token"), rootCmd.PersistentFlags().Lookup("token"))
	viper.BindPFlag(confmap.ConfigKey("url"), rootCmd.PersistentFlags().Lookup("url"))
	viper.BindPFlag(confmap.ConfigKey("rurl"), rootCmd.PersistentFlags().Lookup("rurl"))
	viper.BindPFlag(confmap.ConfigKey("apiver"), rootCmd.PersistentFlags().Lookup("apiver"))
	viper.BindPFlag(confmap.ConfigKey("indent"), rootCmd.PersistentFlags().Lookup("indent"))
	viper.BindPFlag(confmap.ConfigKey("timeout"), rootCmd.PersistentFlags().Lookup("timeout"))
	viper.BindPFlag(confmap.ConfigKey("verbose"), rootCmd.PersistentFlags().Lookup("verbose"))
	viper.BindPFlag(confmap.ConfigKey("debug"), rootCmd.PersistentFlags().Lookup("debug"))

	rootCmd.AddCommand(
		LoginCmd(),
		StackCmd(),
		TemplateCmd(),
		ServerConfigCmd(),
		CredentialsCmd(),
		RegistryCmd(),
		VersionCmd(),
		ResetCmd(),
	)
}

func initConfig() {
	cnf := cli.CliConfig{}
	f := cnf.ConfigFile()
	viper.SetConfigType("yaml")
	viper.SetConfigFile(f)

	err := viper.ReadInConfig()
	if err != nil {
		d.Info("No config file found. Creating default.")
		err = cli.SetDefaultCliConfig()
		d.ErrorExit(err, 1)

		viper.SetConfigFile(f)
	}

	err = viper.ReadInConfig()
	d.ErrorExit(err, 1)
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"strings"

	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	"github.com/grafana/grafana/pkg/setting"
)

func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
	settings, err := getFrontendSettingsMap(c)
	if err != nil {
		return nil, err
	}

	prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
	if err := bus.Dispatch(&prefsQuery); err != nil {
		return nil, err
	}
	prefs := prefsQuery.Result

	// Read locale from acccept-language
	acceptLang := c.Req.Header.Get("Accept-Language")
	locale := "en-US"

	if len(acceptLang) > 0 {
		parts := strings.Split(acceptLang, ",")
		locale = parts[0]
	}

	appUrl := setting.AppUrl
	appSubUrl := setting.AppSubUrl

	// special case when doing localhost call from phantomjs
	if c.IsRenderCall {
		appUrl = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
		appSubUrl = ""
		settings["appSubUrl"] = ""
	}

	var data = dtos.IndexViewData{
		User: &dtos.CurrentUser{
			Id:             c.UserId,
			IsSignedIn:     c.IsSignedIn,
			Login:          c.Login,
			Email:          c.Email,
			Name:           c.Name,
			OrgCount:       c.OrgCount,
			OrgId:          c.OrgId,
			OrgName:        c.OrgName,
			OrgRole:        c.OrgRole,
			GravatarUrl:    dtos.GetGravatarUrl(c.Email),
			IsGrafanaAdmin: c.IsGrafanaAdmin,
			LightTheme:     prefs.Theme == "light",
			Timezone:       prefs.Timezone,
			Locale:         locale,
			HelpFlags1:     c.HelpFlags1,
		},
		Settings:                settings,
		Theme:                   prefs.Theme,
		AppUrl:                  appUrl,
		AppSubUrl:               appSubUrl,
		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
		GoogleTagManagerId:      setting.GoogleTagManagerId,
		BuildVersion:            setting.BuildVersion,
		BuildCommit:             setting.BuildCommit,
		NewGrafanaVersion:       plugins.GrafanaLatestVersion,
		NewGrafanaVersionExists: plugins.GrafanaHasUpdate,
	}

	if setting.DisableGravatar {
		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/transparent.png"
	}

	if len(data.User.Name) == 0 {
		data.User.Name = data.User.Login
	}

	themeUrlParam := c.Query("theme")
	if themeUrlParam == "light" {
		data.User.LightTheme = true
		data.Theme = "light"
	}

	if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
		data.NavTree = append(data.NavTree, &dtos.NavLink{
			Text: "Create",
			Icon: "fa fa-fw fa-plus",
			Url:  "#",
			Children: []*dtos.NavLink{
				{Text: "Dashboard", Icon: "gicon gicon-dashboard-new", Url: setting.AppSubUrl + "/dashboard/new"},
				{Text: "Folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboard/new/?editview=new-folder"},
				{Text: "Import", Icon: "gicon gicon-dashboard-import", Url: setting.AppSubUrl + "/dashboard/new/?editview=import"},
			},
		})
	}

	dashboardChildNavs := []*dtos.NavLink{
		{Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home"},
		{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "fa fa-fw fa-film"},
		{Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "icon-gf icon-gf-fw icon-gf-snapshot"},
		{Text: "Dashboard List", Description: "Manage Dashboards And Folders", Id: "dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "fa fa-fw fa-bars"},
	}

	data.NavTree = append(data.NavTree, &dtos.NavLink{
		Text:     "Dashboards",
		Id:       "dashboards",
		Icon:     "gicon gicon-dashboard",
		Url:      setting.AppSubUrl + "/",
		Children: dashboardChildNavs,
	})

	if c.IsSignedIn {
		profileNode := &dtos.NavLink{
			Text:         c.SignedInUser.Login,
			Id:           "profile",
			Img:          data.User.GravatarUrl,
			Url:          setting.AppSubUrl + "/profile",
			HideFromMenu: true,
			Children: []*dtos.NavLink{
				{Text: "Your profile", Url: setting.AppSubUrl + "/profile", Icon: "fa fa-fw fa-sliders"},
				{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true},
			},
		}

		if !setting.DisableSignoutMenu {
			// add sign out first
			profileNode.Children = append([]*dtos.NavLink{
				{Text: "Sign out", Url: setting.AppSubUrl + "/logout", Icon: "fa fa-fw fa-sign-out", Target: "_self"},
			}, profileNode.Children...)
		}

		data.NavTree = append(data.NavTree, profileNode)
	}

	if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) {
		alertChildNavs := []*dtos.NavLink{
			{Text: "Alert List", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
			{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"},
		}

		data.NavTree = append(data.NavTree, &dtos.NavLink{
			Text:     "Alerting",
			Id:       "alerting",
			Icon:     "gicon gicon-alert",
			Url:      setting.AppSubUrl + "/alerting/list",
			Children: alertChildNavs,
		})
	}

	enabledPlugins, err := plugins.GetEnabledPlugins(c.OrgId)
	if err != nil {
		return nil, err
	}

	for _, plugin := range enabledPlugins.Apps {
		if plugin.Pinned {
			appLink := &dtos.NavLink{
				Text: plugin.Name,
				Id:   "plugin-page-" + plugin.Id,
				Url:  plugin.DefaultNavUrl,
				Img:  plugin.Info.Logos.Small,
			}

			for _, include := range plugin.Includes {
				if !c.HasUserRole(include.Role) {
					continue
				}

				if include.Type == "page" && include.AddToNav {
					link := &dtos.NavLink{
						Url:  setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug,
						Text: include.Name,
					}
					appLink.Children = append(appLink.Children, link)
				}

				if include.Type == "dashboard" && include.AddToNav {
					link := &dtos.NavLink{
						Url:  setting.AppSubUrl + "/dashboard/db/" + include.Slug,
						Text: include.Name,
					}
					appLink.Children = append(appLink.Children, link)
				}
			}

			if len(appLink.Children) > 0 && c.OrgRole == m.ROLE_ADMIN {
				appLink.Children = append(appLink.Children, &dtos.NavLink{Divider: true})
				appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "fa fa-fw fa-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/edit"})
			}

			if len(appLink.Children) > 0 {
				data.NavTree = append(data.NavTree, appLink)
			}
		}
	}

	if c.OrgRole == m.ROLE_ADMIN {
		cfgNode := &dtos.NavLink{
			Id:   "cfg",
			Text: "Configuration",
			Icon: "fa fa-fw fa-cogs",
			Url:  setting.AppSubUrl + "/configuration",
			Children: []*dtos.NavLink{
				{
					Text:        "Data Sources",
					Icon:        "gicon gicon-datasources",
					Description: "Add and configure data sources",
					Id:          "datasources",
					Url:         setting.AppSubUrl + "/datasources",
					Children: []*dtos.NavLink{
						{Text: "List", Url: setting.AppSubUrl + "/datasources", Icon: "gicon gicon-datasources"},
						{Text: "New", Url: setting.AppSubUrl + "/datasources", Icon: "fa fa-fw fa-plus"},
					},
				},

				{
					Text:        "Preferences",
					Id:          "org",
					Description: "Organization preferences",
					Icon:        "fa fa-fw fa-sliders",
					Url:         setting.AppSubUrl + "/org",
				},
				{
					Text:        "Plugins",
					Id:          "plugins",
					Description: "View and configure plugins",
					Icon:        "icon-gf icon-gf-fw icon-gf-apps",
					Url:         setting.AppSubUrl + "/plugins",
					Children: []*dtos.NavLink{
						{Text: "Panels", Url: setting.AppSubUrl + "/plugins?type=panel", Icon: "fa fa-fw fa-stop"},
						{Text: "Data sources", Url: setting.AppSubUrl + "/plugins?type=datasource", Icon: "icon-gf icon-gf-datasources"},
						{Text: "Apps", Url: setting.AppSubUrl + "/plugins?type=app", Icon: "icon-gf icon-gf-apps"},
					},
				},
				{
					Text:        "Members",
					Id:          "users",
					Description: "Manage org members",
					Icon:        "icon-gf icon-gf-fw icon-gf-users",
					Url:         setting.AppSubUrl + "/org/users",
				},
				{
					Text:        "Groups",
					Id:          "users",
					Description: "Manage org groups",
					Icon:        "fa fa-fw fa-users",
					Url:         setting.AppSubUrl + "/org/user-groups",
				},
				{
					Text:        "API Keys",
					Id:          "apikeys",
					Description: "Create & manage API keys",
					Icon:        "fa fa-fw fa-key",
					Url:         setting.AppSubUrl + "/org/apikeys",
				},
			},
		}

		if c.IsGrafanaAdmin {
			cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
				Text: "Server Admin",
				Id:   "admin",
				Icon: "fa fa-fw fa-shield",
				Url:  setting.AppSubUrl + "/admin",
				Children: []*dtos.NavLink{
					{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"},
					{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"},
					{Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"},
					{Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"},
					{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"},
				},
			})
		}

		data.NavTree = append(data.NavTree, cfgNode)
	}

	data.NavTree = append(data.NavTree, &dtos.NavLink{
		Text:         "Help",
		Id:           "help",
		Url:          "#",
		Icon:         "fa fa-fw fa-question",
		HideFromMenu: true,
		Children: []*dtos.NavLink{
			{Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"},
			{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"},
			{Text: "Documentation", Url: "http://docs.grafana.org", Icon: "fa fa-fw fa-file", Target: "_blank"},
		},
	})

	return &data, nil
}

func Index(c *middleware.Context) {
	if data, err := setIndexViewData(c); err != nil {
		c.Handle(500, "Failed to get settings", err)
		return
	} else {
		c.HTML(200, "index", data)
	}
}

func NotFoundHandler(c *middleware.Context) {
	if c.IsApiRequest() {
		c.JsonApiErr(404, "Not found", nil)
		return
	}

	if data, err := setIndexViewData(c); err != nil {
		c.Handle(500, "Failed to get settings", err)
		return
	} else {
		c.HTML(404, "index", data)
	}
}
<commit_msg>dashboards: fix link to bulk edit<commit_after>package api

import (
	"fmt"
	"strings"

	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	"github.com/grafana/grafana/pkg/setting"
)

func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
	settings, err := getFrontendSettingsMap(c)
	if err != nil {
		return nil, err
	}

	prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
	if err := bus.Dispatch(&prefsQuery); err != nil {
		return nil, err
	}
	prefs := prefsQuery.Result

	// Read locale from acccept-language
	acceptLang := c.Req.Header.Get("Accept-Language")
	locale := "en-US"

	if len(acceptLang) > 0 {
		parts := strings.Split(acceptLang, ",")
		locale = parts[0]
	}

	appUrl := setting.AppUrl
	appSubUrl := setting.AppSubUrl

	// special case when doing localhost call from phantomjs
	if c.IsRenderCall {
		appUrl = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
		appSubUrl = ""
		settings["appSubUrl"] = ""
	}

	var data = dtos.IndexViewData{
		User: &dtos.CurrentUser{
			Id:             c.UserId,
			IsSignedIn:     c.IsSignedIn,
			Login:          c.Login,
			Email:          c.Email,
			Name:           c.Name,
			OrgCount:       c.OrgCount,
			OrgId:          c.OrgId,
			OrgName:        c.OrgName,
			OrgRole:        c.OrgRole,
			GravatarUrl:    dtos.GetGravatarUrl(c.Email),
			IsGrafanaAdmin: c.IsGrafanaAdmin,
			LightTheme:     prefs.Theme == "light",
			Timezone:       prefs.Timezone,
			Locale:         locale,
			HelpFlags1:     c.HelpFlags1,
		},
		Settings:                settings,
		Theme:                   prefs.Theme,
		AppUrl:                  appUrl,
		AppSubUrl:               appSubUrl,
		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
		GoogleTagManagerId:      setting.GoogleTagManagerId,
		BuildVersion:            setting.BuildVersion,
		BuildCommit:             setting.BuildCommit,
		NewGrafanaVersion:       plugins.GrafanaLatestVersion,
		NewGrafanaVersionExists: plugins.GrafanaHasUpdate,
	}

	if setting.DisableGravatar {
		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/transparent.png"
	}

	if len(data.User.Name) == 0 {
		data.User.Name = data.User.Login
	}

	themeUrlParam := c.Query("theme")
	if themeUrlParam == "light" {
		data.User.LightTheme = true
		data.Theme = "light"
	}

	if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
		data.NavTree = append(data.NavTree, &dtos.NavLink{
			Text: "Create",
			Icon: "fa fa-fw fa-plus",
			Url:  "#",
			Children: []*dtos.NavLink{
				{Text: "Dashboard", Icon: "gicon gicon-dashboard-new", Url: setting.AppSubUrl + "/dashboard/new"},
				{Text: "Folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboard/new/?editview=new-folder"},
				{Text: "Import", Icon: "gicon gicon-dashboard-import", Url: setting.AppSubUrl + "/dashboard/new/?editview=import"},
			},
		})
	}

	dashboardChildNavs := []*dtos.NavLink{
		{Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home"},
		{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "fa fa-fw fa-film"},
		{Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "icon-gf icon-gf-fw icon-gf-snapshot"},
		{Text: "Dashboard List", Description: "Manage Dashboards And Folders", Id: "dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "fa fa-fw fa-bars"},
	}

	data.NavTree = append(data.NavTree, &dtos.NavLink{
		Text:     "Dashboards",
		Id:       "dashboards",
		Icon:     "gicon gicon-dashboard",
		Url:      setting.AppSubUrl + "/",
		Children: dashboardChildNavs,
	})

	if c.IsSignedIn {
		profileNode := &dtos.NavLink{
			Text:         c.SignedInUser.Login,
			Id:           "profile",
			Img:          data.User.GravatarUrl,
			Url:          setting.AppSubUrl + "/profile",
			HideFromMenu: true,
			Children: []*dtos.NavLink{
				{Text: "Your profile", Url: setting.AppSubUrl + "/profile", Icon: "fa fa-fw fa-sliders"},
				{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true},
			},
		}

		if !setting.DisableSignoutMenu {
			// add sign out first
			profileNode.Children = append([]*dtos.NavLink{
				{Text: "Sign out", Url: setting.AppSubUrl + "/logout", Icon: "fa fa-fw fa-sign-out", Target: "_self"},
			}, profileNode.Children...)
		}

		data.NavTree = append(data.NavTree, profileNode)
	}

	if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) {
		alertChildNavs := []*dtos.NavLink{
			{Text: "Alert List", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
			{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"},
		}

		data.NavTree = append(data.NavTree, &dtos.NavLink{
			Text:     "Alerting",
			Id:       "alerting",
			Icon:     "gicon gicon-alert",
			Url:      setting.AppSubUrl + "/alerting/list",
			Children: alertChildNavs,
		})
	}

	enabledPlugins, err := plugins.GetEnabledPlugins(c.OrgId)
	if err != nil {
		return nil, err
	}

	for _, plugin := range enabledPlugins.Apps {
		if plugin.Pinned {
			appLink := &dtos.NavLink{
				Text: plugin.Name,
				Id:   "plugin-page-" + plugin.Id,
				Url:  plugin.DefaultNavUrl,
				Img:  plugin.Info.Logos.Small,
			}

			for _, include := range plugin.Includes {
				if !c.HasUserRole(include.Role) {
					continue
				}

				if include.Type == "page" && include.AddToNav {
					link := &dtos.NavLink{
						Url:  setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug,
						Text: include.Name,
					}
					appLink.Children = append(appLink.Children, link)
				}

				if include.Type == "dashboard" && include.AddToNav {
					link := &dtos.NavLink{
						Url:  setting.AppSubUrl + "/dashboard/db/" + include.Slug,
						Text: include.Name,
					}
					appLink.Children = append(appLink.Children, link)
				}
			}

			if len(appLink.Children) > 0 && c.OrgRole == m.ROLE_ADMIN {
				appLink.Children = append(appLink.Children, &dtos.NavLink{Divider: true})
				appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "fa fa-fw fa-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/edit"})
			}

			if len(appLink.Children) > 0 {
				data.NavTree = append(data.NavTree, appLink)
			}
		}
	}

	if c.OrgRole == m.ROLE_ADMIN {
		cfgNode := &dtos.NavLink{
			Id:   "cfg",
			Text: "Configuration",
			Icon: "fa fa-fw fa-cogs",
			Url:  setting.AppSubUrl + "/configuration",
			Children: []*dtos.NavLink{
				{
					Text:        "Data Sources",
					Icon:        "gicon gicon-datasources",
					Description: "Add and configure data sources",
					Id:          "datasources",
					Url:         setting.AppSubUrl + "/datasources",
					Children: []*dtos.NavLink{
						{Text: "List", Url: setting.AppSubUrl + "/datasources", Icon: "gicon gicon-datasources"},
						{Text: "New", Url: setting.AppSubUrl + "/datasources", Icon: "fa fa-fw fa-plus"},
					},
				},
				{
					Text:        "Dashboard List",
					Description: "Manage Dashboards And Folders",
					Id:          "dashboards",
					Url:         setting.AppSubUrl + "/dashboards",
				},
				{
					Text:        "Preferences",
					Id:          "org",
					Description: "Organization preferences",
					Icon:        "fa fa-fw fa-sliders",
					Url:         setting.AppSubUrl + "/org",
				},
				{
					Text:        "Plugins",
					Id:          "plugins",
					Description: "View and configure plugins",
					Icon:        "icon-gf icon-gf-fw icon-gf-apps",
					Url:         setting.AppSubUrl + "/plugins",
					Children: []*dtos.NavLink{
						{Text: "Panels", Url: setting.AppSubUrl + "/plugins?type=panel", Icon: "fa fa-fw fa-stop"},
						{Text: "Data sources", Url: setting.AppSubUrl + "/plugins?type=datasource", Icon: "icon-gf icon-gf-datasources"},
						{Text: "Apps", Url: setting.AppSubUrl + "/plugins?type=app", Icon: "icon-gf icon-gf-apps"},
					},
				},
				{
					Text:        "Members",
					Id:          "users",
					Description: "Manage org members",
					Icon:        "icon-gf icon-gf-fw icon-gf-users",
					Url:         setting.AppSubUrl + "/org/users",
				},
				{
					Text:        "Groups",
					Id:          "users",
					Description: "Manage org groups",
					Icon:        "fa fa-fw fa-users",
					Url:         setting.AppSubUrl + "/org/user-groups",
				},
				{
					Text:        "API Keys",
					Id:          "apikeys",
					Description: "Create & manage API keys",
					Icon:        "fa fa-fw fa-key",
					Url:         setting.AppSubUrl + "/org/apikeys",
				},
			},
		}

		if c.IsGrafanaAdmin {
			cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
				Text: "Server Admin",
				Id:   "admin",
				Icon: "fa fa-fw fa-shield",
				Url:  setting.AppSubUrl + "/admin",
				Children: []*dtos.NavLink{
					{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"},
					{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"},
					{Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"},
					{Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"},
					{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"},
				},
			})
		}

		data.NavTree = append(data.NavTree, cfgNode)
	}

	data.NavTree = append(data.NavTree, &dtos.NavLink{
		Text:         "Help",
		Id:           "help",
		Url:          "#",
		Icon:         "fa fa-fw fa-question",
		HideFromMenu: true,
		Children: []*dtos.NavLink{
			{Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"},
			{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"},
			{Text: "Documentation", Url: "http://docs.grafana.org", Icon: "fa fa-fw fa-file", Target: "_blank"},
		},
	})

	return &data, nil
}

func Index(c *middleware.Context) {
	if data, err := setIndexViewData(c); err != nil {
		c.Handle(500, "Failed to get settings", err)
		return
	} else {
		c.HTML(200, "index", data)
	}
}

func NotFoundHandler(c *middleware.Context) {
	if c.IsApiRequest() {
		c.JsonApiErr(404, "Not found", nil)
		return
	}

	if data, err := setIndexViewData(c); err != nil {
		c.Handle(500, "Failed to get settings", err)
		return
	} else {
		c.HTML(404, "index", data)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package repo

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/google/go-github/v33/github"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/oauth2"

	krgh "k8s.io/release/pkg/github"
	"k8s.io/test-infra/prow/git"

	"k8s.io/enhancements/api"
	"k8s.io/enhancements/pkg/kepval"
	"k8s.io/enhancements/pkg/yaml"
)

const (
	ProposalPathStub         = "keps"
	ProposalTemplatePathStub = "NNNN-kep-template"
	ProposalFilename         = "README.md"
	ProposalMetadataFilename = "kep.yaml"

	PRRApprovalPathStub = "prod-readiness"

	remoteOrg  = "kubernetes"
	remoteRepo = "enhancements"

	proposalLabel = "kind/kep"
)

type Repo struct {
	// Paths
	BasePath        string
	ProposalPath    string
	PRRApprovalPath string
	ProposalReadme  string

	// Auth
	TokenPath string
	Token     string

	// Document handlers
	KEPHandler *api.KEPHandler
	PRRHandler *api.PRRHandler

	// Templates
	ProposalTemplate []byte
}

// New returns a new repo client configured to use the the normal os.Stdxxx and Filesystem
func New(repoPath string) (*Repo, error) {
	fetcher := api.DefaultGroupFetcher()
	return NewRepo(repoPath, fetcher)
}

func NewRepo(repoPath string, fetcher api.GroupFetcher) (*Repo, error) {
	var err error
	if repoPath == "" {
		repoPath, err = os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("unable to determine enhancements repo path: %s", err)
		}
	}

	proposalPath := filepath.Join(repoPath, ProposalPathStub)
	fi, err := os.Stat(proposalPath)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for proposal path %s",
			proposalPath,
		)
	}

	if !fi.IsDir() {
		return nil, errors.Wrap(
			err,
			"checking if proposal path is a directory",
		)
	}

	prrApprovalPath := filepath.Join(proposalPath, PRRApprovalPathStub)
	fi, err = os.Stat(prrApprovalPath)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for PRR approval path %s",
			prrApprovalPath,
		)
	}

	if !fi.IsDir() {
		return nil, errors.Wrap(
			err,
			"checking if PRR approval path is a directory",
		)
	}

	proposalReadme := filepath.Join(proposalPath, "README.md")
	fi, err = os.Stat(proposalReadme)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for proposal README path %s",
			proposalPath,
		)
	}

	if !fi.Mode().IsRegular() {
		return nil, errors.Wrap(
			err,
			"checking if proposal README is a file",
		)
	}

	groups, err := fetcher.FetchGroups()
	if err != nil {
		return nil, fmt.Errorf("fetching groups: %w", err)
	}

	prrApprovers, err := fetcher.FetchPRRApprovers()
	if err != nil {
		return nil, fmt.Errorf("fetching PRR approvers: %w", err)
	}

	kepHandler := &api.KEPHandler{Groups: groups, PRRApprovers: prrApprovers}

	prrHandler := &api.PRRHandler{PRRApprovers: prrApprovers}

	repo := &Repo{
		BasePath:        repoPath,
		ProposalPath:    proposalPath,
		PRRApprovalPath: prrApprovalPath,
		ProposalReadme:  proposalReadme,
		KEPHandler:      kepHandler,
		PRRHandler:      prrHandler,
	}

	proposalTemplate, err := repo.getProposalTemplate()
	if err != nil {
		return nil, errors.Wrap(err, "getting proposal template")
	}

	repo.ProposalTemplate = proposalTemplate

	// build a default client with normal os.Stdxx and Filesystem access. Tests can build their own
	// with appropriate test objects
	return repo, nil
}

func (r *Repo) SetGitHubToken(tokenFile string) error {
	if tokenFile != "" {
		token, err := ioutil.ReadFile(tokenFile)
		if err != nil {
			return err
		}

		r.Token = strings.Trim(string(token), "\n\r")
	}

	return nil
}

// getProposalTemplate reads the KEP template from the local clone of
// kubernetes/enhancements.
func (r *Repo) getProposalTemplate() ([]byte, error) {
	path := filepath.Join(
		r.ProposalPath,
		ProposalTemplatePathStub,
		ProposalFilename,
	)

	return ioutil.ReadFile(path)
}

func (r *Repo) findLocalKEPMeta(sig string) ([]string, error) {
	sigPath := filepath.Join(
		r.ProposalPath,
		sig,
	)

	keps := []string{}

	// if the SIG doesn't have a dir, it has no KEPs
	if _, err := os.Stat(sigPath); os.IsNotExist(err) {
		return keps, nil
	}

	err := filepath.Walk(
		sigPath,
		func(path string, info os.FileInfo, err error) error {
			logrus.Debugf("processing filename %s", info.Name())

			if err != nil {
				return err
			}

			// true if the file is a symlink
			if info.Mode()&os.ModeSymlink != 0 {
				// Assume symlink from old KEP location to new. The new location
				// will be processed separately, so no need to process it here.
				logrus.Debugf("%s is a symlink", info.Name())
				return nil
			}

			if !info.Mode().IsRegular() {
				return nil
			}

			if info.Name() == ProposalMetadataFilename {
				logrus.Debugf("adding %s as KEP metadata", info.Name())
				keps = append(keps, path)
				return filepath.SkipDir
			}

			if info.Name() == ProposalFilename {
				return nil
			}

			logrus.Debugf("adding %s as KEP metadata", info.Name())
			keps = append(keps, path)
			return nil
		},
	)

	return keps, err
}

func (r *Repo) LoadLocalKEPs(sig string) ([]*api.Proposal, error) {
	// KEPs in the local filesystem
	files, err := r.findLocalKEPMeta(sig)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"searching for local KEPs from %s",
			sig,
		)
	}

	logrus.Debugf("loading the following local KEPs: %v", files)

	var allKEPs []*api.Proposal
	for _, k := range files {
		if filepath.Ext(k) == ".yaml" {
			kep, err := r.loadKEPFromYaml(k)
			if err != nil {
				return nil, errors.Wrapf(
					err,
					"reading KEP %s from yaml",
					k,
				)
			}

			allKEPs = append(allKEPs, kep)
		}
	}

	logrus.Debugf("returning %d local KEPs", len(allKEPs))

	return allKEPs, nil
}

func (r *Repo) loadKEPPullRequests(sig string) ([]*api.Proposal, error) {
	var auth *http.Client
	ctx := context.Background()
	if r.Token != "" {
		ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: r.Token})
		auth = oauth2.NewClient(ctx, ts)
	}

	gh := github.NewClient(auth)
	allPulls := []*github.PullRequest{}
	opt := &github.PullRequestListOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}

	for {
		pulls, resp, err := gh.PullRequests.List(
			ctx,
			remoteOrg,
			remoteRepo,
			opt,
		)
		if err != nil {
			return nil, err
		}

		allPulls = append(allPulls, pulls...)
		if resp.NextPage == 0 {
			break
		}

		opt.Page = resp.NextPage
	}

	kepPRs := make([]*github.PullRequest, 10)
	for _, pr := range allPulls {
		foundKind, foundSIG := false, false
		sigLabel := strings.Replace(sig, "-", "/", 1)

		for _, l := range pr.Labels {
			if *l.Name == proposalLabel {
				foundKind = true
			}

			if *l.Name == sigLabel {
				foundSIG = true
			}
		}

		if !foundKind || !foundSIG {
			continue
		}

		kepPRs = append(kepPRs, pr)
	}

	if len(kepPRs) == 0 {
		return nil, nil
	}

	// Pull a temporary clone of the repo
	g, err := git.NewClient()
	if err != nil {
		return nil, err
	}

	g.SetCredentials("", func() []byte { return []byte{} })
	g.SetRemote(krgh.GitHubURL)

	repo, err := g.Clone(remoteOrg, remoteRepo)
	if err != nil {
		return nil, err
	}

	// read out each PR, and create a Proposal for each KEP that is
	// touched by a PR. This may result in multiple versions of the same KEP.
	var allKEPs []*api.Proposal
	for _, pr := range kepPRs {
		files, _, err := gh.PullRequests.ListFiles(
			context.Background(),
			remoteOrg,
			remoteRepo,
			pr.GetNumber(),
			&github.ListOptions{},
		)
		if err != nil {
			return nil, err
		}

		kepNames := make(map[string]bool, 10)
		for _, file := range files {
			if !strings.HasPrefix(*file.Filename, "keps/"+sig+"/") {
				continue
			}

			kk := strings.Split(*file.Filename, "/")
			if len(kk) < 3 {
				continue
			}

			if strings.HasSuffix(kk[2], ".md") {
				kepNames[kk[2][0:len(kk[2])-3]] = true
			} else {
				kepNames[kk[2]] = true
			}
		}

		if len(kepNames) == 0 {
			continue
		}

		err = repo.CheckoutPullRequest(pr.GetNumber())
		if err != nil {
			return nil, err
		}

		// read all these KEPs
		for k := range kepNames {
			kep, err := r.ReadKEP(sig, k)
			if err != nil {
				logrus.Warnf("error reading KEP %v: %v", k, err)
			} else {
				kep.PRNumber = strconv.Itoa(pr.GetNumber())
				allKEPs = append(allKEPs, kep)
			}
		}
	}

	return allKEPs, nil
}

func (r *Repo) ReadKEP(sig, name string) (*api.Proposal, error) {
	kepPath := filepath.Join(
		r.ProposalPath,
		sig,
		name,
		ProposalMetadataFilename,
	)

	_, err := os.Stat(kepPath)
	if err != nil {
		return nil, errors.Wrapf(err, "getting file info for %s", kepPath)
	}

	return r.loadKEPFromYaml(kepPath)
}

func (r *Repo) loadKEPFromYaml(kepPath string) (*api.Proposal, error) {
	b, err := ioutil.ReadFile(kepPath)
	if err != nil {
		return nil, fmt.Errorf("unable to read KEP metadata: %s", err)
	}

	var p api.Proposal
	err = yaml.UnmarshalStrict(b, &p)
	if err != nil {
		return nil, fmt.Errorf("unable to load KEP metadata: %s", err)
	}

	p.Name = filepath.Base(filepath.Dir(kepPath))

	// Read the PRR approval file and add any listed PRR approvers in there
	// to the PRR approvers list in the KEP. this is a hack while we transition
	// away from PRR approvers listed in kep.yaml
	handler := r.PRRHandler
	err = kepval.ValidatePRR(&p, handler, r.PRRApprovalPath)
	if err != nil {
		logrus.Errorf(
			"%v",
			errors.Wrapf(err, "validating PRR for %s", p.Name),
		)
	} else {
		prrPath := filepath.Dir(kepPath)
		prrPath = filepath.Dir(prrPath)
		sig := filepath.Base(prrPath)
		prrPath = filepath.Join(
			filepath.Dir(prrPath),
			PRRApprovalPathStub,
			sig,
			p.Number+".yaml",
		)

		prrFile, err := os.Open(prrPath)
		if os.IsNotExist(err) {
			return &p, nil
		}

		if err != nil {
			return nil, errors.Wrapf(err, "opening PRR approval %s", prrPath)
		}

		approval, err := handler.Parse(prrFile)
		if err != nil {
			return nil, errors.Wrapf(err, "parsing PRR")
		}

		approver, err := approval.ApproverForStage(p.Stage)
		if err != nil {
			logrus.Errorf(
				"%v",
				errors.Wrapf(err, "getting PRR approver for %s stage", p.Stage),
			)
		}

		for _, a := range p.PRRApprovers {
			if a == approver {
				approver = ""
			}
		}

		if approver != "" {
			p.PRRApprovers = append(p.PRRApprovers, approver)
		}
	}

	return &p, nil
}
<commit_msg>pkg/repo: only parse kep.yaml files as KEPs<commit_after>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package repo

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/google/go-github/v33/github"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/oauth2"

	krgh "k8s.io/release/pkg/github"
	"k8s.io/test-infra/prow/git"

	"k8s.io/enhancements/api"
	"k8s.io/enhancements/pkg/kepval"
	"k8s.io/enhancements/pkg/yaml"
)

const (
	ProposalPathStub         = "keps"
	ProposalTemplatePathStub = "NNNN-kep-template"
	ProposalFilename         = "README.md"
	ProposalMetadataFilename = "kep.yaml"

	PRRApprovalPathStub = "prod-readiness"

	remoteOrg  = "kubernetes"
	remoteRepo = "enhancements"

	proposalLabel = "kind/kep"
)

type Repo struct {
	// Paths
	BasePath        string
	ProposalPath    string
	PRRApprovalPath string
	ProposalReadme  string

	// Auth
	TokenPath string
	Token     string

	// Document handlers
	KEPHandler *api.KEPHandler
	PRRHandler *api.PRRHandler

	// Templates
	ProposalTemplate []byte
}

// New returns a new repo client configured to use the the normal os.Stdxxx and Filesystem
func New(repoPath string) (*Repo, error) {
	fetcher := api.DefaultGroupFetcher()
	return NewRepo(repoPath, fetcher)
}

func NewRepo(repoPath string, fetcher api.GroupFetcher) (*Repo, error) {
	var err error
	if repoPath == "" {
		repoPath, err = os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("unable to determine enhancements repo path: %s", err)
		}
	}

	proposalPath := filepath.Join(repoPath, ProposalPathStub)
	fi, err := os.Stat(proposalPath)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for proposal path %s",
			proposalPath,
		)
	}

	if !fi.IsDir() {
		return nil, errors.Wrap(
			err,
			"checking if proposal path is a directory",
		)
	}

	prrApprovalPath := filepath.Join(proposalPath, PRRApprovalPathStub)
	fi, err = os.Stat(prrApprovalPath)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for PRR approval path %s",
			prrApprovalPath,
		)
	}

	if !fi.IsDir() {
		return nil, errors.Wrap(
			err,
			"checking if PRR approval path is a directory",
		)
	}

	proposalReadme := filepath.Join(proposalPath, "README.md")
	fi, err = os.Stat(proposalReadme)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"getting file info for proposal README path %s",
			proposalPath,
		)
	}

	if !fi.Mode().IsRegular() {
		return nil, errors.Wrap(
			err,
			"checking if proposal README is a file",
		)
	}

	groups, err := fetcher.FetchGroups()
	if err != nil {
		return nil, fmt.Errorf("fetching groups: %w", err)
	}

	prrApprovers, err := fetcher.FetchPRRApprovers()
	if err != nil {
		return nil, fmt.Errorf("fetching PRR approvers: %w", err)
	}

	kepHandler := &api.KEPHandler{Groups: groups, PRRApprovers: prrApprovers}

	prrHandler := &api.PRRHandler{PRRApprovers: prrApprovers}

	repo := &Repo{
		BasePath:        repoPath,
		ProposalPath:    proposalPath,
		PRRApprovalPath: prrApprovalPath,
		ProposalReadme:  proposalReadme,
		KEPHandler:      kepHandler,
		PRRHandler:      prrHandler,
	}

	proposalTemplate, err := repo.getProposalTemplate()
	if err != nil {
		return nil, errors.Wrap(err, "getting proposal template")
	}

	repo.ProposalTemplate = proposalTemplate

	// build a default client with normal os.Stdxx and Filesystem access. Tests can build their own
	// with appropriate test objects
	return repo, nil
}

func (r *Repo) SetGitHubToken(tokenFile string) error {
	if tokenFile != "" {
		token, err := ioutil.ReadFile(tokenFile)
		if err != nil {
			return err
		}

		r.Token = strings.Trim(string(token), "\n\r")
	}

	return nil
}

// getProposalTemplate reads the KEP template from the local clone of
// kubernetes/enhancements.
func (r *Repo) getProposalTemplate() ([]byte, error) {
	path := filepath.Join(
		r.ProposalPath,
		ProposalTemplatePathStub,
		ProposalFilename,
	)

	return ioutil.ReadFile(path)
}

func (r *Repo) findLocalKEPMeta(sig string) ([]string, error) {
	sigPath := filepath.Join(
		r.ProposalPath,
		sig,
	)

	keps := []string{}

	// if the SIG doesn't have a dir, it has no KEPs
	if _, err := os.Stat(sigPath); os.IsNotExist(err) {
		return keps, nil
	}

	err := filepath.Walk(
		sigPath,
		func(path string, info os.FileInfo, err error) error {
			logrus.Debugf("processing filename %s", info.Name())

			if err != nil {
				return err
			}

			// true if the file is a symlink
			if info.Mode()&os.ModeSymlink != 0 {
				// Assume symlink from old KEP location to new. The new location
				// will be processed separately, so no need to process it here.
				logrus.Debugf("%s is a symlink", info.Name())
				return nil
			}

			if !info.Mode().IsRegular() {
				return nil
			}

			if info.Name() == ProposalMetadataFilename {
				logrus.Debugf("adding %s as KEP metadata", info.Name())
				keps = append(keps, path)
				return filepath.SkipDir
			}

			if info.Name() == ProposalFilename {
				return nil
			}

			return nil
		},
	)

	return keps, err
}

func (r *Repo) LoadLocalKEPs(sig string) ([]*api.Proposal, error) {
	// KEPs in the local filesystem
	files, err := r.findLocalKEPMeta(sig)
	if err != nil {
		return nil, errors.Wrapf(
			err,
			"searching for local KEPs from %s",
			sig,
		)
	}

	logrus.Debugf("loading the following local KEPs: %v", files)

	var allKEPs []*api.Proposal
	for _, k := range files {
		if filepath.Ext(k) == ".yaml" {
			kep, err := r.loadKEPFromYaml(k)
			if err != nil {
				return nil, errors.Wrapf(
					err,
					"reading KEP %s from yaml",
					k,
				)
			}

			allKEPs = append(allKEPs, kep)
		}
	}

	logrus.Debugf("returning %d local KEPs", len(allKEPs))

	return allKEPs, nil
}

func (r *Repo) loadKEPPullRequests(sig string) ([]*api.Proposal, error) {
	var auth *http.Client
	ctx := context.Background()
	if r.Token != "" {
		ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: r.Token})
		auth = oauth2.NewClient(ctx, ts)
	}

	gh := github.NewClient(auth)
	allPulls := []*github.PullRequest{}
	opt := &github.PullRequestListOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}

	for {
		pulls, resp, err := gh.PullRequests.List(
			ctx,
			remoteOrg,
			remoteRepo,
			opt,
		)
		if err != nil {
			return nil, err
		}

		allPulls = append(allPulls, pulls...)
		if resp.NextPage == 0 {
			break
		}

		opt.Page = resp.NextPage
	}

	kepPRs := make([]*github.PullRequest, 10)
	for _, pr := range allPulls {
		foundKind, foundSIG := false, false
		sigLabel := strings.Replace(sig, "-", "/", 1)

		for _, l := range pr.Labels {
			if *l.Name == proposalLabel {
				foundKind = true
			}

			if *l.Name == sigLabel {
				foundSIG = true
			}
		}

		if !foundKind || !foundSIG {
			continue
		}

		kepPRs = append(kepPRs, pr)
	}

	if len(kepPRs) == 0 {
		return nil, nil
	}

	// Pull a temporary clone of the repo
	g, err := git.NewClient()
	if err != nil {
		return nil, err
	}

	g.SetCredentials("", func() []byte { return []byte{} })
	g.SetRemote(krgh.GitHubURL)

	repo, err := g.Clone(remoteOrg, remoteRepo)
	if err != nil {
		return nil, err
	}

	// read out each PR, and create a Proposal for each KEP that is
	// touched by a PR. This may result in multiple versions of the same KEP.
	var allKEPs []*api.Proposal
	for _, pr := range kepPRs {
		files, _, err := gh.PullRequests.ListFiles(
			context.Background(),
			remoteOrg,
			remoteRepo,
			pr.GetNumber(),
			&github.ListOptions{},
		)
		if err != nil {
			return nil, err
		}

		kepNames := make(map[string]bool, 10)
		for _, file := range files {
			if !strings.HasPrefix(*file.Filename, "keps/"+sig+"/") {
				continue
			}

			kk := strings.Split(*file.Filename, "/")
			if len(kk) < 3 {
				continue
			}

			if strings.HasSuffix(kk[2], ".md") {
				kepNames[kk[2][0:len(kk[2])-3]] = true
			} else {
				kepNames[kk[2]] = true
			}
		}

		if len(kepNames) == 0 {
			continue
		}

		err = repo.CheckoutPullRequest(pr.GetNumber())
		if err != nil {
			return nil, err
		}

		// read all these KEPs
		for k := range kepNames {
			kep, err := r.ReadKEP(sig, k)
			if err != nil {
				logrus.Warnf("error reading KEP %v: %v", k, err)
			} else {
				kep.PRNumber = strconv.Itoa(pr.GetNumber())
				allKEPs = append(allKEPs, kep)
			}
		}
	}

	return allKEPs, nil
}

func (r *Repo) ReadKEP(sig, name string) (*api.Proposal, error) {
	kepPath := filepath.Join(
		r.ProposalPath,
		sig,
		name,
		ProposalMetadataFilename,
	)

	_, err := os.Stat(kepPath)
	if err != nil {
		return nil, errors.Wrapf(err, "getting file info for %s", kepPath)
	}

	return r.loadKEPFromYaml(kepPath)
}

func (r *Repo) loadKEPFromYaml(kepPath string) (*api.Proposal, error) {
	b, err := ioutil.ReadFile(kepPath)
	if err != nil {
		return nil, fmt.Errorf("unable to read KEP metadata: %s", err)
	}

	var p api.Proposal
	err = yaml.UnmarshalStrict(b, &p)
	if err != nil {
		return nil, fmt.Errorf("unable to load KEP metadata: %s", err)
	}

	p.Name = filepath.Base(filepath.Dir(kepPath))

	// Read the PRR approval file and add any listed PRR approvers in there
	// to the PRR approvers list in the KEP. this is a hack while we transition
	// away from PRR approvers listed in kep.yaml
	handler := r.PRRHandler
	err = kepval.ValidatePRR(&p, handler, r.PRRApprovalPath)
	if err != nil {
		logrus.Errorf(
			"%v",
			errors.Wrapf(err, "validating PRR for %s", p.Name),
		)
	} else {
		prrPath := filepath.Dir(kepPath)
		prrPath = filepath.Dir(prrPath)
		sig := filepath.Base(prrPath)
		prrPath = filepath.Join(
			filepath.Dir(prrPath),
			PRRApprovalPathStub,
			sig,
			p.Number+".yaml",
		)

		prrFile, err := os.Open(prrPath)
		if os.IsNotExist(err) {
			return &p, nil
		}

		if err != nil {
			return nil, errors.Wrapf(err, "opening PRR approval %s", prrPath)
		}

		approval, err := handler.Parse(prrFile)
		if err != nil {
			return nil, errors.Wrapf(err, "parsing PRR")
		}

		approver, err := approval.ApproverForStage(p.Stage)
		if err != nil {
			logrus.Errorf(
				"%v",
				errors.Wrapf(err, "getting PRR approver for %s stage", p.Stage),
			)
		}

		for _, a := range p.PRRApprovers {
			if a == approver {
				approver = ""
			}
		}

		if approver != "" {
			p.PRRApprovers = append(p.PRRApprovers, approver)
		}
	}

	return &p, nil
}
<|endoftext|>


<commit_before>package via

import (
	"github.com/mrosset/util/file"
	"os"
	"reflect"
	"testing"
)

func TestRepoFilePaths(t *testing.T) {
	tests{
		{
			Expect: "testdata/plans/repo.json",
			Got:    testConfig.Repo.File(testConfig),
		},
		{
			Expect: "testdata/plans/files.json",
			Got:    testConfig.Repo.FilesFile(testConfig),
		},
	}.equals(t)
}

func TestRepoFilesOwns(t *testing.T) {

	var (
		tfile = "libc.so"
		repo  = RepoFiles{
			"glibc":     []string{tfile},
			"glibc-arm": []string{tfile},
		}
		inverse = RepoFiles{
			"glibc-arm": []string{tfile},
			"glibc":     []string{tfile},
		}
		expectOne  = "glibc"
		expectMore = []string{"glibc", "glibc-arm"}
	)

	for i := 0; i <= 100; i++ {
		got := repo.Owns(tfile)
		if expectOne != got {
			t.Errorf(EXPECT_GOT_FMT, "", expectOne, got)
		}
		got = inverse.Owns(tfile)
		if expectOne != got {
			t.Errorf(EXPECT_GOT_FMT, "", expectOne, got)
		}
	}

	if got := repo.Owners(tfile); !reflect.DeepEqual(got, expectMore) {
		t.Errorf(EXPECT_GOT_FMT, "", expectMore, got)
	}

}

func TestRepoCreate(t *testing.T) {
	tests{
		{
			Expect: nil,
			Got:    RepoCreate(testConfig),
		},
		{
			Label:  "files.json",
			Expect: true,
			Got:    file.Exists("testdata/plans/files.json"),
		},
		{
			Label:  "repo.json",
			Expect: true,
			Got:    file.Exists("testdata/plans/repo.json"),
		},
	}.equals(t)
}

func TestRepo_Exists(t *testing.T) {
	tests := []struct {
		name string
		r    Repo
		want bool
	}{
		{
			"",
			Repo{"testdata/repo"},
			true,
		},
		{
			"",
			Repo{"testdata/false"},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.r.Exists(); got != tt.want {
				t.Errorf("Repo.Exists() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestRepo_Expand(t *testing.T) {
	os.Setenv("VIA_TEST_DATA", "testdata")
	tests{
		{
			Label:  "repo expand",
			Expect: "testdata/repo",
			Got:    Repo{"$VIA_TEST_DATA/repo"}.Expand(),
		},
	}.equals(t)
}
<commit_msg>tests: Repo Exists test uses test literals<commit_after>package via

import (
	"github.com/mrosset/util/file"
	"os"
	"reflect"
	"testing"
)

func TestRepoFilePaths(t *testing.T) {
	tests{
		{
			Expect: "testdata/plans/repo.json",
			Got:    testConfig.Repo.File(testConfig),
		},
		{
			Expect: "testdata/plans/files.json",
			Got:    testConfig.Repo.FilesFile(testConfig),
		},
	}.equals(t)
}

func TestRepoFilesOwns(t *testing.T) {

	var (
		tfile = "libc.so"
		repo  = RepoFiles{
			"glibc":     []string{tfile},
			"glibc-arm": []string{tfile},
		}
		inverse = RepoFiles{
			"glibc-arm": []string{tfile},
			"glibc":     []string{tfile},
		}
		expectOne  = "glibc"
		expectMore = []string{"glibc", "glibc-arm"}
	)

	for i := 0; i <= 100; i++ {
		got := repo.Owns(tfile)
		if expectOne != got {
			t.Errorf(EXPECT_GOT_FMT, "", expectOne, got)
		}
		got = inverse.Owns(tfile)
		if expectOne != got {
			t.Errorf(EXPECT_GOT_FMT, "", expectOne, got)
		}
	}

	if got := repo.Owners(tfile); !reflect.DeepEqual(got, expectMore) {
		t.Errorf(EXPECT_GOT_FMT, "", expectMore, got)
	}

}

func TestRepoCreate(t *testing.T) {
	tests{
		{
			Expect: nil,
			Got:    RepoCreate(testConfig),
		},
		{
			Label:  "files.json",
			Expect: true,
			Got:    file.Exists("testdata/plans/files.json"),
		},
		{
			Label:  "repo.json",
			Expect: true,
			Got:    file.Exists("testdata/plans/repo.json"),
		},
	}.equals(t)
}

func TestRepo_Exists(t *testing.T) {
	tests{
		{
			Expect: true,
			Got:    Repo{"testdata/repo"}.Exists(),
		},
		{
			Expect: false,
			Got:    Repo{"testdata/false"}.Exists(),
		},
	}.equals(t)

}

func TestRepo_Expand(t *testing.T) {
	os.Setenv("VIA_TEST_DATA", "testdata")
	tests{
		{
			Label:  "repo expand",
			Expect: "testdata/repo",
			Got:    Repo{"$VIA_TEST_DATA/repo"}.Expand(),
		},
	}.equals(t)
}
<|endoftext|>


<commit_before>package slug

import (
	"os"

	"github.com/flant/dapp/pkg/slug/v1"
	"github.com/flant/dapp/pkg/slug/v2"
	"github.com/flant/dapp/pkg/slug/v3"
)

func Slug(data string) string {
	if os.Getenv("DAPP_SLUG_V3") != "" {
		return v3.Slug(data)
	} else if os.Getenv("DAPP_SLUG_V2") != "" {
		return v2.Slug(data)
	} else {
		return v1.Slug(data)
	}
}
<commit_msg>[go dapp] slug funcs stubs<commit_after>package slug

import (
	"os"

	"github.com/flant/dapp/pkg/slug/v1"
	"github.com/flant/dapp/pkg/slug/v2"
	"github.com/flant/dapp/pkg/slug/v3"
)

func Slug(data string) string {
	if os.Getenv("DAPP_SLUG_V3") != "" {
		return v3.Slug(data)
	} else if os.Getenv("DAPP_SLUG_V2") != "" {
		return v2.Slug(data)
	} else {
		return v1.Slug(data)
	}
}

func DockerTag(data string) string {
	return data
}

func KubernetesNamespace(namespace string) string {
	return namespace
}

func HelmRelease(name string) string {
	return name
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/olekukonko/tablewriter"
	"github.com/rubenv/sql-migrate"
)

type StatusCommand struct {
}

func (c *StatusCommand) Help() string {
	helpText := `
Usage: sql-migrate status [options] ...

  Show migration status.

Options:

  -config=dbconfig.yml   Configuration file to use.
  -env="development"     Environment.

`
	return strings.TrimSpace(helpText)
}

func (c *StatusCommand) Synopsis() string {
	return "Show migration status"
}

func (c *StatusCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("status", flag.ContinueOnError)
	cmdFlags.Usage = func() { ui.Output(c.Help()) }
	ConfigFlags(cmdFlags)

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	env, err := GetEnvironment()
	if err != nil {
		ui.Error(fmt.Sprintf("Could not parse config: %s", err))
		return 1
	}

	db, dialect, err := GetConnection(env)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	source := migrate.FileMigrationSource{
		Dir: env.Dir,
	}
	migrations, err := source.FindMigrations()
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	records, err := migrate.GetMigrationRecords(db, dialect)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Migration", "Applied"})
	table.SetColWidth(60)

	rows := make(map[string]*statusRow)

	for _, m := range migrations {
		rows[m.Id] = &statusRow{
			Id:       m.Id,
			Migrated: false,
		}
	}

	for _, r := range records {
		rows[r.Id].Migrated = true
		rows[r.Id].AppliedAt = r.AppliedAt
	}

	for _, m := range migrations {
		if rows[m.Id].Migrated {
			table.Append([]string{
				m.Id,
				rows[m.Id].AppliedAt.String(),
			})
		} else {
			table.Append([]string{
				m.Id,
				"no",
			})
		}
	}

	table.Render()

	return 0
}

type statusRow struct {
	Id        string
	Migrated  bool
	AppliedAt time.Time
}
<commit_msg>Fix panic when migration file removed<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/olekukonko/tablewriter"
	"github.com/rubenv/sql-migrate"
)

type StatusCommand struct {
}

func (c *StatusCommand) Help() string {
	helpText := `
Usage: sql-migrate status [options] ...

  Show migration status.

Options:

  -config=dbconfig.yml   Configuration file to use.
  -env="development"     Environment.

`
	return strings.TrimSpace(helpText)
}

func (c *StatusCommand) Synopsis() string {
	return "Show migration status"
}

func (c *StatusCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("status", flag.ContinueOnError)
	cmdFlags.Usage = func() { ui.Output(c.Help()) }
	ConfigFlags(cmdFlags)

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	env, err := GetEnvironment()
	if err != nil {
		ui.Error(fmt.Sprintf("Could not parse config: %s", err))
		return 1
	}

	db, dialect, err := GetConnection(env)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	source := migrate.FileMigrationSource{
		Dir: env.Dir,
	}
	migrations, err := source.FindMigrations()
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	records, err := migrate.GetMigrationRecords(db, dialect)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Migration", "Applied"})
	table.SetColWidth(60)

	rows := make(map[string]*statusRow)

	for _, m := range migrations {
		rows[m.Id] = &statusRow{
			Id:       m.Id,
			Migrated: false,
		}
	}

	for _, r := range records {
		if rows[r.Id] == nil {
			ui.Warn(fmt.Sprintf("Could not find migration file: %v", r.Id))
			continue
		}

		rows[r.Id].Migrated = true
		rows[r.Id].AppliedAt = r.AppliedAt
	}

	for _, m := range migrations {
		if rows[m.Id] != nil && rows[m.Id].Migrated {
			table.Append([]string{
				m.Id,
				rows[m.Id].AppliedAt.String(),
			})
		} else {
			table.Append([]string{
				m.Id,
				"no",
			})
		}
	}

	table.Render()

	return 0
}

type statusRow struct {
	Id        string
	Migrated  bool
	AppliedAt time.Time
}
<|endoftext|>