<commit_before>// The purpose of this tool is to provide a realistic load on the
// system. It creates "n" albums (default 2), each contains 20 images.
// In total, it sends n x 94 requests.
package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"strings"
	"time"

	"github.com/cheggaaa/pb/v3"

	"github.com/zitryss/aye-and-nay/pkg/debug"
)

var (
	n            int
	apiAddress   string
	minioAddress string
	connections  int
	testdata     string
	verbose      bool
	b            []byte
	sep          string
)

func main() {
	flag.IntVar(&n, "n", 2, "#albums")
	flag.StringVar(&apiAddress, "api-address", "https://localhost", "")
	flag.StringVar(&minioAddress, "minio-address", "https://localhost", "")
	flag.IntVar(&connections, "connections", 2, "")
	flag.StringVar(&testdata, "testdata", "./testdata", "")
	flag.BoolVar(&verbose, "verbose", true, "")
	flag.Parse()

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	bar := pb.StartNew(n * 94)
	if !verbose {
		bar.SetWriter(ioutil.Discard)
	}

	readFiles()

	sem := make(chan struct{}, connections)
	for i := 0; i < n; i++ {
		sem <- struct{}{}
		go func() {
			defer func() { <-sem }()
			albumHtml()
			album := albumApi()
			bar.Increment()
			readyApi(album)
			bar.Increment()
			for j := 0; j < 4; j++ {
				pairHtml()
				for k := 0; k < 11; k++ {
					src1, token1, src2, token2 := pairApi(album)
					bar.Increment()
					pairMinio(src1, src2)
					voteApi(album, token1, token2)
					bar.Increment()
				}
				topHtml()
				src := topApi(album)
				bar.Increment()
				topMinio(src)
			}
		}()
	}
	for i := 0; i < connections; i++ {
		sem <- struct{}{}
	}

	bar.Finish()
	fmt.Println(time.Since(bar.StartTime()), "sec")
	fmt.Println(float64(n*94)/time.Since(bar.StartTime()).Seconds(), "rps")
}

func readFiles() {
	body := bytes.Buffer{}
	multi := multipart.NewWriter(&body)
	for i := 0; i < 4; i++ {
		for _, filename := range []string{"alan.jpg", "john.bmp", "dennis.png", "tim.gif", "linus.jpg"} {
			part, err := multi.CreateFormFile("images", filename)
			debug.Check(err)
			b, err := ioutil.ReadFile(testdata + "/" + filename)
			debug.Check(err)
			_, err = part.Write(b)
			debug.Check(err)
		}
	}
	err := multi.Close()
	debug.Check(err)

	b = body.Bytes()
	sep = multi.FormDataContentType()
}

func albumHtml() {
	html("/index.html")
}

func albumApi() string {
	body := bytes.NewReader(b)
	req, err := http.NewRequest("POST", apiAddress+"/api/albums/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", sep)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 201)

	type result struct {
		Album struct {
			Id string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Album.Id
}

func readyApi(album string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/ready/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Album struct {
			Progress float64
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func pairHtml() {
	html("/pair.html")
}

func pairApi(album string) (string, string, string, string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/pair/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Img1 struct {
			Token string
			Src   string
		}
		Img2 struct {
			Token string
			Src   string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Img1.Src, res.Img1.Token, res.Img2.Src, res.Img2.Token
}

func pairMinio(src1 string, src2 string) {
	minio(src1)
	minio(src2)
}

func voteApi(album string, token1 string, token2 string) {
	body := strings.NewReader("{\"album\":{\"imgFrom\":{\"token\":\"" + token1 + "\"},\"imgTo\":{\"token\":\"" + token2 + "\"}}}")
	req, err := http.NewRequest("PATCH", apiAddress+"/api/albums/"+album+"/vote/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func topHtml() {
	html("/top.html")
}

func topApi(album string) []string {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/top/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type image struct {
		Src    string
		Rating float64
	}
	type result struct {
		Images []image
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	src := []string(nil)
	for _, image := range res.Images {
		src = append(src, image.Src)
	}
	return src
}

func topMinio(src []string) {
	for _, s := range src {
		minio(s)
	}
}

func html(page string) {
	if apiAddress != minioAddress {
		return
	}
	req, err := http.NewRequest("GET", apiAddress+page, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}

func minio(src string) {
	if minioAddress == "" {
		return
	}
	req, err := http.NewRequest("GET", minioAddress+src, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}
<commit_msg>Fix CI 2<commit_after>// The purpose of this tool is to provide a realistic load on the
// system. It creates "n" albums (default 2), each contains 20 images.
// In total, it sends n x 94 requests.
package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"strings"
	"time"

	"github.com/cheggaaa/pb/v3"

	"github.com/zitryss/aye-and-nay/pkg/debug"
)

var (
	n            int
	apiAddress   string
	minioAddress string
	connections  int
	testdata     string
	verbose      bool
	b            []byte
	sep          string
)

func main() {
	flag.IntVar(&n, "n", 2, "#albums")
	flag.StringVar(&apiAddress, "api-address", "https://localhost", "")
	flag.StringVar(&minioAddress, "minio-address", "https://localhost", "")
	flag.IntVar(&connections, "connections", 2, "")
	flag.StringVar(&testdata, "testdata", "./testdata", "")
	flag.BoolVar(&verbose, "verbose", true, "")
	flag.Parse()

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	bar := pb.StartNew(n * 94)
	if !verbose {
		bar.SetWriter(ioutil.Discard)
	}

	readFiles()

	sem := make(chan struct{}, connections)
	for i := 0; i < n; i++ {
		sem <- struct{}{}
		go func() {
			defer func() { <-sem }()
			albumHtml()
			album := albumApi()
			bar.Increment()
			readyApi(album)
			bar.Increment()
			for j := 0; j < 4; j++ {
				pairHtml()
				for k := 0; k < 11; k++ {
					src1, token1, src2, token2 := pairApi(album)
					bar.Increment()
					pairMinio(src1, src2)
					voteApi(album, token1, token2)
					bar.Increment()
				}
				topHtml()
				src := topApi(album)
				bar.Increment()
				topMinio(src)
			}
		}()
	}
	for i := 0; i < connections; i++ {
		sem <- struct{}{}
	}

	bar.Finish()
	fmt.Println(time.Since(bar.StartTime()), "sec")
	fmt.Println(float64(n*94)/time.Since(bar.StartTime()).Seconds(), "rps")
}

func readFiles() {
	body := bytes.Buffer{}
	multi := multipart.NewWriter(&body)
	for i := 0; i < 4; i++ {
		for _, filename := range []string{"alan.jpg", "john.bmp", "dennis.png", "tim.gif", "linus.jpg"} {
			part, err := multi.CreateFormFile("images", filename)
			debug.Check(err)
			b, err := ioutil.ReadFile(testdata + "/" + filename)
			debug.Check(err)
			_, err = part.Write(b)
			debug.Check(err)
		}
	}
	err := multi.Close()
	debug.Check(err)

	b = body.Bytes()
	sep = multi.FormDataContentType()
}

func albumHtml() {
	html("/index.html")
}

func albumApi() string {
	body := bytes.NewReader(b)
	req, err := http.NewRequest("POST", apiAddress+"/api/albums/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", sep)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 201)

	type result struct {
		Album struct {
			Id string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Album.Id
}

func readyApi(album string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/ready/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Album struct {
			Progress float64
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func pairHtml() {
	html("/pair.html")
}

func pairApi(album string) (string, string, string, string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/pair/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Album struct {
			Img1 struct {
				Token string
				Src   string
			}
			Img2 struct {
				Token string
				Src   string
			}
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Album.Img1.Src, res.Album.Img1.Token, res.Album.Img2.Src, res.Album.Img2.Token
}

func pairMinio(src1 string, src2 string) {
	minio(src1)
	minio(src2)
}

func voteApi(album string, token1 string, token2 string) {
	body := strings.NewReader("{\"album\":{\"imgFrom\":{\"token\":\"" + token1 + "\"},\"imgTo\":{\"token\":\"" + token2 + "\"}}}")
	req, err := http.NewRequest("PATCH", apiAddress+"/api/albums/"+album+"/vote/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func topHtml() {
	html("/top.html")
}

func topApi(album string) []string {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/top/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type image struct {
		Src    string
		Rating float64
	}
	type result struct {
		Album struct {
			Images []image
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	src := []string(nil)
	for _, image := range res.Album.Images {
		src = append(src, image.Src)
	}
	return src
}

func topMinio(src []string) {
	for _, s := range src {
		minio(s)
	}
}

func html(page string) {
	if apiAddress != minioAddress {
		return
	}
	req, err := http.NewRequest("GET", apiAddress+page, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}

func minio(src string) {
	if minioAddress == "" {
		return
	}
	req, err := http.NewRequest("GET", minioAddress+src, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	astparser "go/parser"
	"go/token"
	"os"
	"path/filepath"

	"go/ast"

	"github.com/davecgh/go-spew/spew"
	"github.com/devimteam/microgen/generator"
	"github.com/devimteam/microgen/generator/template"
	"github.com/devimteam/microgen/parser"
)

var (
	flagFileName    = flag.String("file", "", "File name")
	flagIfaceName   = flag.String("interface", "", "Interface name")
	flagOutputDir   = flag.String("out", "", "Output directory")
	flagPackagePath = flag.String("package", "", "Service package path for out")
	flagGRPC        = flag.Bool("grpc", false, "Render gRPC transport")
	debug           = flag.Bool("debug", false, "Debug mode")
)

func init() {
	flag.Parse()
}

func main() {
	currentDir, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	path := filepath.Join(currentDir, *flagFileName)
	fset := token.NewFileSet()
	f, err := astparser.ParseFile(fset, path, nil, astparser.ParseComments)
	if err != nil {
		panic(fmt.Errorf("error when parse file: %v", err))
	}
	i, err := parser.ParseInterface(f, *flagIfaceName)
	if err != nil {
		panic(fmt.Errorf("error when parse interface from file : %v", err))
	}

	if *debug {
		ast.Print(fset, f)
		spew.Dump(i)
	}

	var strategy generator.Strategy
	if *flagOutputDir == "" {
		strategy = generator.NewWriterStrategy(os.Stdout)
	} else {
		strategy = generator.NewFileStrategy(*flagOutputDir)
	}

	templates := []generator.Template{
		&template.ExchangeTemplate{},
		&template.EndpointsTemplate{},
		&template.ClientTemplate{},
		&template.MiddlewareTemplate{PackagePath: *flagPackagePath},
		&template.LoggingTemplate{PackagePath: *flagPackagePath},
	}
	if *flagGRPC {
		templates = append(templates,
			&template.GRPCServerTemplate{},
			&template.GRPCClientTemplate{PackagePath: *flagPackagePath},
			&template.GRPCConverterTemplate{PackagePath: *flagPackagePath},
		)
	}

	gen := generator.NewGenerator(templates, i, strategy)
	err = gen.Generate()

	if err != nil {
		fmt.Println(err.Error())
	}
}
<commit_msg>feat(main): add basic flag validation<commit_after>package main

import (
	"flag"
	"fmt"
	astparser "go/parser"
	"go/token"
	"os"
	"path/filepath"

	"go/ast"

	"github.com/davecgh/go-spew/spew"
	"github.com/devimteam/microgen/generator"
	"github.com/devimteam/microgen/generator/template"
	"github.com/devimteam/microgen/parser"
)

var (
	flagFileName    = flag.String("file", "", "Name of file where described interface definition")
	flagIfaceName   = flag.String("interface", "", "Interface name")
	flagOutputDir   = flag.String("out", "", "Output directory")
	flagPackagePath = flag.String("package", "", "Service package path")
	flagGRPC        = flag.Bool("grpc", false, "Render gRPC transport")
	flagDebug       = flag.Bool("debug", false, "Debug mode")
	flagHelp        = flag.Bool("help", false, "Show help")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagHelp || *flagFileName == "" || *flagIfaceName == "" || *flagPackagePath == "" {
		flag.Usage()
		os.Exit(0)
	}

	currentDir, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	path := filepath.Join(currentDir, *flagFileName)
	fset := token.NewFileSet()
	f, err := astparser.ParseFile(fset, path, nil, astparser.ParseComments)
	if err != nil {
		panic(fmt.Errorf("error when parse file: %v", err))
	}
	i, err := parser.ParseInterface(f, *flagIfaceName)
	if err != nil {
		panic(fmt.Errorf("error when parse interface from file : %v", err))
	}

	if *flagDebug {
		ast.Print(fset, f)
		spew.Dump(i)
	}

	var strategy generator.Strategy
	if *flagOutputDir == "" {
		strategy = generator.NewWriterStrategy(os.Stdout)
	} else {
		strategy = generator.NewFileStrategy(*flagOutputDir)
	}

	templates := []generator.Template{
		&template.ExchangeTemplate{},
		&template.EndpointsTemplate{},
		&template.ClientTemplate{},
		&template.MiddlewareTemplate{PackagePath: *flagPackagePath},
		&template.LoggingTemplate{PackagePath: *flagPackagePath},
	}
	if *flagGRPC {
		templates = append(templates,
			&template.GRPCServerTemplate{},
			&template.GRPCClientTemplate{PackagePath: *flagPackagePath},
			&template.GRPCConverterTemplate{PackagePath: *flagPackagePath},
		)
	}

	gen := generator.NewGenerator(templates, i, strategy)
	err = gen.Generate()

	if err != nil {
		fmt.Println(err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"runtime"
	"strings"
	"text/template"

	"github.com/client9/misspell"
)

var (
	defaultWrite *template.Template
	defaultRead  *template.Template
	stdout       *log.Logger // see below in init()
)

const (
	defaultWriteTmpl = `{{ .Filename }}:{{ .Line }}:{{ .Column }}:corrected "{{ js .Original }}" to "{{ js .Corrected }}"`
	defaultReadTmpl  = `{{ .Filename }}:{{ .Line }}:{{ .Column }}:found "{{ js .Original }}" a misspelling of "{{ js .Corrected }}"`
)

func init() {
	defaultWrite = template.Must(template.New("defaultWrite").Parse(defaultWriteTmpl))
	defaultRead = template.Must(template.New("defaultRead").Parse(defaultReadTmpl))

	// we cant't just write to os.Stdout directly since we have multiple goroutine
	// all writing at the same time causing broken output.  Log is routine safe.
	// we see it so it doesn't use a prefix or include a time stamp.
	stdout = log.New(os.Stdout, "", 0)
}

func worker(writeit bool, r *misspell.Replacer, mode string, files <-chan string, results chan<- int) {
	count := 0
	for filename := range files {
		// ignore directories
		if f, err := os.Stat(filename); err != nil || f.IsDir() {
			continue
		}

		raw, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Printf("Unable to read %q: %s", filename, err)
			continue
		}

		// try to avoid reading in binary files.
		//  with "-w" flag this will cause corruption
		// allow any text/ type with utf-8 encoding
		// DetectContentType sometimes returns charset=utf-16 for XML stuff
		//  in which case ignore.
		// TODO: we'd really like to prevent reading in multi-gig files
		//  just to ignore them
		mime := http.DetectContentType(raw)
		if !strings.HasPrefix(mime, "text/") || !strings.HasSuffix(mime, "charset=utf-8") {
			continue
		}

		orig := string(raw)
		updated, changes := r.Replace(orig)

		if len(changes) == 0 {
			continue
		}
		count += len(changes)
		for _, diff := range changes {
			// add in filename
			diff.Filename = filename

			// output can be done by doing multiple goroutines
			// and can clobber os.Stdout.
			//
			// the log package can be used simultaneously from multiple goroutines
			var output bytes.Buffer
			if writeit {
				defaultWrite.Execute(&output, diff)
			} else {
				defaultRead.Execute(&output, diff)
			}

			// goroutine-safe print to os.Stdout
			stdout.Println(output.String())
		}

		if writeit {
			ioutil.WriteFile(filename, []byte(updated), 0)
		}
	}
	results <- count
}

func main() {
	quiet := flag.Bool("q", false, "quiet")
	workers := flag.Int("j", 0, "Number of workers, 0 = number of CPUs")
	writeit := flag.Bool("w", false, "Overwrite file with corrections (default is just to display)")
	format := flag.String("f", "", "use Golang template for log message")
	ignores := flag.String("i", "", "ignore the following corrections, comma separated")
	locale := flag.String("locale", "", "Correct spellings using locale perferances for US or UK.  Default is to use a neutral variety of English.  Setting locale to US will correct the British spelling of 'colour' to 'color'")
	mode := flag.String("source", "auto", "Source mode: auto=guess, go=golang source, text=plain or markdown-like text")
	debug := flag.Bool("debug", false, "Debug matching, very slow")
	exitError := flag.Bool("error", false, "Exit with 2 if misspelling found")

	flag.Parse()

	r := misspell.Replacer{
		Replacements: misspell.DictMain,
		Debug:        *debug,
	}

	//
	// Figure out regional variations
	//
	switch strings.ToUpper(*locale) {
	case "":
		// nothing
	case "US":
		r.AddRuleList(misspell.DictAmerican)
	case "UK", "GB":
		r.AddRuleList(misspell.DictBritish)
	case "NZ", "AU", "CA":
		log.Fatalf("Help wanted.  https://github.com/client9/misspell/issues/6")
	default:
		log.Fatalf("Unknow locale: %q", *locale)
	}

	//
	// Stuff to ignore
	//
	if len(*ignores) > 0 {
		r.RemoveRule(strings.Split(*ignores, ","))
	}

	//
	// Source input mode
	//
	switch *mode {
	case "auto":
	case "go":
	case "text":
	default:
		log.Fatalf("Mode must be one of auto=guess, go=golang source, text=plain or markdown-like text")
	}

	//
	// Custom output
	//
	if len(*format) > 0 {
		t, err := template.New("custom").Parse(*format)
		if err != nil {
			log.Fatalf("Unable to compile log format: %s", err)
		}
		defaultWrite = t
		defaultRead = t
	}

	//
	// Number of Workers / CPU to use
	//
	if *workers < 0 {
		log.Fatalf("-j must >= 0")
	}
	if *workers == 0 {
		*workers = runtime.NumCPU()
	}
	if *debug {
		*workers = 1
	}

	//
	// Done with Flags.
	//  Compile the Replacer and process files
	//
	r.Compile()

	args := flag.Args()

	// unix style pipes: different output module
	// stdout: output of corrected text
	// stderr: output of log lines
	if len(args) == 0 {
		raw, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatalf("Unable to read stdin")
		}
		orig := string(raw)
		updated, changes := r.Replace(orig)
		if !*quiet {
			for _, diff := range changes {
				diff.Filename = "stdin"
				if *writeit {
					defaultWrite.Execute(os.Stderr, diff)
				} else {
					defaultRead.Execute(os.Stderr, diff)
				}
				os.Stderr.Write([]byte("\n"))
			}
		}
		if *writeit {
			os.Stdout.Write([]byte(updated))
		}
		return
	}

	c := make(chan string, len(args))
	results := make(chan int, *workers)

	for i := 0; i < *workers; i++ {
		go worker(*writeit, &r, *mode, c, results)
	}

	for _, filename := range args {
		if !misspell.IsSCMPath(filename) && !misspell.IsBinaryFile(filename) {
			c <- filename
		}
	}
	close(c)

	count := 0
	for i := 0; i < *workers; i++ {
		changed := <-results
		count += changed
	}
	if count != 0 && *exitError {
		// log.Printf("Got %d", count)
		// error
		os.Exit(2)
	}
}
<commit_msg>Issue #53 - ignore binary files<commit_after>package main

import (
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"
	"text/template"

	"github.com/client9/misspell"
)

var (
	defaultWrite *template.Template
	defaultRead  *template.Template
	stdout       *log.Logger // see below in init()
)

const (
	defaultWriteTmpl = `{{ .Filename }}:{{ .Line }}:{{ .Column }}:corrected "{{ js .Original }}" to "{{ js .Corrected }}"`
	defaultReadTmpl  = `{{ .Filename }}:{{ .Line }}:{{ .Column }}:found "{{ js .Original }}" a misspelling of "{{ js .Corrected }}"`
)

func init() {
	defaultWrite = template.Must(template.New("defaultWrite").Parse(defaultWriteTmpl))
	defaultRead = template.Must(template.New("defaultRead").Parse(defaultReadTmpl))

	// we cant't just write to os.Stdout directly since we have multiple goroutine
	// all writing at the same time causing broken output.  Log is routine safe.
	// we see it so it doesn't use a prefix or include a time stamp.
	stdout = log.New(os.Stdout, "", 0)
}

func worker(writeit bool, r *misspell.Replacer, mode string, files <-chan string, results chan<- int) {
	count := 0
	for filename := range files {
		orig, err := misspell.ReadTextFile(filename)
		if err != nil {
			log.Println(err)
			continue
		}
		if len(orig) == 0 {
			continue
		}

		updated, changes := r.Replace(orig)

		if len(changes) == 0 {
			continue
		}
		count += len(changes)
		for _, diff := range changes {
			// add in filename
			diff.Filename = filename

			// output can be done by doing multiple goroutines
			// and can clobber os.Stdout.
			//
			// the log package can be used simultaneously from multiple goroutines
			var output bytes.Buffer
			if writeit {
				defaultWrite.Execute(&output, diff)
			} else {
				defaultRead.Execute(&output, diff)
			}

			// goroutine-safe print to os.Stdout
			stdout.Println(output.String())
		}

		if writeit {
			ioutil.WriteFile(filename, []byte(updated), 0)
		}
	}
	results <- count
}

func main() {
	quiet := flag.Bool("q", false, "quiet")
	workers := flag.Int("j", 0, "Number of workers, 0 = number of CPUs")
	writeit := flag.Bool("w", false, "Overwrite file with corrections (default is just to display)")
	format := flag.String("f", "", "use Golang template for log message")
	ignores := flag.String("i", "", "ignore the following corrections, comma separated")
	locale := flag.String("locale", "", "Correct spellings using locale perferances for US or UK.  Default is to use a neutral variety of English.  Setting locale to US will correct the British spelling of 'colour' to 'color'")
	mode := flag.String("source", "auto", "Source mode: auto=guess, go=golang source, text=plain or markdown-like text")
	debug := flag.Bool("debug", false, "Debug matching, very slow")
	exitError := flag.Bool("error", false, "Exit with 2 if misspelling found")

	flag.Parse()

	r := misspell.Replacer{
		Replacements: misspell.DictMain,
		Debug:        *debug,
	}

	//
	// Figure out regional variations
	//
	switch strings.ToUpper(*locale) {
	case "":
		// nothing
	case "US":
		r.AddRuleList(misspell.DictAmerican)
	case "UK", "GB":
		r.AddRuleList(misspell.DictBritish)
	case "NZ", "AU", "CA":
		log.Fatalf("Help wanted.  https://github.com/client9/misspell/issues/6")
	default:
		log.Fatalf("Unknow locale: %q", *locale)
	}

	//
	// Stuff to ignore
	//
	if len(*ignores) > 0 {
		r.RemoveRule(strings.Split(*ignores, ","))
	}

	//
	// Source input mode
	//
	switch *mode {
	case "auto":
	case "go":
	case "text":
	default:
		log.Fatalf("Mode must be one of auto=guess, go=golang source, text=plain or markdown-like text")
	}

	//
	// Custom output
	//
	if len(*format) > 0 {
		t, err := template.New("custom").Parse(*format)
		if err != nil {
			log.Fatalf("Unable to compile log format: %s", err)
		}
		defaultWrite = t
		defaultRead = t
	}

	//
	// Number of Workers / CPU to use
	//
	if *workers < 0 {
		log.Fatalf("-j must >= 0")
	}
	if *workers == 0 {
		*workers = runtime.NumCPU()
	}
	if *debug {
		*workers = 1
	}

	//
	// Done with Flags.
	//  Compile the Replacer and process files
	//
	r.Compile()

	args := flag.Args()

	// unix style pipes: different output module
	// stdout: output of corrected text
	// stderr: output of log lines
	if len(args) == 0 {
		raw, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatalf("Unable to read stdin")
		}
		orig := string(raw)
		updated, changes := r.Replace(orig)
		if !*quiet {
			for _, diff := range changes {
				diff.Filename = "stdin"
				if *writeit {
					defaultWrite.Execute(os.Stderr, diff)
				} else {
					defaultRead.Execute(os.Stderr, diff)
				}
				os.Stderr.Write([]byte("\n"))
			}
		}
		if *writeit {
			os.Stdout.Write([]byte(updated))
		}
		return
	}

	c := make(chan string, len(args))
	results := make(chan int, *workers)

	for i := 0; i < *workers; i++ {
		go worker(*writeit, &r, *mode, c, results)
	}

	for _, filename := range args {
		c <- filename
	}
	close(c)

	count := 0
	for i := 0; i < *workers; i++ {
		changed := <-results
		count += changed
	}
	if count != 0 && *exitError {
		// log.Printf("Got %d", count)
		// error
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io/ioutil"
	"runtime"
	"strconv"
	"strings"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var mrCreateDiscussionCmd = &cobra.Command{
	Use:     "discussion [remote] [<MR ID or branch>]",
	Short:   "Start a discussion on an MR on GitLab",
	Aliases: []string{"block", "thread"},
	Example: heredoc.Doc(`
		lab mr discussion
		lab mr discussion origin
		lab mr discussion my_remote -m "discussion comment"
		lab mr discussion upstream -F test_file.txt
		lab mr discussion --commit abcdef123456
		lab mr discussion my-topic-branch
		lab mr discussion origin 123
		lab mr discussion origin my-topic-branch
		`),
	PersistentPreRun: labPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		rn, mrNum, err := parseArgsWithGitBranchMR(args)
		if err != nil {
			log.Fatal(err)
		}

		msgs, err := cmd.Flags().GetStringSlice("message")
		if err != nil {
			log.Fatal(err)
		}

		filename, err := cmd.Flags().GetString("file")
		if err != nil {
			log.Fatal(err)
		}

		commit, err := cmd.Flags().GetString("commit")
		if err != nil {
			log.Fatal(err)
		}

		state := noteGetState(rn, true, int(mrNum))

		body := ""
		if filename != "" {
			content, err := ioutil.ReadFile(filename)
			if err != nil {
				log.Fatal(err)
			}
			body = string(content)
		} else if commit == "" {
			body, err = mrDiscussionMsg(int(mrNum), state, commit, msgs, "\n")
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
		} else {
			body = getCommitBody(rn, commit)
			body, err = mrDiscussionMsg(int(mrNum), state, commit, nil, body)
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
			createCommitComments(rn, int(mrNum), commit, body, true)
			return
		}

		if body == "" {
			log.Fatal("aborting discussion due to empty discussion msg")
		}

		discussionURL, err := lab.MRCreateDiscussion(rn, int(mrNum), &gitlab.CreateMergeRequestDiscussionOptions{
			Body: &body,
		})
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(discussionURL)
	},
}

func mrDiscussionMsg(mrNum int, state string, commit string, msgs []string, body string) (string, error) {
	if len(msgs) > 0 {
		return strings.Join(msgs[0:], "\n\n"), nil
	}

	tmpl := mrDiscussionGetTemplate(commit)
	text, err := noteText(mrNum, state, commit, body, tmpl)
	if err != nil {
		return "", err
	}
	return git.EditFile("MR_DISCUSSION", text)
}

func mrDiscussionGetTemplate(commit string) string {
	if commit == "" {
		return heredoc.Doc(`
		{{.InitMsg}}
		{{.CommentChar}} This thread is being started on {{.State}} Merge Request {{.IDnum}}.
		{{.CommentChar}} Comment lines beginning with '{{.CommentChar}}' are discarded.`)
	}
	return heredoc.Doc(`
		{{.InitMsg}}
		{{.CommentChar}} This thread is being started on {{.State}} Merge Request {{.IDnum}} commit {{.Commit}}.
		{{.CommentChar}} Do not delete patch tracking lines that begin with '|'.
		{{.CommentChar}} Comment lines beginning with '{{.CommentChar}}' are discarded.`)
}

func init() {
	mrCreateDiscussionCmd.Flags().StringSliceP("message", "m", []string{}, "use the given <msg>; multiple -m are concatenated as separate paragraphs")
	mrCreateDiscussionCmd.Flags().StringP("file", "F", "", "use the given file as the message")
	mrCreateDiscussionCmd.Flags().StringP("commit", "c", "", "start a thread on a commit")

	mrCmd.AddCommand(mrCreateDiscussionCmd)
	carapace.Gen(mrCreateDiscussionCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<commit_msg>mr discussion: use meaningful example argument to -F<commit_after>package cmd

import (
	"fmt"
	"io/ioutil"
	"runtime"
	"strconv"
	"strings"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var mrCreateDiscussionCmd = &cobra.Command{
	Use:     "discussion [remote] [<MR ID or branch>]",
	Short:   "Start a discussion on an MR on GitLab",
	Aliases: []string{"block", "thread"},
	Example: heredoc.Doc(`
		lab mr discussion
		lab mr discussion origin
		lab mr discussion my_remote -m "discussion comment"
		lab mr discussion upstream -F my_comment.txt
		lab mr discussion --commit abcdef123456
		lab mr discussion my-topic-branch
		lab mr discussion origin 123
		lab mr discussion origin my-topic-branch
		`),
	PersistentPreRun: labPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		rn, mrNum, err := parseArgsWithGitBranchMR(args)
		if err != nil {
			log.Fatal(err)
		}

		msgs, err := cmd.Flags().GetStringSlice("message")
		if err != nil {
			log.Fatal(err)
		}

		filename, err := cmd.Flags().GetString("file")
		if err != nil {
			log.Fatal(err)
		}

		commit, err := cmd.Flags().GetString("commit")
		if err != nil {
			log.Fatal(err)
		}

		state := noteGetState(rn, true, int(mrNum))

		body := ""
		if filename != "" {
			content, err := ioutil.ReadFile(filename)
			if err != nil {
				log.Fatal(err)
			}
			body = string(content)
		} else if commit == "" {
			body, err = mrDiscussionMsg(int(mrNum), state, commit, msgs, "\n")
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
		} else {
			body = getCommitBody(rn, commit)
			body, err = mrDiscussionMsg(int(mrNum), state, commit, nil, body)
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
			createCommitComments(rn, int(mrNum), commit, body, true)
			return
		}

		if body == "" {
			log.Fatal("aborting discussion due to empty discussion msg")
		}

		discussionURL, err := lab.MRCreateDiscussion(rn, int(mrNum), &gitlab.CreateMergeRequestDiscussionOptions{
			Body: &body,
		})
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(discussionURL)
	},
}

func mrDiscussionMsg(mrNum int, state string, commit string, msgs []string, body string) (string, error) {
	if len(msgs) > 0 {
		return strings.Join(msgs[0:], "\n\n"), nil
	}

	tmpl := mrDiscussionGetTemplate(commit)
	text, err := noteText(mrNum, state, commit, body, tmpl)
	if err != nil {
		return "", err
	}
	return git.EditFile("MR_DISCUSSION", text)
}

func mrDiscussionGetTemplate(commit string) string {
	if commit == "" {
		return heredoc.Doc(`
		{{.InitMsg}}
		{{.CommentChar}} This thread is being started on {{.State}} Merge Request {{.IDnum}}.
		{{.CommentChar}} Comment lines beginning with '{{.CommentChar}}' are discarded.`)
	}
	return heredoc.Doc(`
		{{.InitMsg}}
		{{.CommentChar}} This thread is being started on {{.State}} Merge Request {{.IDnum}} commit {{.Commit}}.
		{{.CommentChar}} Do not delete patch tracking lines that begin with '|'.
		{{.CommentChar}} Comment lines beginning with '{{.CommentChar}}' are discarded.`)
}

func init() {
	mrCreateDiscussionCmd.Flags().StringSliceP("message", "m", []string{}, "use the given <msg>; multiple -m are concatenated as separate paragraphs")
	mrCreateDiscussionCmd.Flags().StringP("file", "F", "", "use the given file as the message")
	mrCreateDiscussionCmd.Flags().StringP("commit", "c", "", "start a thread on a commit")

	mrCmd.AddCommand(mrCreateDiscussionCmd)
	carapace.Gen(mrCreateDiscussionCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"sync"

	"net/http"

	"time"

	"github.com/jhernandezme/go-multasgt"
)

func main() {
	client := &http.Client{
		Timeout: time.Duration(5 * time.Second),
	}
	var pType = flag.String("type", "P", "Plate Type")
	var pNumber = flag.String("number", "123ABC", "Plate Number")
	flag.Parse()
	var wg sync.WaitGroup
	wg.Add(7)
	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		e := &multasgt.Emetra{}
		e.Client = client
		tickets, _ = e.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		em := &multasgt.Emixtra{}
		em.Client = client
		tickets, _ = em.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		scp := &multasgt.SCP{}
		scp.Client = client
		tickets, _ = scp.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		f := &multasgt.Fraijanes{}
		f.Client = client
		tickets, _ = f.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		v := &multasgt.VillaNueva{}
		v.Client = client
		tickets, _ = v.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()
	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		p := &multasgt.PNC{}
		p.Client = client
		tickets, _ = p.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		a := &multasgt.Antigua{}
		a.Client = client
		tickets, _ = a.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	wg.Wait()

}
<commit_msg>Increase timeout<commit_after>package main

import (
	"flag"
	"fmt"
	"sync"

	"net/http"

	"time"

	"github.com/jhernandezme/go-multasgt"
)

func main() {
	client := &http.Client{
		Timeout: time.Duration(15 * time.Second),
	}
	var pType = flag.String("type", "P", "Plate Type")
	var pNumber = flag.String("number", "123ABC", "Plate Number")
	flag.Parse()
	var wg sync.WaitGroup
	wg.Add(7)
	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		e := &multasgt.Emetra{}
		e.Client = client
		tickets, _ = e.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		em := &multasgt.Emixtra{}
		em.Client = client
		tickets, _ = em.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		scp := &multasgt.SCP{}
		scp.Client = client
		tickets, _ = scp.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		f := &multasgt.Fraijanes{}
		f.Client = client
		tickets, _ = f.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		v := &multasgt.VillaNueva{}
		v.Client = client
		tickets, _ = v.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()
	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		p := &multasgt.PNC{}
		p.Client = client
		tickets, _ = p.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	go func() {
		defer wg.Done()
		var tickets []multasgt.Ticket
		a := &multasgt.Antigua{}
		a.Client = client
		tickets, _ = a.Check(*pType, *pNumber)
		for _, r := range tickets {
			fmt.Printf("%#v \n", r)
		}
	}()

	wg.Wait()

}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"

	"github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/go-kit/kit/log"
)

var (
	cfg prometheuscontroller.Config
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.CommandLine
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", "quay.io/coreos/prometheus-config-reloader:v0.0.2", "Config and rule reload image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.Namespace, "namespace", v1.NamespaceAll, "Namespace to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.BoolVar(&cfg.EnableValidation, "with-validation", false, "Include the validation spec")
	flagset.Parse(os.Args[1:])

}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	r := prometheus.NewRegistry()
	r.MustRegister(prometheus.NewGoCollector())

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanager.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	po.RegisterMetrics(r)
	ao.RegisterMetrics(r)
	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

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

package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"

	"github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/go-kit/kit/log"
)

var (
	cfg prometheuscontroller.Config
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.CommandLine
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", "quay.io/coreos/prometheus-config-reloader:v0.0.2", "Config and rule reload image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.Namespace, "namespace", v1.NamespaceAll, "Namespace to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.BoolVar(&cfg.EnableValidation, "with-validation", true, "Include the validation spec in the CRD")
	flagset.Parse(os.Args[1:])

}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	r := prometheus.NewRegistry()
	r.MustRegister(prometheus.NewGoCollector())

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanager.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	po.RegisterMetrics(r)
	ao.RegisterMetrics(r)
	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

func main() {
	os.Exit(Main())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/utils"
)

func TaipeiNow() time.Time {
	d := time.Now()
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, utils.TaipeiTimeZone)
}

func prettyprint(data realtime.Data) string {
	return fmt.Sprintf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]",
		data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var twseNo = flag.String("twse", "", "TWSE NO.")
var otcNo = flag.String("otc", "", "OTC NO.")
var index = flag.Bool("index", false, "Show Index.")

func main() {
	flag.Parse()
	var r *realtime.StockRealTime
	if *twseNo != "" {
		r = realtime.NewTWSE(*twseNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *otcNo != "" {
		r = realtime.NewOTC(*otcNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *index {
		var wg sync.WaitGroup
		indexList := [3]*realtime.StockRealTime{realtime.NewWeight(TaipeiNow()),
			realtime.NewOTCI(TaipeiNow()), realtime.NewFRMSA(TaipeiNow())}
		result := make(chan string)
		wg.Add(3)
		for _, r := range indexList {
			go func(r *realtime.StockRealTime) {
				data, _ := r.Get()
				result <- prettyprint(data)
			}(r)
		}
		go func() {
			for v := range result {
				wg.Done()
				log.Println(v)
			}
		}()
		wg.Wait()
	}
	//flag.PrintDefaults()
}
<commit_msg>Add commend.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/utils"
)

// TaipeiNow show Taipei Now time.
func TaipeiNow() time.Time {
	d := time.Now()
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, utils.TaipeiTimeZone)
}

func prettyprint(data realtime.Data) string {
	return fmt.Sprintf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]",
		data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var twseNo = flag.String("twse", "", "TWSE NO.")
var otcNo = flag.String("otc", "", "OTC NO.")
var index = flag.Bool("index", false, "Show Index.")

func main() {
	flag.Parse()
	var r *realtime.StockRealTime
	if *twseNo != "" {
		r = realtime.NewTWSE(*twseNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *otcNo != "" {
		r = realtime.NewOTC(*otcNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *index {
		var wg sync.WaitGroup
		indexList := [3]*realtime.StockRealTime{realtime.NewWeight(TaipeiNow()),
			realtime.NewOTCI(TaipeiNow()), realtime.NewFRMSA(TaipeiNow())}
		result := make(chan string)
		wg.Add(3)
		for _, r := range indexList {
			go func(r *realtime.StockRealTime) {
				data, _ := r.Get()
				result <- prettyprint(data)
			}(r)
		}
		go func() {
			for v := range result {
				wg.Done()
				log.Println(v)
			}
		}()
		wg.Wait()
	}
	//flag.PrintDefaults()
}
<|endoftext|>


<commit_before>package podsecuritypolicy

import (
	"fmt"

	v13 "github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/extensions/v1beta1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	v12 "github.com/rancher/types/apis/rbac.authorization.k8s.io/v1"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	rbac "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const psptpbByTargetProjectNameAnnotationIndex = "podsecuritypolicy.rbac.user.cattle.io/psptpb-by-project-id"
const roleBindingByServiceAccountIndex = "podsecuritypolicy.rbac.user.cattle.io/role-binding-by-service-account"
const psptpbRoleBindingAnnotation = "podsecuritypolicy.rbac.user.cattle.io/psptpb-role-binding"

// RegisterServiceAccount ensures that:
// 	1. Each namespace has a pod security policy assigned to a role if:
//		a. its project has a PSPT assigned to it
//		OR
//		b. its cluster has a default PSPT assigned to it
//  2. PSPs are bound to their associated service accounts via a cluster role binding
func RegisterServiceAccount(context *config.UserContext) {
	logrus.Infof("registering podsecuritypolicy serviceaccount handler for cluster %v", context.ClusterName)

	psptpbInformer := context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").Controller().Informer()
	psptpbIndexers := map[string]cache.IndexFunc{
		psptpbByTargetProjectNameAnnotationIndex: psptpbByTargetProjectName,
	}
	psptpbInformer.AddIndexers(psptpbIndexers)

	roleBindingInformer := context.RBAC.RoleBindings("").Controller().Informer()
	roleBindingIndexers := map[string]cache.IndexFunc{
		roleBindingByServiceAccountIndex: roleBindingByServiceAccount,
	}
	roleBindingInformer.AddIndexers(roleBindingIndexers)

	m := &serviceAccountManager{
		clusterName:        context.ClusterName,
		clusters:           context.Management.Management.Clusters(""),
		pspts:              context.Management.Management.PodSecurityPolicyTemplates(""),
		roleBindings:       context.RBAC.RoleBindings(""),
		roleBindingIndexer: roleBindingInformer.GetIndexer(),

		policies:      context.Extensions.PodSecurityPolicies(""),
		psptpbIndexer: psptpbInformer.GetIndexer(),

		clusterLister:     context.Management.Management.Clusters("").Controller().Lister(),
		psptLister:        context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		templateLister:    context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		policyLister:      context.Extensions.PodSecurityPolicies("").Controller().Lister(),
		roleBindingLister: context.RBAC.RoleBindings("").Controller().Lister(),
		roleLister:        context.RBAC.ClusterRoles("").Controller().Lister(),
		namespaceLister:   context.Core.Namespaces("").Controller().Lister(),
		projectLister:     context.Management.Management.Projects("").Controller().Lister(),
		psptpbLister: context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").
			Controller().Lister(),
	}

	context.Core.ServiceAccounts("").AddHandler("ServiceAccountLifecycleHandler", m.sync)
}

func psptpbByTargetProjectName(obj interface{}) ([]string, error) {
	psptpb, ok := obj.(*v3.PodSecurityPolicyTemplateProjectBinding)
	if !ok || psptpb.TargetProjectName == "" {
		return []string{}, nil
	}

	return []string{psptpb.TargetProjectName}, nil
}

func roleBindingByServiceAccount(obj interface{}) ([]string, error) {
	roleBinding, ok := obj.(*rbac.RoleBinding)
	if !ok || len(roleBinding.Subjects) != 1 ||
		roleBinding.Subjects[0].Name == "" ||
		roleBinding.Subjects[0].Namespace == "" {
		return []string{}, nil
	}

	subject := roleBinding.Subjects[0]
	return []string{subject.Namespace + "-" + subject.Name}, nil
}

type serviceAccountManager struct {
	clusterName        string
	clusterLister      v3.ClusterLister
	clusters           v3.ClusterInterface
	pspts              v3.PodSecurityPolicyTemplateInterface
	psptLister         v3.PodSecurityPolicyTemplateLister
	psptpbIndexer      cache.Indexer
	templateLister     v3.PodSecurityPolicyTemplateLister
	policyLister       v1beta1.PodSecurityPolicyLister
	roleBindingLister  v12.RoleBindingLister
	roleBindings       v12.RoleBindingInterface
	roleBindingIndexer cache.Indexer
	policies           v1beta1.PodSecurityPolicyInterface
	roleLister         v12.ClusterRoleLister
	namespaceLister    v13.NamespaceLister
	projectLister      v3.ProjectLister
	psptpbLister       v3.PodSecurityPolicyTemplateProjectBindingLister
}

func (m *serviceAccountManager) sync(key string, obj *v1.ServiceAccount) error {
	if obj == nil {
		// do nothing
		return nil
	}

	namespace, err := m.namespaceLister.Get("", obj.Namespace)
	if err != nil {
		return fmt.Errorf("error getting projects: %v", err)
	}

	if namespace.Annotations[projectIDAnnotation] == "" {
		return nil
	}

	psptpbs, err := m.psptpbIndexer.ByIndex(psptpbByTargetProjectNameAnnotationIndex, namespace.Annotations[projectIDAnnotation])
	if err != nil {
		return fmt.Errorf("error getting psptpbs: %v", err)
	}

	onePSPTPBExists := false
	desiredBindings := map[string]*v3.PodSecurityPolicyTemplateProjectBinding{}

	for _, rawPSPTPB := range psptpbs {
		psptpb, ok := rawPSPTPB.(*v3.PodSecurityPolicyTemplateProjectBinding)
		if !ok {
			return fmt.Errorf("could not convert to *v3.PodSecurityPolicyTemplateProjectBinding: %v", rawPSPTPB)
		}

		if psptpb.DeletionTimestamp != nil {
			continue
		}

		onePSPTPBExists = true

		key := getClusterRoleName(psptpb.PodSecurityPolicyTemplateName)
		desiredBindings[key] = psptpb
	}

	originalDesiredBindingsLen := len(desiredBindings)

	roleBindings, err := m.roleBindingIndexer.ByIndex(roleBindingByServiceAccountIndex, obj.Namespace+"-"+obj.Name)
	if err != nil {
		return fmt.Errorf("error getting role bindings: %v", err)
	}

	cluster, err := m.clusterLister.Get("", m.clusterName)
	if err != nil {
		return fmt.Errorf("error getting cluster: %v", err)
	}

	for _, rawRoleBinding := range roleBindings {
		roleBinding, ok := rawRoleBinding.(*rbac.RoleBinding)
		if !ok {
			return fmt.Errorf("could not convert to *rbac2.RoleBinding: %v", rawRoleBinding)
		}

		key := roleBinding.RoleRef.Name

		if desiredBindings[key] == nil && okToDelete(obj, roleBinding, cluster, originalDesiredBindingsLen) {
			err = m.roleBindings.DeleteNamespaced(roleBinding.Namespace, roleBinding.Name, &metav1.DeleteOptions{})
			if err != nil {
				return fmt.Errorf("error deleting role binding: %v", err)
			}
		} else {
			delete(desiredBindings, key)
		}
	}

	for clusterRoleName, desiredBinding := range desiredBindings {
		roleBindingName := getRoleBindingName(obj, clusterRoleName)
		_, err = m.roleBindings.Create(&rbac.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      roleBindingName,
				Namespace: obj.Namespace,
				Annotations: map[string]string{
					podSecurityPolicyTemplateParentAnnotation: desiredBinding.PodSecurityPolicyTemplateName,
					psptpbRoleBindingAnnotation:               "true",
				},
				OwnerReferences: []metav1.OwnerReference{
					{
						APIVersion: "v1",
						Name:       obj.Name,
						Kind:       "ServiceAccount",
						UID:        obj.UID,
					},
				},
			},
			TypeMeta: metav1.TypeMeta{
				Kind: "RoleBinding",
			},
			RoleRef: rbac.RoleRef{
				APIGroup: apiGroup,
				Name:     clusterRoleName,
				Kind:     "ClusterRole",
			},
			Subjects: []rbac.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      obj.Name,
					Namespace: obj.Namespace,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("error creating binding: %v", err)
		}
	}

	if !onePSPTPBExists {
		// create default pspt role binding if it is set
		clusterRoleName := getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)
		roleBindingName := getDefaultRoleBindingName(obj, clusterRoleName)

		if cluster.Spec.DefaultPodSecurityPolicyTemplateName != "" {
			_, err := m.roleBindingLister.Get(obj.Namespace, roleBindingName)
			if err != nil {
				if errors.IsNotFound(err) {
					_, err = m.roleBindings.Create(&rbac.RoleBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:      roleBindingName,
							Namespace: obj.Namespace,
							Annotations: map[string]string{
								podSecurityPolicyTemplateParentAnnotation: cluster.Spec.DefaultPodSecurityPolicyTemplateName,
								psptpbRoleBindingAnnotation:               "true",
							},
							OwnerReferences: []metav1.OwnerReference{
								{
									APIVersion: "v1",
									Name:       obj.Name,
									Kind:       "ServiceAccount",
									UID:        obj.UID,
								},
							},
						},
						TypeMeta: metav1.TypeMeta{
							Kind: "RoleBinding",
						},
						RoleRef: rbac.RoleRef{
							APIGroup: apiGroup,
							Name:     clusterRoleName,
							Kind:     "ClusterRole",
						},
						Subjects: []rbac.Subject{
							{
								Kind:      "ServiceAccount",
								Name:      obj.Name,
								Namespace: obj.Namespace,
							},
						},
					})
					if err != nil {
						return fmt.Errorf("error creating role binding: %v", err)
					}
				} else {
					return fmt.Errorf("error getting role binding %v: %v", roleBindingName, err)
				}
			}
		}
	}

	return nil
}

func okToDelete(svcAct *v1.ServiceAccount, rb *rbac.RoleBinding, cluster *v3.Cluster,
	originalDesiredBindingsLen int) bool {
	// This is not a role binding this logic should manage so exit immediately
	if rb.Annotations[psptpbRoleBindingAnnotation] == "" {
		return false
	}

	// No default PSPT is set so its ok to delete this if its a normal rolebinding or a leftover default PSPT binding
	if cluster.Spec.DefaultPodSecurityPolicyTemplateName == "" {
		return true
	}

	// at least one PSPTPB exists so we need to delete all default PSPT bindings
	if originalDesiredBindingsLen > 0 {
		return true
	}

	// the default PSPT has changed so we need to clean it up before creating the new one
	if getDefaultRoleBindingName(svcAct,
		getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)) != rb.Name {
		return true
	}

	return false
}

func getRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getDefaultRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("default-%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getClusterRoleName(podSecurityPolicyTemplateName string) string {
	return fmt.Sprintf("%v-clusterrole", podSecurityPolicyTemplateName)
}

func resyncServiceAccounts(serviceAccountLister v13.ServiceAccountLister,
	serviceAccountController v13.ServiceAccountController, namespace string) error {
	serviceAccounts, err := serviceAccountLister.List(namespace, labels.Everything())
	if err != nil {
		return fmt.Errorf("error getting service accounts: %v", err)
	}

	for _, account := range serviceAccounts {
		serviceAccountController.Enqueue(account.Namespace, account.Name)
	}

	return nil
}
<commit_msg>Fix so project-less namespaces have their PSPT removed<commit_after>package podsecuritypolicy

import (
	"fmt"

	v13 "github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/extensions/v1beta1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	v12 "github.com/rancher/types/apis/rbac.authorization.k8s.io/v1"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	rbac "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const psptpbByTargetProjectNameAnnotationIndex = "podsecuritypolicy.rbac.user.cattle.io/psptpb-by-project-id"
const roleBindingByServiceAccountIndex = "podsecuritypolicy.rbac.user.cattle.io/role-binding-by-service-account"
const psptpbRoleBindingAnnotation = "podsecuritypolicy.rbac.user.cattle.io/psptpb-role-binding"

// RegisterServiceAccount ensures that:
// 	1. Each namespace has a pod security policy assigned to a role if:
//		a. its project has a PSPT assigned to it
//		OR
//		b. its cluster has a default PSPT assigned to it
//  2. PSPs are bound to their associated service accounts via a cluster role binding
func RegisterServiceAccount(context *config.UserContext) {
	logrus.Infof("registering podsecuritypolicy serviceaccount handler for cluster %v", context.ClusterName)

	psptpbInformer := context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").Controller().Informer()
	psptpbIndexers := map[string]cache.IndexFunc{
		psptpbByTargetProjectNameAnnotationIndex: psptpbByTargetProjectName,
	}
	psptpbInformer.AddIndexers(psptpbIndexers)

	roleBindingInformer := context.RBAC.RoleBindings("").Controller().Informer()
	roleBindingIndexers := map[string]cache.IndexFunc{
		roleBindingByServiceAccountIndex: roleBindingByServiceAccount,
	}
	roleBindingInformer.AddIndexers(roleBindingIndexers)

	m := &serviceAccountManager{
		clusterName:        context.ClusterName,
		clusters:           context.Management.Management.Clusters(""),
		pspts:              context.Management.Management.PodSecurityPolicyTemplates(""),
		roleBindings:       context.RBAC.RoleBindings(""),
		roleBindingIndexer: roleBindingInformer.GetIndexer(),

		policies:      context.Extensions.PodSecurityPolicies(""),
		psptpbIndexer: psptpbInformer.GetIndexer(),

		clusterLister:     context.Management.Management.Clusters("").Controller().Lister(),
		psptLister:        context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		templateLister:    context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		policyLister:      context.Extensions.PodSecurityPolicies("").Controller().Lister(),
		roleBindingLister: context.RBAC.RoleBindings("").Controller().Lister(),
		roleLister:        context.RBAC.ClusterRoles("").Controller().Lister(),
		namespaceLister:   context.Core.Namespaces("").Controller().Lister(),
		projectLister:     context.Management.Management.Projects("").Controller().Lister(),
		psptpbLister: context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").
			Controller().Lister(),
	}

	context.Core.ServiceAccounts("").AddHandler("ServiceAccountLifecycleHandler", m.sync)
}

func psptpbByTargetProjectName(obj interface{}) ([]string, error) {
	psptpb, ok := obj.(*v3.PodSecurityPolicyTemplateProjectBinding)
	if !ok || psptpb.TargetProjectName == "" {
		return []string{}, nil
	}

	return []string{psptpb.TargetProjectName}, nil
}

func roleBindingByServiceAccount(obj interface{}) ([]string, error) {
	roleBinding, ok := obj.(*rbac.RoleBinding)
	if !ok || len(roleBinding.Subjects) != 1 ||
		roleBinding.Subjects[0].Name == "" ||
		roleBinding.Subjects[0].Namespace == "" {
		return []string{}, nil
	}

	subject := roleBinding.Subjects[0]
	return []string{subject.Namespace + "-" + subject.Name}, nil
}

type serviceAccountManager struct {
	clusterName        string
	clusterLister      v3.ClusterLister
	clusters           v3.ClusterInterface
	pspts              v3.PodSecurityPolicyTemplateInterface
	psptLister         v3.PodSecurityPolicyTemplateLister
	psptpbIndexer      cache.Indexer
	templateLister     v3.PodSecurityPolicyTemplateLister
	policyLister       v1beta1.PodSecurityPolicyLister
	roleBindingLister  v12.RoleBindingLister
	roleBindings       v12.RoleBindingInterface
	roleBindingIndexer cache.Indexer
	policies           v1beta1.PodSecurityPolicyInterface
	roleLister         v12.ClusterRoleLister
	namespaceLister    v13.NamespaceLister
	projectLister      v3.ProjectLister
	psptpbLister       v3.PodSecurityPolicyTemplateProjectBindingLister
}

func (m *serviceAccountManager) sync(key string, obj *v1.ServiceAccount) error {
	if obj == nil {
		// do nothing
		return nil
	}

	namespace, err := m.namespaceLister.Get("", obj.Namespace)
	if err != nil {
		return fmt.Errorf("error getting projects: %v", err)
	}

	var psptpbs []interface{}

	if namespace.Annotations[projectIDAnnotation] != "" {
		psptpbs, err = m.psptpbIndexer.ByIndex(psptpbByTargetProjectNameAnnotationIndex, namespace.Annotations[projectIDAnnotation])
		if err != nil {
			return fmt.Errorf("error getting psptpbs: %v", err)
		}
	}

	onePSPTPBExists := false
	desiredBindings := map[string]*v3.PodSecurityPolicyTemplateProjectBinding{}

	for _, rawPSPTPB := range psptpbs {
		psptpb, ok := rawPSPTPB.(*v3.PodSecurityPolicyTemplateProjectBinding)
		if !ok {
			return fmt.Errorf("could not convert to *v3.PodSecurityPolicyTemplateProjectBinding: %v", rawPSPTPB)
		}

		if psptpb.DeletionTimestamp != nil {
			continue
		}

		onePSPTPBExists = true

		key := getClusterRoleName(psptpb.PodSecurityPolicyTemplateName)
		desiredBindings[key] = psptpb
	}

	originalDesiredBindingsLen := len(desiredBindings)

	roleBindings, err := m.roleBindingIndexer.ByIndex(roleBindingByServiceAccountIndex, obj.Namespace+"-"+obj.Name)
	if err != nil {
		return fmt.Errorf("error getting role bindings: %v", err)
	}

	cluster, err := m.clusterLister.Get("", m.clusterName)
	if err != nil {
		return fmt.Errorf("error getting cluster: %v", err)
	}

	for _, rawRoleBinding := range roleBindings {
		roleBinding, ok := rawRoleBinding.(*rbac.RoleBinding)
		if !ok {
			return fmt.Errorf("could not convert to *rbac2.RoleBinding: %v", rawRoleBinding)
		}

		key := roleBinding.RoleRef.Name

		if desiredBindings[key] == nil && okToDelete(obj, roleBinding, cluster, originalDesiredBindingsLen) {
			err = m.roleBindings.DeleteNamespaced(roleBinding.Namespace, roleBinding.Name, &metav1.DeleteOptions{})
			if err != nil {
				return fmt.Errorf("error deleting role binding: %v", err)
			}
		} else {
			delete(desiredBindings, key)
		}
	}

	for clusterRoleName, desiredBinding := range desiredBindings {
		roleBindingName := getRoleBindingName(obj, clusterRoleName)
		_, err = m.roleBindings.Create(&rbac.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      roleBindingName,
				Namespace: obj.Namespace,
				Annotations: map[string]string{
					podSecurityPolicyTemplateParentAnnotation: desiredBinding.PodSecurityPolicyTemplateName,
					psptpbRoleBindingAnnotation:               "true",
				},
				OwnerReferences: []metav1.OwnerReference{
					{
						APIVersion: "v1",
						Name:       obj.Name,
						Kind:       "ServiceAccount",
						UID:        obj.UID,
					},
				},
			},
			TypeMeta: metav1.TypeMeta{
				Kind: "RoleBinding",
			},
			RoleRef: rbac.RoleRef{
				APIGroup: apiGroup,
				Name:     clusterRoleName,
				Kind:     "ClusterRole",
			},
			Subjects: []rbac.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      obj.Name,
					Namespace: obj.Namespace,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("error creating binding: %v", err)
		}
	}

	if !onePSPTPBExists && namespace.Annotations[projectIDAnnotation] != "" {
		// create default pspt role binding if it is set
		clusterRoleName := getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)
		roleBindingName := getDefaultRoleBindingName(obj, clusterRoleName)

		if cluster.Spec.DefaultPodSecurityPolicyTemplateName != "" {
			_, err := m.roleBindingLister.Get(obj.Namespace, roleBindingName)
			if err != nil {
				if errors.IsNotFound(err) {
					_, err = m.roleBindings.Create(&rbac.RoleBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:      roleBindingName,
							Namespace: obj.Namespace,
							Annotations: map[string]string{
								podSecurityPolicyTemplateParentAnnotation: cluster.Spec.DefaultPodSecurityPolicyTemplateName,
								psptpbRoleBindingAnnotation:               "true",
							},
							OwnerReferences: []metav1.OwnerReference{
								{
									APIVersion: "v1",
									Name:       obj.Name,
									Kind:       "ServiceAccount",
									UID:        obj.UID,
								},
							},
						},
						TypeMeta: metav1.TypeMeta{
							Kind: "RoleBinding",
						},
						RoleRef: rbac.RoleRef{
							APIGroup: apiGroup,
							Name:     clusterRoleName,
							Kind:     "ClusterRole",
						},
						Subjects: []rbac.Subject{
							{
								Kind:      "ServiceAccount",
								Name:      obj.Name,
								Namespace: obj.Namespace,
							},
						},
					})
					if err != nil {
						return fmt.Errorf("error creating role binding: %v", err)
					}
				} else {
					return fmt.Errorf("error getting role binding %v: %v", roleBindingName, err)
				}
			}
		}
	}

	return nil
}

func okToDelete(svcAct *v1.ServiceAccount, rb *rbac.RoleBinding, cluster *v3.Cluster,
	originalDesiredBindingsLen int) bool {
	// This is not a role binding this logic should manage so exit immediately
	if rb.Annotations[psptpbRoleBindingAnnotation] == "" {
		return false
	}

	// Namespace isn't in a project so it should have no role bindings
	if rb.Annotations[projectIDAnnotation] == "" {
		return true
	}

	// No default PSPT is set so its ok to delete this if its a normal rolebinding or a leftover default PSPT binding
	if cluster.Spec.DefaultPodSecurityPolicyTemplateName == "" {
		return true
	}

	// at least one PSPTPB exists so we need to delete all default PSPT bindings
	if originalDesiredBindingsLen > 0 {
		return true
	}

	// the default PSPT has changed so we need to clean it up before creating the new one
	if getDefaultRoleBindingName(svcAct,
		getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)) != rb.Name {
		return true
	}

	return false
}

func getRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getDefaultRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("default-%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getClusterRoleName(podSecurityPolicyTemplateName string) string {
	return fmt.Sprintf("%v-clusterrole", podSecurityPolicyTemplateName)
}

func resyncServiceAccounts(serviceAccountLister v13.ServiceAccountLister,
	serviceAccountController v13.ServiceAccountController, namespace string) error {
	serviceAccounts, err := serviceAccountLister.List(namespace, labels.Everything())
	if err != nil {
		return fmt.Errorf("error getting service accounts: %v", err)
	}

	for _, account := range serviceAccounts {
		serviceAccountController.Enqueue(account.Namespace, account.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Nuclio 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 functiontemplates

import (
	"archive/zip"
	"bytes"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/nuclio/nuclio/pkg/common"

	"github.com/nuclio/errors"
	"github.com/nuclio/logger"
)

type ZipFunctionTemplateFetcher struct {
	BaseFunctionTemplateFetcher

	fileAddress string
	logger      logger.Logger
}

func NewZipFunctionTemplateFetcher(parentLogger logger.Logger, fileAddress string) (*ZipFunctionTemplateFetcher, error) {

	return &ZipFunctionTemplateFetcher{
		fileAddress: fileAddress,
		logger:      parentLogger.GetChild("ZipFunctionTemplateFetcher"),
	}, nil
}

func (zftf *ZipFunctionTemplateFetcher) Fetch() ([]*FunctionTemplate, error) {
	var functionTemplates []*FunctionTemplate
	var functionsFileContents map[string]*FunctionTemplateFileContents
	var zipFileBody []byte
	var err error

	zftf.logger.DebugWith("Getting the zip file from the given address", "fileAddress", zftf.fileAddress)

	if common.IsLocalFileURL(zftf.fileAddress) {
		localFilePath := common.GetPathFromLocalFileURL(zftf.fileAddress)
		zipFileBody, err = ioutil.ReadFile(localFilePath)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read local file")
		}

	} else {

		response, err := http.Get(zftf.fileAddress)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to get zip file")
		}
		defer response.Body.Close() // nolint: errcheck

		zipFileBody, err = ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read response body")
		}
	}

	zipReader, err := zip.NewReader(bytes.NewReader(zipFileBody), int64(len(zipFileBody)))
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create zip reader")
	}

	functionsFileContents = zftf.parseFiles(zipReader)

	// parse every function file contents into a function template object
	for functionName, ffc := range functionsFileContents {
		functionTemplate, err := zftf.createFunctionTemplate(*ffc, functionName)
		if err != nil {
			zftf.logger.WarnWith("Failed to create function template",
				"functionName",
				functionName,
				"err",
				err)
		}

		if functionTemplate != nil {
			functionTemplates = append(functionTemplates, functionTemplate)
		}
	}

	return functionTemplates, nil
}

func (zftf *ZipFunctionTemplateFetcher) parseFiles(zipReader *zip.Reader) map[string]*FunctionTemplateFileContents {
	functionTemplateFileContents := make(map[string]*FunctionTemplateFileContents)

	// Read all the files from zip archive
	for _, zipFile := range zipReader.File {
		fileName := zipFile.Name
		splitFileName := strings.Split(fileName, "/")

		// make sure it's a file inside a valid function folder path and it's not a folder
		if zipFile.FileInfo().IsDir() || len(splitFileName) != 3 {
			continue
		}

		functionName := splitFileName[1]
		fileNameWithoutPath := splitFileName[2]

		// get file contents
		fileContents, err := common.GetZipFileContents(zipFile)
		if err != nil {
			zftf.logger.WarnWith("Failed to get zip file contents. Ignoring file", "fileName", fileName, "err", err)
			continue
		}

		// if functionTemplateFileContents was not created for this function - create it
		if _, ok := functionTemplateFileContents[functionName]; !ok {
			zftf.logger.Debug("Found new function files", "functionName", functionName)

			functionTemplateFileContents[functionName] = &FunctionTemplateFileContents{}
		}
		fs := functionTemplateFileContents[functionName]

		if strings.Contains(fileNameWithoutPath, ".values") {
			fs.Values = fileContents

		} else if strings.Contains(fileNameWithoutPath, ".template") {
			fs.Template = fileContents

		} else {
			fs.Code = fileContents
		}
	}

	return functionTemplateFileContents
}
<commit_msg>Fix function files discovery log line (#1573)<commit_after>/*
Copyright 2017 The Nuclio 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 functiontemplates

import (
	"archive/zip"
	"bytes"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/nuclio/nuclio/pkg/common"

	"github.com/nuclio/errors"
	"github.com/nuclio/logger"
)

type ZipFunctionTemplateFetcher struct {
	BaseFunctionTemplateFetcher

	fileAddress string
	logger      logger.Logger
}

func NewZipFunctionTemplateFetcher(parentLogger logger.Logger, fileAddress string) (*ZipFunctionTemplateFetcher, error) {

	return &ZipFunctionTemplateFetcher{
		fileAddress: fileAddress,
		logger:      parentLogger.GetChild("ZipFunctionTemplateFetcher"),
	}, nil
}

func (zftf *ZipFunctionTemplateFetcher) Fetch() ([]*FunctionTemplate, error) {
	var functionTemplates []*FunctionTemplate
	var functionsFileContents map[string]*FunctionTemplateFileContents
	var zipFileBody []byte
	var err error

	zftf.logger.DebugWith("Getting the zip file from the given address", "fileAddress", zftf.fileAddress)

	if common.IsLocalFileURL(zftf.fileAddress) {
		localFilePath := common.GetPathFromLocalFileURL(zftf.fileAddress)
		zipFileBody, err = ioutil.ReadFile(localFilePath)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read local file")
		}

	} else {

		response, err := http.Get(zftf.fileAddress)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to get zip file")
		}
		defer response.Body.Close() // nolint: errcheck

		zipFileBody, err = ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read response body")
		}
	}

	zipReader, err := zip.NewReader(bytes.NewReader(zipFileBody), int64(len(zipFileBody)))
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create zip reader")
	}

	functionsFileContents = zftf.parseFiles(zipReader)

	// parse every function file contents into a function template object
	for functionName, ffc := range functionsFileContents {
		functionTemplate, err := zftf.createFunctionTemplate(*ffc, functionName)
		if err != nil {
			zftf.logger.WarnWith("Failed to create function template",
				"functionName",
				functionName,
				"err",
				err)
		}

		if functionTemplate != nil {
			functionTemplates = append(functionTemplates, functionTemplate)
		}
	}

	return functionTemplates, nil
}

func (zftf *ZipFunctionTemplateFetcher) parseFiles(zipReader *zip.Reader) map[string]*FunctionTemplateFileContents {
	functionTemplateFileContents := make(map[string]*FunctionTemplateFileContents)

	// Read all the files from zip archive
	for _, zipFile := range zipReader.File {
		fileName := zipFile.Name
		splitFileName := strings.Split(fileName, "/")

		// make sure it's a file inside a valid function folder path and it's not a folder
		if zipFile.FileInfo().IsDir() || len(splitFileName) != 3 {
			continue
		}

		functionName := splitFileName[1]
		fileNameWithoutPath := splitFileName[2]

		// get file contents
		fileContents, err := common.GetZipFileContents(zipFile)
		if err != nil {
			zftf.logger.WarnWith("Failed to get zip file contents. Ignoring file", "fileName", fileName, "err", err)
			continue
		}

		// if functionTemplateFileContents was not created for this function - create it
		if _, ok := functionTemplateFileContents[functionName]; !ok {
			zftf.logger.DebugWith("Found new function files", "functionName", functionName)

			functionTemplateFileContents[functionName] = &FunctionTemplateFileContents{}
		}
		fs := functionTemplateFileContents[functionName]

		if strings.Contains(fileNameWithoutPath, ".values") {
			fs.Values = fileContents

		} else if strings.Contains(fileNameWithoutPath, ".template") {
			fs.Template = fileContents

		} else {
			fs.Code = fileContents
		}
	}

	return functionTemplateFileContents
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/clsung/tailer"
	"github.com/jessevdk/go-flags"
)

var version = "v0.0.1"

type cmdOpts struct {
	OptVersion    bool   `short:"v" long:"version" description:"print the version and exit"`
	OptNats       bool   `long:"nats" description:"Using nats to publish" default:"false"`
	OptConfigfile string `long:"config" description:"config file" optional:"yes"`
}

func main() {
	var err error
	var exitCode int

	defer func() { os.Exit(exitCode) }()
	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	done := make(chan bool)

	opts := &cmdOpts{}
	p := flags.NewParser(opts, flags.Default)
	p.Usage = "[OPTIONS] DIR1[,DIR2...]"

	args, err := p.Parse()

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "tailer: %s\n", version)
		return
	}

	if err != nil || len(args) == 0 {
		p.WriteHelp(os.Stderr)
		exitCode = 1
		return
	}

	config := tailer.Config{FileGlob: "*-*"} // assume abc-def.log
	if opts.OptConfigfile != "" {
		file, err := os.Open(opts.OptConfigfile)
		if err != nil {
			if os.IsNotExist(err) {
				fmt.Println("Using default config")
			} else {
				fmt.Printf("error: %v\n", err)
				exitCode = 1
				return
			}
		} else {
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&config)
			if err != nil {
				fmt.Printf("error: %v\n", err)
				exitCode = 1
				return
			}
		}
	}

	watchDirs := strings.Split(args[0], ",")

	tailer, err := tailer.NewTailer(opts.OptNats, config)
	if err != nil {
		fmt.Printf("error: %v\n", err)
		exitCode = 1
		return
	}
	tailer.Serve(watchDirs, config.FileGlob)

	// TODO: exit if all watched files removed or closed
	<-done
	fmt.Println("Exit with 0")
}
<commit_msg>add TAILER_CONFIG env for docker<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/clsung/tailer"
	"github.com/jessevdk/go-flags"
)

var version = "v0.0.1"

type cmdOpts struct {
	OptVersion    bool   `short:"v" long:"version" description:"print the version and exit"`
	OptNats       bool   `long:"nats" description:"Using nats to publish" default:"false"`
	OptConfigfile string `long:"config" description:"config file" optional:"yes"`
}

func main() {
	var err error
	var exitCode int

	defer func() { os.Exit(exitCode) }()
	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	done := make(chan bool)

	opts := &cmdOpts{}
	p := flags.NewParser(opts, flags.Default)
	p.Usage = "[OPTIONS] DIR1[,DIR2...]"

	args, err := p.Parse()

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "tailer: %s\n", version)
		return
	}

	if err != nil || len(args) == 0 {
		p.WriteHelp(os.Stderr)
		exitCode = 1
		return
	}

	config := tailer.Config{FileGlob: "*-*"} // assume abc-def.log
	configFile := opts.OptConfigfile
	if configFile != "" {
		configFile = os.Getenv("TAILER_CONFIG")
	}
	if configFile != "" {
		file, err := os.Open(configFile)
		if err != nil {
			if os.IsNotExist(err) {
				fmt.Println("Using default config")
			} else {
				fmt.Printf("error: %v\n", err)
				exitCode = 1
				return
			}
		} else {
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&config)
			if err != nil {
				fmt.Printf("error: %v\n", err)
				exitCode = 1
				return
			}
		}
	}

	watchDirs := strings.Split(args[0], ",")

	tailer, err := tailer.NewTailer(opts.OptNats, config)
	if err != nil {
		fmt.Printf("error: %v\n", err)
		exitCode = 1
		return
	}
	tailer.Serve(watchDirs, config.FileGlob)

	// TODO: exit if all watched files removed or closed
	<-done
	fmt.Println("Exit with 0")
}
<|endoftext|>