<commit_before>package main

import (
	"bytes"
	"crypto"
	"crypto/hmac"
	_ "crypto/md5"
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
	"encoding/hex"
	"fmt"
	_ "golang.org/x/crypto/sha3"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

import flag "github.com/spf13/pflag"

const version string = "v2.2"

func getOutput(results *Checksums) []*Output {
	outputs := make([]*Output, 0, len(results.checksums)+1)
	prefix, file := escapeFilename(results.file)
	if opts.size {
		outputs = append(outputs, &Output{
			File: file,
			Name: "SIZE",
			Sum:  strconv.FormatInt(results.checksums[0].written, 10),
		})
	}
	for i := range results.checksums {
		outputs = append(outputs, &Output{
			File: file,
			Name: algorithms[results.checksums[i].hash].name,
			Sum:  prefix + hex.EncodeToString(results.checksums[i].sum),
		})
	}
	return outputs
}

func display(results *Checksums) {
	file := results.file
	if opts.check != "\x00" {
		var ok bool
		var i int
		for i = range results.checksums {
			if macKey != nil {
				ok = hmac.Equal(results.checksums[i].sum, results.checksums[i].csum)
			} else {
				ok = bytes.Equal(results.checksums[i].sum, results.checksums[i].csum)
			}
			if ok {
				if !opts.quiet && !opts.status {
					if opts.verbose {
						fmt.Printf("%s: %s OK\n", file, algorithms[results.checksums[i].hash].name)
					} else {
						fmt.Printf("%s: OK\n", file)
					}
				}
			} else {
				stats.unmatched++
				if !opts.status {
					if opts.verbose {
						fmt.Printf("%s: %s FAILED with %s\n", file, algorithms[results.checksums[i].hash].name, hex.EncodeToString(results.checksums[i].sum))
					} else {
						fmt.Printf("%s: FAILED\n", file)
					}
				}
			}
		}
	} else {
		if err := format.Execute(os.Stdout, getOutput(results)); err != nil {
			logger.Print(err)
		}
	}
}

func init() {
	logger = log.New(os.Stderr, "ERROR: ", 0)

	progname := filepath.Base(os.Args[0])

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] [-s STRING...]|[-c FILE]|[-i FILE]|[FILE...]|[-r FILE... DIRECTORY...]\n", progname)
		flag.PrintDefaults()
	}

	if strings.HasPrefix(progname, "xhash") {
		flag.BoolVarP(&opts.all, "all", "a", false, "all algorithms (except others specified, if any)")
	}
	if strings.Contains(progname, "sum") {
		flag.BoolVarP(&opts.dummy, "binary", "b", false, "read in binary mode")
		flag.BoolVarP(&opts.dummy, "text", "t", false, "read in text mode (default)")
		flag.BoolVarP(&opts.tag, "tag", "", false, "create a BSD-style checksum")
	} else {
		flag.BoolVarP(&opts.gnu, "gnu", "", false, "output hashes in the format used by md5sum")
	}
	flag.BoolVarP(&opts.ignore, "ignore-missing", "", false, "don't fail or report status for missing files")
	flag.BoolVarP(&opts.quiet, "quiet", "q", false, "don't print OK for each successfully verified file")
	flag.BoolVarP(&opts.mmap, "mmap", "M", false, "use mmap - EXPERIMENTAL")
	flag.BoolVarP(&opts.recursive, "recursive", "r", false, "recurse into directories")
	flag.BoolVarP(&opts.size, "size", "", false, "output size")
	flag.BoolVarP(&opts.status, "status", "S", false, "don't output anything, status code shows success")
	flag.BoolVarP(&opts.strict, "strict", "", false, "exit non-zero for improperly formatted checksum lines")
	flag.BoolVarP(&opts.str, "string", "s", false, "treat arguments as strings")
	flag.BoolVarP(&opts.symlinks, "symlinks", "L", false, "don't follow symbolic links with the -r option")
	flag.BoolVarP(&opts.verbose, "verbose", "v", false, "verbose operation")
	flag.BoolVarP(&opts.version, "version", "", false, "show version and exit")
	flag.BoolVarP(&opts.warn, "warn", "w", false, "warn about improperly formatted checksum lines")
	flag.BoolVarP(&opts.zero, "zero", "z", false, "consider NUL instead as EOL and disable file name escaping")
	flag.StringVarP(&opts.check, "check", "c", "\x00", "read checksums from file (use \"\" for stdin)")
	flag.StringVarP(&opts.input, "input", "i", "\x00", "read pathnames from file (use \"\" for stdin)")
	flag.StringVarP(&opts.key, "hmac", "H", "\x00", "key for HMAC (in hexadecimal) or read from specified pathname")
	if strings.Contains(progname, "sum") {
		flag.StringVarP(&opts.format, "format", "f", gnuFormat, "output format")
	} else {
		flag.StringVarP(&opts.format, "format", "f", bsdFormat, "output format")
	}

	// Allow xhash to be hard-linked to "md5sum" or "md5", etc and only use that algorithm
	if !strings.HasPrefix(progname, "xhash") {
		cmd := strings.TrimSuffix(strings.TrimSuffix(progname, ".exe"), "sum")
		var defaults = map[string]crypto.Hash{
			"b2":     crypto.BLAKE2b_512,
			"md5":    crypto.MD5,
			"sha1":   crypto.SHA1,
			"sha224": crypto.SHA224,
			"sha256": crypto.SHA256,
			"sha384": crypto.SHA384,
			"sha512": crypto.SHA512,
		}
		if h, ok := defaults[cmd]; ok {
			hashes = []crypto.Hash{h}
		}
	}

	max := 0
	for _, h := range hashes {
		if int(h) > max {
			max = int(h)
		}
	}

	algorithms = make(map[crypto.Hash]*Algorithm)
	for _, h := range hashes {
		if h.Available() {
			algorithms[h] = &Algorithm{
				name: strings.ReplaceAll(strings.ReplaceAll(h.String(), "SHA-", "SHA"), "/", "-"),
			}
			if strings.HasPrefix(progname, "xhash") {
				flag.BoolVar(
					&algorithms[h].check,
					strings.ToLower(algorithms[h].name),
					false,
					h.String()+" algorithm")
			}
		}
	}
	flag.Parse()

	if opts.input != "\x00" && opts.check != "\x00" {
		logger.Fatal("The --input & --check options are mutually exclusive")
	}

	if opts.version {
		fmt.Printf("%s\t%v %s/%s\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
		fmt.Printf("Supported hashes:")
		for _, h := range hashes {
			fmt.Printf(" %s", algorithms[h].name)
		}
		fmt.Println()
		os.Exit(0)
	}

	if strings.HasPrefix(progname, "xhash") {
		if opts.all {
			// Ignore algorithm if --all was specified
			for h := range algorithms {
				algorithms[h].check = !algorithms[h].check
			}
		}

		// Initialize chosen and populate name2Hash
		for _, h := range hashes {
			if h.Available() && algorithms[h].check {
				chosen = append(chosen, &Checksum{hash: h})
			}
			name2Hash[algorithms[h].name] = h
		}

		if opts.check == "\x00" && len(chosen) == 0 {
			// SHA-256 is default
			chosen = append(chosen, &Checksum{hash: crypto.SHA256})
		}
	} else {
		chosen = []*Checksum{&Checksum{hash: hashes[0]}}
		name2Hash[algorithms[hashes[0]].name] = hashes[0]
	}

	if opts.key != "\x00" {
		var err error
		if opts.key == "" {
			if macKey, err = io.ReadAll(os.Stdin); err != nil {
				logger.Fatal(err)
			}
		} else if macKey, err = hex.DecodeString(opts.key); err != nil {
			if macKey, err = os.ReadFile(opts.key); err != nil {
				logger.Fatal(err)
			}
		}
	}

	if opts.gnu {
		opts.format = gnuFormat
	} else if opts.tag {
		opts.format = bsdFormat
	}
	opts.format = unescape(opts.format)
	if opts.zero && !strings.Contains(opts.format, "\x00") {
		opts.format = strings.ReplaceAll(opts.format, "\n", "\x00")
	}
	var err error
	format, err = format.Parse(opts.format)
	if err != nil {
		logger.Fatal(err)
	}

	if strings.Contains(progname, "sum") {
		opts.gnu = true
	}
}

func main() {
	inputFrom := inputFromArgs
	if opts.check != "\x00" {
		inputFrom = inputFromCheck
	} else if opts.input != "\x00" {
		inputFrom = inputFromFile
	} else if flag.NArg() == 0 {
		display(hashStdin(nil))
		os.Exit(0)
	} else if opts.recursive {
		inputFrom = inputFromDir
	} else if opts.str {
		for _, s := range flag.Args() {
			display(hashString(s))
		}
		os.Exit(0)
	}

	var wg sync.WaitGroup
	channel := make(chan *Checksums)

	go func() {
		for input := range inputFrom(nil) {
			wg.Add(1)
			go func(input *Checksums) {
				defer wg.Done()
				channel <- hashFile(input)
			}(input)

		}
		wg.Wait()
		close(channel)
	}()

	for checksum := range channel {
		if checksum != nil {
			display(checksum)
		}
	}

	if opts.check != "\x00" || opts.input != "\x00" {
		plural := ""
		if !opts.status && stats.unreadable > 0 {
			if stats.unreadable > 1 {
				plural = "s"
			}
			fmt.Fprintf(os.Stderr, "WARNING: %d listed file%s could not be read\n", stats.unreadable, plural)
		}
		if !opts.status && stats.unmatched > 0 {
			if stats.unmatched > 1 {
				plural = "s"
			}
			fmt.Fprintf(os.Stderr, "WARNING: %d computed checksum%s did NOT match\n", stats.unmatched, plural)
		}
	}
	if stats.unreadable > 0 || stats.unmatched > 0 || stats.invalid > 0 {
		os.Exit(1)
	}
}
<commit_msg>Bump version<commit_after>package main

import (
	"bytes"
	"crypto"
	"crypto/hmac"
	_ "crypto/md5"
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
	"encoding/hex"
	"fmt"
	_ "golang.org/x/crypto/sha3"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

import flag "github.com/spf13/pflag"

const version string = "v2.3"

func getOutput(results *Checksums) []*Output {
	outputs := make([]*Output, 0, len(results.checksums)+1)
	prefix, file := escapeFilename(results.file)
	if opts.size {
		outputs = append(outputs, &Output{
			File: file,
			Name: "SIZE",
			Sum:  strconv.FormatInt(results.checksums[0].written, 10),
		})
	}
	for i := range results.checksums {
		outputs = append(outputs, &Output{
			File: file,
			Name: algorithms[results.checksums[i].hash].name,
			Sum:  prefix + hex.EncodeToString(results.checksums[i].sum),
		})
	}
	return outputs
}

func display(results *Checksums) {
	file := results.file
	if opts.check != "\x00" {
		var ok bool
		var i int
		for i = range results.checksums {
			if macKey != nil {
				ok = hmac.Equal(results.checksums[i].sum, results.checksums[i].csum)
			} else {
				ok = bytes.Equal(results.checksums[i].sum, results.checksums[i].csum)
			}
			if ok {
				if !opts.quiet && !opts.status {
					if opts.verbose {
						fmt.Printf("%s: %s OK\n", file, algorithms[results.checksums[i].hash].name)
					} else {
						fmt.Printf("%s: OK\n", file)
					}
				}
			} else {
				stats.unmatched++
				if !opts.status {
					if opts.verbose {
						fmt.Printf("%s: %s FAILED with %s\n", file, algorithms[results.checksums[i].hash].name, hex.EncodeToString(results.checksums[i].sum))
					} else {
						fmt.Printf("%s: FAILED\n", file)
					}
				}
			}
		}
	} else {
		if err := format.Execute(os.Stdout, getOutput(results)); err != nil {
			logger.Print(err)
		}
	}
}

func init() {
	logger = log.New(os.Stderr, "ERROR: ", 0)

	progname := filepath.Base(os.Args[0])

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] [-s STRING...]|[-c FILE]|[-i FILE]|[FILE...]|[-r FILE... DIRECTORY...]\n", progname)
		flag.PrintDefaults()
	}

	if strings.HasPrefix(progname, "xhash") {
		flag.BoolVarP(&opts.all, "all", "a", false, "all algorithms (except others specified, if any)")
	}
	if strings.Contains(progname, "sum") {
		flag.BoolVarP(&opts.dummy, "binary", "b", false, "read in binary mode")
		flag.BoolVarP(&opts.dummy, "text", "t", false, "read in text mode (default)")
		flag.BoolVarP(&opts.tag, "tag", "", false, "create a BSD-style checksum")
	} else {
		flag.BoolVarP(&opts.gnu, "gnu", "", false, "output hashes in the format used by md5sum")
	}
	flag.BoolVarP(&opts.ignore, "ignore-missing", "", false, "don't fail or report status for missing files")
	flag.BoolVarP(&opts.quiet, "quiet", "q", false, "don't print OK for each successfully verified file")
	flag.BoolVarP(&opts.mmap, "mmap", "M", false, "use mmap - EXPERIMENTAL")
	flag.BoolVarP(&opts.recursive, "recursive", "r", false, "recurse into directories")
	flag.BoolVarP(&opts.size, "size", "", false, "output size")
	flag.BoolVarP(&opts.status, "status", "S", false, "don't output anything, status code shows success")
	flag.BoolVarP(&opts.strict, "strict", "", false, "exit non-zero for improperly formatted checksum lines")
	flag.BoolVarP(&opts.str, "string", "s", false, "treat arguments as strings")
	flag.BoolVarP(&opts.symlinks, "symlinks", "L", false, "don't follow symbolic links with the -r option")
	flag.BoolVarP(&opts.verbose, "verbose", "v", false, "verbose operation")
	flag.BoolVarP(&opts.version, "version", "", false, "show version and exit")
	flag.BoolVarP(&opts.warn, "warn", "w", false, "warn about improperly formatted checksum lines")
	flag.BoolVarP(&opts.zero, "zero", "z", false, "consider NUL instead as EOL and disable file name escaping")
	flag.StringVarP(&opts.check, "check", "c", "\x00", "read checksums from file (use \"\" for stdin)")
	flag.StringVarP(&opts.input, "input", "i", "\x00", "read pathnames from file (use \"\" for stdin)")
	flag.StringVarP(&opts.key, "hmac", "H", "\x00", "key for HMAC (in hexadecimal) or read from specified pathname")
	if strings.Contains(progname, "sum") {
		flag.StringVarP(&opts.format, "format", "f", gnuFormat, "output format")
	} else {
		flag.StringVarP(&opts.format, "format", "f", bsdFormat, "output format")
	}

	// Allow xhash to be hard-linked to "md5sum" or "md5", etc and only use that algorithm
	if !strings.HasPrefix(progname, "xhash") {
		cmd := strings.TrimSuffix(strings.TrimSuffix(progname, ".exe"), "sum")
		var defaults = map[string]crypto.Hash{
			"b2":     crypto.BLAKE2b_512,
			"md5":    crypto.MD5,
			"sha1":   crypto.SHA1,
			"sha224": crypto.SHA224,
			"sha256": crypto.SHA256,
			"sha384": crypto.SHA384,
			"sha512": crypto.SHA512,
		}
		if h, ok := defaults[cmd]; ok {
			hashes = []crypto.Hash{h}
		}
	}

	max := 0
	for _, h := range hashes {
		if int(h) > max {
			max = int(h)
		}
	}

	algorithms = make(map[crypto.Hash]*Algorithm)
	for _, h := range hashes {
		if h.Available() {
			algorithms[h] = &Algorithm{
				name: strings.ReplaceAll(strings.ReplaceAll(h.String(), "SHA-", "SHA"), "/", "-"),
			}
			if strings.HasPrefix(progname, "xhash") {
				flag.BoolVar(
					&algorithms[h].check,
					strings.ToLower(algorithms[h].name),
					false,
					h.String()+" algorithm")
			}
		}
	}
	flag.Parse()

	if opts.input != "\x00" && opts.check != "\x00" {
		logger.Fatal("The --input & --check options are mutually exclusive")
	}

	if opts.version {
		fmt.Printf("%s\t%v %s/%s\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
		fmt.Printf("Supported hashes:")
		for _, h := range hashes {
			fmt.Printf(" %s", algorithms[h].name)
		}
		fmt.Println()
		os.Exit(0)
	}

	if strings.HasPrefix(progname, "xhash") {
		if opts.all {
			// Ignore algorithm if --all was specified
			for h := range algorithms {
				algorithms[h].check = !algorithms[h].check
			}
		}

		// Initialize chosen and populate name2Hash
		for _, h := range hashes {
			if h.Available() && algorithms[h].check {
				chosen = append(chosen, &Checksum{hash: h})
			}
			name2Hash[algorithms[h].name] = h
		}

		if opts.check == "\x00" && len(chosen) == 0 {
			// SHA-256 is default
			chosen = append(chosen, &Checksum{hash: crypto.SHA256})
		}
	} else {
		chosen = []*Checksum{&Checksum{hash: hashes[0]}}
		name2Hash[algorithms[hashes[0]].name] = hashes[0]
	}

	if opts.key != "\x00" {
		var err error
		if opts.key == "" {
			if macKey, err = io.ReadAll(os.Stdin); err != nil {
				logger.Fatal(err)
			}
		} else if macKey, err = hex.DecodeString(opts.key); err != nil {
			if macKey, err = os.ReadFile(opts.key); err != nil {
				logger.Fatal(err)
			}
		}
	}

	if opts.gnu {
		opts.format = gnuFormat
	} else if opts.tag {
		opts.format = bsdFormat
	}
	opts.format = unescape(opts.format)
	if opts.zero && !strings.Contains(opts.format, "\x00") {
		opts.format = strings.ReplaceAll(opts.format, "\n", "\x00")
	}
	var err error
	format, err = format.Parse(opts.format)
	if err != nil {
		logger.Fatal(err)
	}

	if strings.Contains(progname, "sum") {
		opts.gnu = true
	}
}

func main() {
	inputFrom := inputFromArgs
	if opts.check != "\x00" {
		inputFrom = inputFromCheck
	} else if opts.input != "\x00" {
		inputFrom = inputFromFile
	} else if flag.NArg() == 0 {
		display(hashStdin(nil))
		os.Exit(0)
	} else if opts.recursive {
		inputFrom = inputFromDir
	} else if opts.str {
		for _, s := range flag.Args() {
			display(hashString(s))
		}
		os.Exit(0)
	}

	var wg sync.WaitGroup
	channel := make(chan *Checksums)

	go func() {
		for input := range inputFrom(nil) {
			wg.Add(1)
			go func(input *Checksums) {
				defer wg.Done()
				channel <- hashFile(input)
			}(input)

		}
		wg.Wait()
		close(channel)
	}()

	for checksum := range channel {
		if checksum != nil {
			display(checksum)
		}
	}

	if opts.check != "\x00" || opts.input != "\x00" {
		plural := ""
		if !opts.status && stats.unreadable > 0 {
			if stats.unreadable > 1 {
				plural = "s"
			}
			fmt.Fprintf(os.Stderr, "WARNING: %d listed file%s could not be read\n", stats.unreadable, plural)
		}
		if !opts.status && stats.unmatched > 0 {
			if stats.unmatched > 1 {
				plural = "s"
			}
			fmt.Fprintf(os.Stderr, "WARNING: %d computed checksum%s did NOT match\n", stats.unmatched, plural)
		}
	}
	if stats.unreadable > 0 || stats.unmatched > 0 || stats.invalid > 0 {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"encoding/xml"
	"io"
	"log"
	"mellium.im/sasl"
	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/dial"
	"mellium.im/xmpp/jid"
	"mellium.im/xmpp/stanza"
	"net/http"
	"os"
	"strings"
)

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

type MessageBody struct {
	stanza.Message
	Body string `xml:"body"`
}

func initXMPP(address jid.JID, pass string, skipTLSVerify bool, legacyTLS bool, forceStartTLS bool) (*xmpp.Session, error) {
	tlsConfig := tls.Config{InsecureSkipVerify: skipTLSVerify}
	var dialer dial.Dialer
	// only use the tls config for the dialer if necessary
	if skipTLSVerify {
		dialer = dial.Dialer{NoTLS: !legacyTLS, TLSConfig: &tlsConfig}
	} else {
		dialer = dial.Dialer{NoTLS: !legacyTLS}
	}
	conn, err := dialer.Dial(context.TODO(), "tcp", address)
	if err != nil {
		return nil, err
	}
	// we need the domain in the tls config if we want to verify the cert
	if !skipTLSVerify {
		tlsConfig.ServerName = address.Domainpart()
	}
	return xmpp.NegotiateSession(
		context.TODO(),
		address.Domain(),
		address,
		conn,
		false,
		xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{
			xmpp.BindResource(),
			xmpp.StartTLS(forceStartTLS, &tlsConfig),
			xmpp.SASL("", pass, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
		}}),
	)
}

func closeXMPP(session *xmpp.Session) {
	session.Close()
	session.Conn().Close()
}

func main() {
	// get xmpp credentials, message receivers
	xi := os.Getenv("XMPP_ID")
	xp := os.Getenv("XMPP_PASS")
	xr := os.Getenv("XMPP_RECEIVERS")

	// get tls settings from env
	_, skipTLSVerify := os.LookupEnv("XMPP_SKIP_VERIFY")
	_, legacyTLS := os.LookupEnv("XMPP_OVER_TLS")
	_, forceStartTLS := os.LookupEnv("XMPP_FORCE_STARTTLS")

	// check if xmpp credentials and receiver list are supplied
	if xi == "" || xp == "" || xr == "" {
		log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
	}

	address, err := jid.Parse(xi)
	panicOnErr(err)

	// connect to xmpp server
	xmppSession, err := initXMPP(address, xp, skipTLSVerify, legacyTLS, forceStartTLS)
	panicOnErr(err)
	defer closeXMPP(xmppSession)

	// send initial presence
	panicOnErr(xmppSession.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil)))

	// listen for messages and echo them
	go func() {
		err = xmppSession.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			d := xml.NewTokenDecoder(t)
			// ignore elements that aren't messages
			if start.Name.Local != "message" {
				return nil
			}

			// parse message into struct
			msg := MessageBody{}
			err = d.DecodeElement(&msg, start)
			if err != nil && err != io.EOF {
				return nil
			}

			// ignore empty messages and stanzas that aren't messages
			if msg.Body == "" || msg.Type != stanza.ChatMessage {
				return nil
			}

			// create reply with identical contents
			reply := MessageBody{
				Message: stanza.Message{
					To: msg.From.Bare(),
				},
				Body: msg.Body,
			}

			// try to send reply, ignore errors
			_ = t.Encode(reply)
			return nil
		}))
		panicOnErr(err)
	}()

	// create chan for messages (webhooks -> xmpp)
	messages := make(chan string)

	// wait for messages from the webhooks and send them to all receivers
	go func() {
		for m := range messages {
			for _, r := range strings.Split(xr, ",") {
				recipient, err := jid.Parse(r)
				panicOnErr(err)
				// try to send message, ignore errors
				_ = xmppSession.Encode(MessageBody{
					Message: stanza.Message{
						To: recipient,
					},
					Body: m,
				})
			}
		}
	}()

	// initialize handler for grafana alerts
	http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))

	// listen for requests
	http.ListenAndServe(":4321", nil)
}
<commit_msg>Minor refactoring<commit_after>package main

import (
	"context"
	"crypto/tls"
	"encoding/xml"
	"io"
	"log"
	"mellium.im/sasl"
	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/dial"
	"mellium.im/xmpp/jid"
	"mellium.im/xmpp/stanza"
	"net/http"
	"os"
	"strings"
)

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

type MessageBody struct {
	stanza.Message
	Body string `xml:"body"`
}

func initXMPP(address jid.JID, pass string, skipTLSVerify bool, useXMPPS bool) (*xmpp.Session, error) {
	tlsConfig := tls.Config{InsecureSkipVerify: skipTLSVerify}
	var dialer dial.Dialer
	// only use the tls config for the dialer if necessary
	if skipTLSVerify {
		dialer = dial.Dialer{NoTLS: !useXMPPS, TLSConfig: &tlsConfig}
	} else {
		dialer = dial.Dialer{NoTLS: !useXMPPS}
	}
	conn, err := dialer.Dial(context.TODO(), "tcp", address)
	if err != nil {
		return nil, err
	}
	// we need the domain in the tls config if we want to verify the cert
	if !skipTLSVerify {
		tlsConfig.ServerName = address.Domainpart()
	}
	return xmpp.NegotiateSession(
		context.TODO(),
		address.Domain(),
		address,
		conn,
		false,
		xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{
			xmpp.BindResource(),
			xmpp.StartTLS(false, &tlsConfig),
			xmpp.SASL("", pass, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
		}}),
	)
}

func closeXMPP(session *xmpp.Session) {
	session.Close()
	session.Conn().Close()
}

func main() {
	// get xmpp credentials, message receivers
	xi := os.Getenv("XMPP_ID")
	xp := os.Getenv("XMPP_PASS")
	xr := os.Getenv("XMPP_RECEIVERS")

	// get tls settings from env
	_, skipTLSVerify := os.LookupEnv("XMPP_SKIP_VERIFY")
	_, useXMPPS := os.LookupEnv("XMPP_OVER_TLS")

	// check if xmpp credentials and receiver list are supplied
	if xi == "" || xp == "" || xr == "" {
		log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
	}

	address, err := jid.Parse(xi)
	panicOnErr(err)

	// connect to xmpp server
	xmppSession, err := initXMPP(address, xp, skipTLSVerify, useXMPPS)
	panicOnErr(err)
	defer closeXMPP(xmppSession)

	// send initial presence
	panicOnErr(xmppSession.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil)))

	// listen for messages and echo them
	go func() {
		err = xmppSession.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			d := xml.NewTokenDecoder(t)
			// ignore elements that aren't messages
			if start.Name.Local != "message" {
				return nil
			}

			// parse message into struct
			msg := MessageBody{}
			err = d.DecodeElement(&msg, start)
			if err != nil && err != io.EOF {
				return nil
			}

			// ignore empty messages and stanzas that aren't messages
			if msg.Body == "" || msg.Type != stanza.ChatMessage {
				return nil
			}

			// create reply with identical contents
			reply := MessageBody{
				Message: stanza.Message{
					To: msg.From.Bare(),
				},
				Body: msg.Body,
			}

			// try to send reply, ignore errors
			_ = t.Encode(reply)
			return nil
		}))
		panicOnErr(err)
	}()

	// create chan for messages (webhooks -> xmpp)
	messages := make(chan string)

	// wait for messages from the webhooks and send them to all receivers
	go func() {
		for m := range messages {
			for _, r := range strings.Split(xr, ",") {
				recipient, err := jid.Parse(r)
				panicOnErr(err)
				// try to send message, ignore errors
				_ = xmppSession.Encode(MessageBody{
					Message: stanza.Message{
						To: recipient,
					},
					Body: m,
				})
			}
		}
	}()

	// initialize handler for grafana alerts
	http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))

	// listen for requests
	http.ListenAndServe(":4321", nil)
}
<|endoftext|>


<commit_before>// Package main defines a command line interface for the sqlboiler package
package main

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

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cmdState  *State
	cmdConfig *Config
)

func main() {
	var err error

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Find and read config
	err = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates boilerplate structs and statements",
		Long: "SQL Boiler generates boilerplate structs and statements from template files.\n" +
			`Complete documentation is available at http://github.com/nullbio/sqlboiler`,
		Example:  `sqlboiler -o models -p models postgres`,
		PreRunE:  preRun,
		RunE:     run,
		PostRunE: postRun,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringSliceP("tables", "t", nil, "Tables to generate models for, all tables if empty")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")

	viper.BindPFlags(rootCmd.PersistentFlags())

	if err := rootCmd.Execute(); err != nil {
		fmt.Printf("\n%+v\n", err)
		os.Exit(1)
	}
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return errors.New("must provide a driver name")
	}

	cmdConfig = &Config{
		DriverName: args[0],
		OutFolder:  viper.GetString("output"),
		PkgName:    viper.GetString("pkgname"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of TableNames directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.TableNames = viper.GetStringSlice("tables")
	if len(cmdConfig.TableNames) == 1 && strings.HasPrefix(cmdConfig.TableNames[0], "[") {
		cmdConfig.TableNames, err = cmd.PersistentFlags().GetStringSlice("tables")
		if err != nil {
			return err
		}
	}

	if len(cmdConfig.DriverName) == 0 {
		return errors.New("Must supply a driver flag.")
	}
	if len(cmdConfig.OutFolder) == 0 {
		return fmt.Errorf("No output folder specified.")
	}

	if viper.IsSet("postgres.dbname") {
		cmdConfig.Postgres = PostgresConfig{
			User:   viper.GetString("postgres.user"),
			Pass:   viper.GetString("postgres.pass"),
			Host:   viper.GetString("postgres.host"),
			Port:   viper.GetInt("postgres.port"),
			DBName: viper.GetString("postgres.dbname"),
		}
	}

	cmdState, err = New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<commit_msg>Improve command error reporting & validation<commit_after>// Package main defines a command line interface for the sqlboiler package
package main

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

	"github.com/kat-co/vala"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cmdState  *State
	cmdConfig *Config
)

func main() {
	var err error

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Find and read config, ignore errors because we'll fall back to defaults
	// and other validation mechanisms
	_ = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates boilerplate structs and statements",
		Long: "SQL Boiler generates boilerplate structs and statements from template files.\n" +
			`Complete documentation is available at http://github.com/nullbio/sqlboiler`,
		Example:       `sqlboiler -o models -p models postgres`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringSliceP("tables", "t", nil, "Tables to generate models for, all tables if empty")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")

	viper.BindPFlags(rootCmd.PersistentFlags())

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			rootCmd.Help()
			fmt.Printf("\n%s\n", string(e))
			return
		}

		fmt.Printf("\n%+v\n", err)
		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	cmdConfig = &Config{
		DriverName: args[0],
		OutFolder:  viper.GetString("output"),
		PkgName:    viper.GetString("pkgname"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of TableNames directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.TableNames = viper.GetStringSlice("tables")
	if len(cmdConfig.TableNames) == 1 && strings.HasPrefix(cmdConfig.TableNames[0], "[") {
		cmdConfig.TableNames, err = cmd.PersistentFlags().GetStringSlice("tables")
		if err != nil {
			return err
		}
	}

	if viper.IsSet("postgres.dbname") {
		cmdConfig.Postgres = PostgresConfig{
			User:   viper.GetString("postgres.user"),
			Pass:   viper.GetString("postgres.pass"),
			Host:   viper.GetString("postgres.host"),
			Port:   viper.GetInt("postgres.port"),
			DBName: viper.GetString("postgres.dbname"),
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"),
			vala.StringNotEmpty(cmdConfig.Postgres.Pass, "postgres.pass"),
			vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"),
			vala.Not(vala.Equals(cmdConfig.Postgres.Port, 0, "postgres.port")),
			vala.StringNotEmpty(cmdConfig.Postgres.DBName, "postgres.dbname"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	cmdState, err = New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/elgs/gojq"
	homedir "github.com/mitchellh/go-homedir"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
)

var (
	versionNumber = "0.1"

	verbose = kingpin.Flag("verbose", "Verbose mode").Short('v').Bool()

	dbFile string

	request   Request
	noHeaders bool
	noQueries bool
	filter    string
)

func init() {

	dir, err := homedir.Dir()
	if err != nil {
		panic(err)
	}
	dbFile = fmt.Sprintf("%s/%s", dir, ".rest.db")

	kingpin.Flag("db", "which config database to use").Default(dbFile).StringVar(&dbFile)
}

func main() {
	command := kingpin.Parse()

	switch command {
	case "version":
		fmt.Println(versionNumber)
	case "service init":
		if err := initService(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service set":
		if err := setValue(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "service unset":
		if err := unsetValue(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service use":
		if err := useService(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service config":
		if err := displayConfig(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "get", "post", "put", "delete":
		request.Method = command
		resp, err := makeRequest()
		if err != nil {
			fmt.Println("error making request:", err)
			os.Exit(1)
		}

		if *verbose {
			fmt.Println(resp.Status)
		}

		if err := showRequest(resp); err != nil {
			fmt.Println("error displaying result:", err)
			os.Exit(1)
		}

		// exit non zero if not a 200 response
		if resp.StatusCode < 200 || resp.StatusCode > 300 {
			// if the exit value gets too high it gets mangled
			// so only keep the hundreds
			os.Exit(resp.StatusCode / 100)
		}
	}
}

func useService() error {
	db, err := bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	err = db.Update(func(tx *bolt.Tx) error {
		serviceBucket := tx.Bucket([]byte("services"))
		if serviceBucket == nil {
			return ErrNoServicesBucket
		}

		if b := serviceBucket.Bucket([]byte(request.Service)); b == nil {
			return ErrNoService{Name: request.Service}
		}

		info, err := tx.CreateBucketIfNotExists([]byte("info"))
		if err != nil {
			return err
		}

		err = info.Put([]byte("current"), []byte(request.Service))
		if err != nil {
			return err
		}

		return nil
	})

	return err
}

func getValues() error {
	db, err := bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		b, err := getServiceBucket(tx)
		if err != nil {
			return err
		}

		pb := getDefinedPath(b)
		var rb *bolt.Bucket
		if pb != nil {
			rb = pb.Bucket([]byte(request.Method))
		}
		// current holds the current command line flags
		current := settings

		// load settings from db
		settings = LoadSettings(b)

		switch {
		// load request type specific settings
		// this has to merge the path specific settings first
		case rb != nil:
			s := LoadSettings(pb)
			settings.Merge(s)
			s = LoadSettings(rb)
			settings.Merge(s)
		// load path specific settings
		case pb != nil:
			s := LoadSettings(pb)
			settings.Merge(s)
		}

		// apply the current cli flags
		settings.Merge(current)

		return nil
	})

	return err
}

func getServiceBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	info := tx.Bucket([]byte("info"))
	if info == nil {
		return nil, ErrNoInfoBucket
	}

	current := info.Get([]byte("current"))
	if current == nil {
		return nil, ErrNoServiceSet
	}

	serviceBucket := tx.Bucket([]byte("services"))
	if serviceBucket == nil {
		return nil, ErrNoServicesBucket
	}

	b := serviceBucket.Bucket(current)
	if b == nil {
		return nil, ErrNoService{Name: string(current)}
	}

	return b, nil
}

func getDefinedPath(b *bolt.Bucket) *bolt.Bucket {
	pb := b.Bucket([]byte("paths"))
	if pb == nil {
		return nil
	}

	c := pb.Cursor()
	for key, _ := c.First(); key != nil; key, _ = c.Next() {
		// TODO: improve matching
		if string(key) == request.Path {
			return pb.Bucket(key)
		}
	}

	return nil
}

func makeRequest() (*http.Response, error) {
	err := getValues()
	if err != nil {
		return nil, err
	}

	u := settings.URL()

	params := paramReplacer(settings.Parameters)
	u.Path = path.Join(settings.BasePath.String, params.Replace(request.Path))
	request.Data = params.Replace(request.Data)

	if !noQueries {
		q := u.Query()
		for key, value := range settings.Queries {
			q.Set(key, value)
		}
		u.RawQuery = q.Encode()
	}

	if *verbose {
		fmt.Println(u)
	}

	client := &http.Client{}

	req, err := http.NewRequest(strings.ToUpper(request.Method), u.String(), strings.NewReader(request.Data))
	if err != nil {
		return nil, err
	}

	if !noHeaders {
		for key, value := range settings.Headers {
			req.Header.Set(key, value)
		}
	}

	return client.Do(req)
}

func usedFlag(b *bool) func(*kingpin.ParseContext) error {
	return func(*kingpin.ParseContext) error {
		*b = true
		return nil
	}
}

func showRequest(r *http.Response) error {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	switch {
	case filter != "":
		result, err := filterResult(body)
		if err != nil {
			return err
		}
		if err := printJSON(result); err != nil {
			return err
		}
	case settings.Pretty.Bool:
		var msg json.RawMessage
		if err := json.Unmarshal(body, &msg); err != nil {
			return err
		}
		if err := printJSON(msg); err != nil {
			return err
		}
	default:
		fmt.Println(string(body))
	}

	return nil
}

func paramReplacer(parameters map[string]string) *strings.Replacer {
	rep := make([]string, 0, len(parameters))
	for key, value := range parameters {
		rep = append(rep, ":"+key)
		rep = append(rep, value)
	}
	return strings.NewReplacer(rep...)
}

func filterResult(body []byte) (interface{}, error) {
	parser, err := gojq.NewStringQuery(string(body))
	if err != nil {
		return nil, err
	}

	return parser.Query(filter)
}

func printJSON(v interface{}) error {
	var out []byte
	var err error
	if settings.Pretty.Bool {
		out, err = json.MarshalIndent(v, "", settings.PrettyIndent.String)
	} else {
		out, err = json.Marshal(v)
	}
	if err != nil {
		return err
	}
	fmt.Println(string(out))
	return nil
}
<commit_msg>Remove obsolete function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/elgs/gojq"
	homedir "github.com/mitchellh/go-homedir"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
)

var (
	versionNumber = "0.1"

	verbose = kingpin.Flag("verbose", "Verbose mode").Short('v').Bool()

	dbFile string

	request   Request
	noHeaders bool
	noQueries bool
	filter    string
)

func init() {

	dir, err := homedir.Dir()
	if err != nil {
		panic(err)
	}
	dbFile = fmt.Sprintf("%s/%s", dir, ".rest.db")

	kingpin.Flag("db", "which config database to use").Default(dbFile).StringVar(&dbFile)
}

func main() {
	command := kingpin.Parse()

	switch command {
	case "version":
		fmt.Println(versionNumber)
	case "service init":
		if err := initService(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service set":
		if err := setValue(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "service unset":
		if err := unsetValue(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service use":
		if err := useService(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "service config":
		if err := displayConfig(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "get", "post", "put", "delete":
		request.Method = command
		resp, err := makeRequest()
		if err != nil {
			fmt.Println("error making request:", err)
			os.Exit(1)
		}

		if *verbose {
			fmt.Println(resp.Status)
		}

		if err := showRequest(resp); err != nil {
			fmt.Println("error displaying result:", err)
			os.Exit(1)
		}

		// exit non zero if not a 200 response
		if resp.StatusCode < 200 || resp.StatusCode > 300 {
			// if the exit value gets too high it gets mangled
			// so only keep the hundreds
			os.Exit(resp.StatusCode / 100)
		}
	}
}

func useService() error {
	db, err := bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	err = db.Update(func(tx *bolt.Tx) error {
		serviceBucket := tx.Bucket([]byte("services"))
		if serviceBucket == nil {
			return ErrNoServicesBucket
		}

		if b := serviceBucket.Bucket([]byte(request.Service)); b == nil {
			return ErrNoService{Name: request.Service}
		}

		info, err := tx.CreateBucketIfNotExists([]byte("info"))
		if err != nil {
			return err
		}

		err = info.Put([]byte("current"), []byte(request.Service))
		if err != nil {
			return err
		}

		return nil
	})

	return err
}

func getValues() error {
	db, err := bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		b, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		pb := getDefinedPath(b)
		var rb *bolt.Bucket
		if pb != nil {
			rb = pb.Bucket([]byte(request.Method))
		}
		// current holds the current command line flags
		current := settings

		// load settings from db
		settings = LoadSettings(b)

		switch {
		// load request type specific settings
		// this has to merge the path specific settings first
		case rb != nil:
			s := LoadSettings(pb)
			settings.Merge(s)
			s = LoadSettings(rb)
			settings.Merge(s)
		// load path specific settings
		case pb != nil:
			s := LoadSettings(pb)
			settings.Merge(s)
		}

		// apply the current cli flags
		settings.Merge(current)

		return nil
	})

	return err
}

func getDefinedPath(b *bolt.Bucket) *bolt.Bucket {
	pb := b.Bucket([]byte("paths"))
	if pb == nil {
		return nil
	}

	c := pb.Cursor()
	for key, _ := c.First(); key != nil; key, _ = c.Next() {
		// TODO: improve matching
		if string(key) == request.Path {
			return pb.Bucket(key)
		}
	}

	return nil
}

func makeRequest() (*http.Response, error) {
	err := getValues()
	if err != nil {
		return nil, err
	}

	u := settings.URL()

	params := paramReplacer(settings.Parameters)
	u.Path = path.Join(settings.BasePath.String, params.Replace(request.Path))
	request.Data = params.Replace(request.Data)

	if !noQueries {
		q := u.Query()
		for key, value := range settings.Queries {
			q.Set(key, value)
		}
		u.RawQuery = q.Encode()
	}

	if *verbose {
		fmt.Println(u)
	}

	client := &http.Client{}

	req, err := http.NewRequest(strings.ToUpper(request.Method), u.String(), strings.NewReader(request.Data))
	if err != nil {
		return nil, err
	}

	if !noHeaders {
		for key, value := range settings.Headers {
			req.Header.Set(key, value)
		}
	}

	return client.Do(req)
}

func usedFlag(b *bool) func(*kingpin.ParseContext) error {
	return func(*kingpin.ParseContext) error {
		*b = true
		return nil
	}
}

func showRequest(r *http.Response) error {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	switch {
	case filter != "":
		result, err := filterResult(body)
		if err != nil {
			return err
		}
		if err := printJSON(result); err != nil {
			return err
		}
	case settings.Pretty.Bool:
		var msg json.RawMessage
		if err := json.Unmarshal(body, &msg); err != nil {
			return err
		}
		if err := printJSON(msg); err != nil {
			return err
		}
	default:
		fmt.Println(string(body))
	}

	return nil
}

func paramReplacer(parameters map[string]string) *strings.Replacer {
	rep := make([]string, 0, len(parameters))
	for key, value := range parameters {
		rep = append(rep, ":"+key)
		rep = append(rep, value)
	}
	return strings.NewReplacer(rep...)
}

func filterResult(body []byte) (interface{}, error) {
	parser, err := gojq.NewStringQuery(string(body))
	if err != nil {
		return nil, err
	}

	return parser.Query(filter)
}

func printJSON(v interface{}) error {
	var out []byte
	var err error
	if settings.Pretty.Bool {
		out, err = json.MarshalIndent(v, "", settings.PrettyIndent.String)
	} else {
		out, err = json.Marshal(v)
	}
	if err != nil {
		return err
	}
	fmt.Println(string(out))
	return nil
}
<|endoftext|>


<commit_before>// Command cache-pkgs caches pacakge directories based on the hash of
// dependency specification file. Unix only.
//
//     Usage:
//        cache-pkgs [opts] <dep-spec-file> <dir> <cmd> [args..]
//
//     Caches output directory (dir) based on the hash of the dependency
//     specification file. If the specification changes the output directory
//     is regenerated using cmd and the args. Useful in CI settings.
//
//     Example:
//        cache-pkgs package.json node_modules npm install
//
//     Options can be:
//       -clean
//         	Clean cache and exit
//       -f	Force remove existing output directory
//       -symlink
//         	Use a symlink instead of copy (default true)
package main

import (
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"time"
)

var (
	symlink = flag.Bool("symlink", true, "Use a symlink instead of copy")
	force   = flag.Bool("f", false, "Force remove existing output directory")
	clean   = flag.Bool("clean", false, "Clean cache and exit")
)

func usage() {
	usageStr := `Usage:
   %s [opts] <dep-spec-file> <dir> <cmd> [args..]

Caches output directory (dir) based on the hash of the dependency
specification file. If the specification changes the output directory
is regenerated using cmd and the args. Useful in CI settings.

Example:
   %s package.json node_modules npm install

Options can be:
`
	me := filepath.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, usageStr, me, me)
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() < 3 {
		exitUsage("please supply both dependency description file, outputdir and the command to generate it")
	}

	cacheStore, err := cacheDir("")
	if err != nil {
		exitWith("Cache dir problems: ", err)
	}

	if *clean {
		fmt.Printf("Wiping cache %q\n", cacheStore)
		err := os.RemoveAll(cacheStore)
		if err != nil {
			exitWith(err)
		}
		return
	}

	depDesc := flag.Arg(0)
	outputdir := flag.Arg(1)
	cmd := flag.Args()[2]
	args := flag.Args()[3:]

	h, err := hashFile(depDesc)
	if err != nil {
		exitWith("Can't hash dependency description:", err)
	}

	depDir := path.Join(cacheStore, h)

	// pre build
	if *force {
		err := os.RemoveAll(outputdir)
		if err != nil && err != os.ErrNotExist {
			exitWith("Error trying to remove existing output dir", err)
		}
	} else {
		_, err := os.Stat(outputdir)
		if !os.IsNotExist(err) {
			exitWith("output path '", outputdir, "' already exists - maybe rerun with `-f`")
		}
	}

	cached, err := IsDir(depDir)
	if err != nil {
		exitWith("Error looking up cache dir", err)
	}

	// build
	start := time.Now()
	if cached {
		Progress("Found cached dependencies - installing those")
		err = Install(depDir, outputdir, *symlink)
	} else {
		Progressf("Running `%s %s` and caching the output", cmd, strings.Join(args, " "))
		err = GenerateAndCache(depDir, outputdir, cmd, args)
	}

	if err != nil {
		exitWith(err)
	}

	Progressf("Succeeded in %.2f sec", time.Now().Sub(start).Seconds())
}

func Install(from, to string, link bool) (err error) {
	from, err = filepath.Abs(from)
	if err != nil {
		return err
	}
	to, err = filepath.Abs(to)
	if err != nil {
		return err
	}

	if link {
		// to is a symlink to from
		err = os.Symlink(from, to)
	} else {
		err = Copy(from, to)
	}
	return err
}

func IsDir(d string) (bool, error) {
	info, err := os.Stat(d)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	return info.IsDir(), nil

}

func run(bin string, args ...string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	return cmd.Run()
}

func GenerateAndCache(cache, outputdir, cmd string, args []string) error {
	err := run(cmd, args...)
	if err != nil {
		return err
	}
	return Copy(outputdir, cache)
}

//
func Copy(a, b string) error {
	return run("cp", "-R", a, b)
}

func exitUsage(a ...interface{}) {
	flag.Usage()
	exitWith(a...)
}
func exitWith(a ...interface{}) {
	fmt.Fprint(os.Stderr, append([]interface{}{"Error: "}, append(a, "\n")...)...)
	os.Exit(1)
}

func hashFile(fname string) (hash string, err error) {
	h := sha1.New()
	f, err := os.Open(fname)
	if err != nil {
		return "", err
	}
	defer f.Close()

	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func ensureDir(dir string) error {

	info, err := os.Stat(dir)
	if os.IsNotExist(err) {
		Progress("creating cahce dir", dir)
		return os.MkdirAll(dir, 0750)
	}
	if err != nil {
		return err
	}
	if !info.IsDir() {
		return errors.New(dir + " exists but is not a dir")
	}
	return nil
}

func cacheDir(dirName string) (dir string, err error) {

	if dirName == "" {
		dir = dirName
	}

	if dir == "" {
		dir = os.Getenv("CACHE_DIR")
	}

	if dir == "" {
		u, err := user.Current()
		if err != nil {
			return "", err
		}

		dir = path.Join(u.HomeDir, ".dep-cache")
	}

	err = ensureDir(dir)
	if err != nil {
		return "", err
	}
	return dir, nil
}

func Progressf(format string, a ...interface{}) {
	ProgressPrint(fmt.Sprintf(format, a...))
}

func Progress(a ...interface{}) {
	ProgressPrint(fmt.Sprint(a...))
}

func ProgressPrint(s string) {
	fmt.Fprintf(os.Stderr, "==> %s\n", s)
}
<commit_msg>allow cleaning without further args<commit_after>// Command cache-pkgs caches pacakge directories based on the hash of
// dependency specification file. Unix only.
//
//     Usage:
//        cache-pkgs [opts] <dep-spec-file> <dir> <cmd> [args..]
//
//     Caches output directory (dir) based on the hash of the dependency
//     specification file. If the specification changes the output directory
//     is regenerated using cmd and the args. Useful in CI settings.
//
//     Example:
//        cache-pkgs package.json node_modules npm install
//
//     Options can be:
//       -clean
//         	Clean cache and exit
//       -f	Force remove existing output directory
//       -symlink
//         	Use a symlink instead of copy (default true)
package main

import (
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"time"
)

var (
	symlink = flag.Bool("symlink", true, "Use a symlink instead of copy")
	force   = flag.Bool("f", false, "Force remove existing output directory")
	clean   = flag.Bool("clean", false, "Clean cache and exit")
)

func usage() {
	usageStr := `Usage:
   %s [opts] <dep-spec-file> <dir> <cmd> [args..]

Caches output directory (dir) based on the hash of the dependency
specification file. If the specification changes the output directory
is regenerated using cmd and the args. Useful in CI settings.

Example:
   %s package.json node_modules npm install

Options can be:
`
	me := filepath.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, usageStr, me, me)
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	cacheStore, err := cacheDir("")
	if err != nil {
		exitWith("Cache dir problems: ", err)
	}

	if *clean {
		fmt.Printf("Wiping cache %q\n", cacheStore)
		err := os.RemoveAll(cacheStore)
		if err != nil {
			exitWith(err)
		}
		return
	}

	if flag.NArg() < 3 {
		exitUsage("please supply both dependency description file, outputdir and the command to generate it")
	}

	depDesc := flag.Arg(0)
	outputdir := flag.Arg(1)
	cmd := flag.Args()[2]
	args := flag.Args()[3:]

	h, err := hashFile(depDesc)
	if err != nil {
		exitWith("Can't hash dependency description:", err)
	}

	depDir := path.Join(cacheStore, h)

	// pre build
	if *force {
		err := os.RemoveAll(outputdir)
		if err != nil && err != os.ErrNotExist {
			exitWith("Error trying to remove existing output dir", err)
		}
	} else {
		_, err := os.Stat(outputdir)
		if !os.IsNotExist(err) {
			exitWith("output path '", outputdir, "' already exists - maybe rerun with `-f`")
		}
	}

	cached, err := IsDir(depDir)
	if err != nil {
		exitWith("Error looking up cache dir", err)
	}

	// build
	start := time.Now()
	if cached {
		Progress("Found cached dependencies - installing those")
		err = Install(depDir, outputdir, *symlink)
	} else {
		Progressf("Running `%s %s` and caching the output", cmd, strings.Join(args, " "))
		err = GenerateAndCache(depDir, outputdir, cmd, args)
	}

	if err != nil {
		exitWith(err)
	}

	Progressf("Succeeded in %.2f sec", time.Now().Sub(start).Seconds())
}

func Install(from, to string, link bool) (err error) {
	from, err = filepath.Abs(from)
	if err != nil {
		return err
	}
	to, err = filepath.Abs(to)
	if err != nil {
		return err
	}

	if link {
		// to is a symlink to from
		err = os.Symlink(from, to)
	} else {
		err = Copy(from, to)
	}
	return err
}

func IsDir(d string) (bool, error) {
	info, err := os.Stat(d)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	return info.IsDir(), nil

}

func run(bin string, args ...string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	return cmd.Run()
}

func GenerateAndCache(cache, outputdir, cmd string, args []string) error {
	err := run(cmd, args...)
	if err != nil {
		return err
	}
	return Copy(outputdir, cache)
}

//
func Copy(a, b string) error {
	return run("cp", "-R", a, b)
}

func exitUsage(a ...interface{}) {
	flag.Usage()
	exitWith(a...)
}
func exitWith(a ...interface{}) {
	fmt.Fprint(os.Stderr, append([]interface{}{"Error: "}, append(a, "\n")...)...)
	os.Exit(1)
}

func hashFile(fname string) (hash string, err error) {
	h := sha1.New()
	f, err := os.Open(fname)
	if err != nil {
		return "", err
	}
	defer f.Close()

	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func ensureDir(dir string) error {

	info, err := os.Stat(dir)
	if os.IsNotExist(err) {
		Progress("creating cahce dir", dir)
		return os.MkdirAll(dir, 0750)
	}
	if err != nil {
		return err
	}
	if !info.IsDir() {
		return errors.New(dir + " exists but is not a dir")
	}
	return nil
}

func cacheDir(dirName string) (dir string, err error) {

	if dirName == "" {
		dir = dirName
	}

	if dir == "" {
		dir = os.Getenv("CACHE_DIR")
	}

	if dir == "" {
		u, err := user.Current()
		if err != nil {
			return "", err
		}

		dir = path.Join(u.HomeDir, ".dep-cache")
	}

	err = ensureDir(dir)
	if err != nil {
		return "", err
	}
	return dir, nil
}

func Progressf(format string, a ...interface{}) {
	ProgressPrint(fmt.Sprintf(format, a...))
}

func Progress(a ...interface{}) {
	ProgressPrint(fmt.Sprint(a...))
}

func ProgressPrint(s string) {
	fmt.Fprintf(os.Stderr, "==> %s\n", s)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/CiscoCloud/mantl-api/api"
	"github.com/CiscoCloud/mantl-api/install"
	"github.com/CiscoCloud/mantl-api/marathon"
	"github.com/CiscoCloud/mantl-api/mesos"
	"github.com/CiscoCloud/mantl-api/utils/http"
	"github.com/CiscoCloud/mantl-api/zookeeper"
	log "github.com/Sirupsen/logrus"
	consul "github.com/hashicorp/consul/api"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"os"
	"strings"
)

const Name = "mantl-api"
const Version = "0.1.3.1"

func main() {
	rootCmd := &cobra.Command{
		Use:   "mantl-api",
		Short: "runs the mantl-api",
		Run: func(cmd *cobra.Command, args []string) {
			start()
		},
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			readConfigFile()
			setupLogging()
		},
	}

	rootCmd.PersistentFlags().String("log-level", "info", "one of debug, info, warn, error, or fatal")
	rootCmd.PersistentFlags().String("log-format", "text", "specify output (text or json)")
	rootCmd.PersistentFlags().String("consul", "http://localhost:8500", "Consul Api address")
	rootCmd.PersistentFlags().String("marathon", "", "Marathon Api address")
	rootCmd.PersistentFlags().String("marathon-user", "", "Marathon Api user")
	rootCmd.PersistentFlags().String("marathon-password", "", "Marathon Api password")
	rootCmd.PersistentFlags().Bool("marathon-no-verify-ssl", false, "Marathon SSL verification")
	rootCmd.PersistentFlags().String("mesos", "", "Mesos Api address")
	rootCmd.PersistentFlags().String("mesos-principal", "", "Mesos principal for framework authentication")
	rootCmd.PersistentFlags().String("mesos-secret", "", "Mesos secret for framework authentication")
	rootCmd.PersistentFlags().Bool("mesos-no-verify-ssl", false, "Mesos SSL verification")
	rootCmd.PersistentFlags().String("listen", ":4001", "mantl-api listen address")
	rootCmd.PersistentFlags().String("zookeeper", "", "Comma-delimited list of zookeeper servers")
	rootCmd.PersistentFlags().Bool("force-sync", false, "Force a synchronization of all sources")
	rootCmd.PersistentFlags().String("config-file", "", "The path to a configuration file")

	for _, flags := range []*pflag.FlagSet{rootCmd.PersistentFlags()} {
		err := viper.BindPFlags(flags)
		if err != nil {
			log.WithField("error", err).Fatal("could not bind flags")
		}
	}

	viper.SetEnvPrefix("mantl_api")
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()

	syncCommand := &cobra.Command{
		Use:   "sync",
		Short: "Synchronize universe repositories",
		Long:  "Forces a synchronization of all configured sources",
		Run: func(cmd *cobra.Command, args []string) {
			sync(nil, true)
		},
	}
	rootCmd.AddCommand(syncCommand)

	versionCommand := &cobra.Command{
		Use:   "version",
		Short: fmt.Sprintf("Print the version number of %s", Name),
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("%s v%s\n", Name, Version)
		},
	}
	rootCmd.AddCommand(versionCommand)

	rootCmd.Execute()
}

func start() {
	log.Infof("Starting %s v%s", Name, Version)
	client := consulClient()

	marathonUrl := viper.GetString("marathon")
	if marathonUrl == "" {
		marathonUrl = NewDiscovery(client, "marathon", "", "http", marathonUrl).discoveredUrl
	}
	marathonClient, err := marathon.NewMarathon(
		marathonUrl,
		viper.GetString("marathon-user"),
		viper.GetString("marathon-password"),
		viper.GetBool("marathon-no-verify-ssl"),
	)
	if err != nil {
		log.Fatalf("Could not create marathon client: %v", err)
	}

	mesosUrl := viper.GetString("mesos")
	if mesosUrl == "" {
		mesosUrl = NewDiscovery(client, "mesos", "leader", "http", "http://localhost:5050").discoveredUrl
	}
	mesosClient, err := mesos.NewMesos(
		mesosUrl,
		viper.GetString("mesos-principal"),
		viper.GetString("mesos-secret"),
		viper.GetBool("mesos-no-verify-ssl"),
	)
	if err != nil {
		log.Fatalf("Could not create mesos client: %v", err)
	}

	zkUrls := viper.GetString("zookeeper")
	if zkUrls == "" {
		zkUrls = NewDiscovery(client, "zookeeper", "", "", "localhost:2181").discoveredUrl
	}
	zkServers := strings.Split(zkUrls, ",")
	zk := zookeeper.NewZookeeper(zkServers)

	inst, err := install.NewInstall(client, marathonClient, mesosClient, zk)
	if err != nil {
		log.Fatalf("Could not create install client: %v", err)
	}

	// sync sources to consul
	sync(inst, viper.GetBool("force-sync"))

	// start listener
	api.NewApi(viper.GetString("listen"), inst).Start()
}

func consulClient() *consul.Client {
	consulConfig := consul.DefaultConfig()
	scheme, address, err := http.ParseUrl(viper.GetString("consul"))
	if err != nil {
		log.Fatalf("Could not create consul client: %v", err)
	}
	consulConfig.Scheme = scheme
	consulConfig.Address = address

	log.Debugf("Using Consul at %s over %s", consulConfig.Address, consulConfig.Scheme)

	client, err := consul.NewClient(consulConfig)
	if err != nil {
		log.Fatalf("Could not create consul client: %v", err)
	}

	// abort if we cannot connect to consul
	err = testConsul(client)
	if err != nil {
		log.Fatalf("Could not connect to consul: %v", err)
	}

	return client
}

func testConsul(client *consul.Client) error {
	kv := client.KV()
	_, _, err := kv.Get("mantl-install", nil)
	return err
}

func sync(inst *install.Install, force bool) {
	var err error
	if inst == nil {
		client := consulClient()
		inst, err = install.NewInstall(client, nil, nil, nil)
		if err != nil {
			log.Fatalf("Could not create install client: %v", err)
		}
	}

	defaultSources := []*install.Source{
		&install.Source{
			Name:       "mantl",
			Path:       "https://github.com/CiscoCloud/mantl-universe.git",
			SourceType: install.Git,
			Branch:     "master",
			Index:      1,
		},
		&install.Source{
			Name:       "mesosphere",
			Path:       "https://github.com/mesosphere/universe.git",
			SourceType: install.Git,
			Index:      0,
		},
	}

	sources := []*install.Source{}

	configuredSources := viper.GetStringMap("sources")

	if len(configuredSources) > 0 {
		for name, val := range configuredSources {
			source := &install.Source{Name: name, SourceType: install.FileSystem}
			sourceConfig := val.(map[string]interface{})

			if path, ok := sourceConfig["path"].(string); ok {
				source.Path = path
			}

			if index, ok := sourceConfig["index"].(int64); ok {
				source.Index = int(index)
			}

			if sourceType, ok := sourceConfig["type"].(string); ok {
				if strings.EqualFold(sourceType, "git") {
					source.SourceType = install.Git
				}
			}

			if branch, ok := sourceConfig["branch"].(string); ok {
				source.Branch = branch
			}

			if source.IsValid() {
				sources = append(sources, source)
			} else {
				log.Warnf("Invalid source configuration for %s", name)
			}
		}
	}

	if len(sources) == 0 {
		sources = defaultSources
	}

	inst.SyncSources(sources, force)
}

func readConfigFile() {
	// read configuration file if specified
	configFile := viper.GetString("config-file")
	if configFile != "" {
		if _, err := os.Stat(configFile); err == nil {
			viper.SetConfigFile(configFile)
			err = viper.ReadInConfig()
			if err != nil {
				log.Warnf("Could not read configuration file: %v", err)
			}
		} else {
			log.Warnf("Could not find configuration file: %s", configFile)
		}
	}
}

func setupLogging() {
	switch viper.GetString("log-level") {
	case "debug":
		log.SetLevel(log.DebugLevel)
	case "info":
		log.SetLevel(log.InfoLevel)
	case "warn":
		log.SetLevel(log.WarnLevel)
	case "error":
		log.SetLevel(log.ErrorLevel)
	case "fatal":
		log.SetLevel(log.FatalLevel)
	default:
		log.WithField("log-level", viper.GetString("log-level")).Warning("invalid log level. defaulting to info.")
		log.SetLevel(log.InfoLevel)
	}

	switch viper.GetString("log-format") {
	case "text":
		log.SetFormatter(new(log.TextFormatter))
	case "json":
		log.SetFormatter(new(log.JSONFormatter))
	default:
		log.WithField("log-format", viper.GetString("log-format")).Warning("invalid log format. defaulting to text.")
		log.SetFormatter(new(log.TextFormatter))
	}
}
<commit_msg>expand env vars when looking for config file<commit_after>package main

import (
	"fmt"
	"github.com/CiscoCloud/mantl-api/api"
	"github.com/CiscoCloud/mantl-api/install"
	"github.com/CiscoCloud/mantl-api/marathon"
	"github.com/CiscoCloud/mantl-api/mesos"
	"github.com/CiscoCloud/mantl-api/utils/http"
	"github.com/CiscoCloud/mantl-api/zookeeper"
	log "github.com/Sirupsen/logrus"
	consul "github.com/hashicorp/consul/api"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"os"
	"strings"
)

const Name = "mantl-api"
const Version = "0.1.3.1"

func main() {
	rootCmd := &cobra.Command{
		Use:   "mantl-api",
		Short: "runs the mantl-api",
		Run: func(cmd *cobra.Command, args []string) {
			start()
		},
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			readConfigFile()
			setupLogging()
		},
	}

	rootCmd.PersistentFlags().String("log-level", "info", "one of debug, info, warn, error, or fatal")
	rootCmd.PersistentFlags().String("log-format", "text", "specify output (text or json)")
	rootCmd.PersistentFlags().String("consul", "http://localhost:8500", "Consul Api address")
	rootCmd.PersistentFlags().String("marathon", "", "Marathon Api address")
	rootCmd.PersistentFlags().String("marathon-user", "", "Marathon Api user")
	rootCmd.PersistentFlags().String("marathon-password", "", "Marathon Api password")
	rootCmd.PersistentFlags().Bool("marathon-no-verify-ssl", false, "Marathon SSL verification")
	rootCmd.PersistentFlags().String("mesos", "", "Mesos Api address")
	rootCmd.PersistentFlags().String("mesos-principal", "", "Mesos principal for framework authentication")
	rootCmd.PersistentFlags().String("mesos-secret", "", "Mesos secret for framework authentication")
	rootCmd.PersistentFlags().Bool("mesos-no-verify-ssl", false, "Mesos SSL verification")
	rootCmd.PersistentFlags().String("listen", ":4001", "mantl-api listen address")
	rootCmd.PersistentFlags().String("zookeeper", "", "Comma-delimited list of zookeeper servers")
	rootCmd.PersistentFlags().Bool("force-sync", false, "Force a synchronization of all sources")
	rootCmd.PersistentFlags().String("config-file", "", "The path to a configuration file")

	for _, flags := range []*pflag.FlagSet{rootCmd.PersistentFlags()} {
		err := viper.BindPFlags(flags)
		if err != nil {
			log.WithField("error", err).Fatal("could not bind flags")
		}
	}

	viper.SetEnvPrefix("mantl_api")
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()

	syncCommand := &cobra.Command{
		Use:   "sync",
		Short: "Synchronize universe repositories",
		Long:  "Forces a synchronization of all configured sources",
		Run: func(cmd *cobra.Command, args []string) {
			sync(nil, true)
		},
	}
	rootCmd.AddCommand(syncCommand)

	versionCommand := &cobra.Command{
		Use:   "version",
		Short: fmt.Sprintf("Print the version number of %s", Name),
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("%s v%s\n", Name, Version)
		},
	}
	rootCmd.AddCommand(versionCommand)

	rootCmd.Execute()
}

func start() {
	log.Infof("Starting %s v%s", Name, Version)
	client := consulClient()

	marathonUrl := viper.GetString("marathon")
	if marathonUrl == "" {
		marathonUrl = NewDiscovery(client, "marathon", "", "http", marathonUrl).discoveredUrl
	}
	marathonClient, err := marathon.NewMarathon(
		marathonUrl,
		viper.GetString("marathon-user"),
		viper.GetString("marathon-password"),
		viper.GetBool("marathon-no-verify-ssl"),
	)
	if err != nil {
		log.Fatalf("Could not create marathon client: %v", err)
	}

	mesosUrl := viper.GetString("mesos")
	if mesosUrl == "" {
		mesosUrl = NewDiscovery(client, "mesos", "leader", "http", "http://localhost:5050").discoveredUrl
	}
	mesosClient, err := mesos.NewMesos(
		mesosUrl,
		viper.GetString("mesos-principal"),
		viper.GetString("mesos-secret"),
		viper.GetBool("mesos-no-verify-ssl"),
	)
	if err != nil {
		log.Fatalf("Could not create mesos client: %v", err)
	}

	zkUrls := viper.GetString("zookeeper")
	if zkUrls == "" {
		zkUrls = NewDiscovery(client, "zookeeper", "", "", "localhost:2181").discoveredUrl
	}
	zkServers := strings.Split(zkUrls, ",")
	zk := zookeeper.NewZookeeper(zkServers)

	inst, err := install.NewInstall(client, marathonClient, mesosClient, zk)
	if err != nil {
		log.Fatalf("Could not create install client: %v", err)
	}

	// sync sources to consul
	sync(inst, viper.GetBool("force-sync"))

	// start listener
	api.NewApi(viper.GetString("listen"), inst).Start()
}

func consulClient() *consul.Client {
	consulConfig := consul.DefaultConfig()
	scheme, address, err := http.ParseUrl(viper.GetString("consul"))
	if err != nil {
		log.Fatalf("Could not create consul client: %v", err)
	}
	consulConfig.Scheme = scheme
	consulConfig.Address = address

	log.Debugf("Using Consul at %s over %s", consulConfig.Address, consulConfig.Scheme)

	client, err := consul.NewClient(consulConfig)
	if err != nil {
		log.Fatalf("Could not create consul client: %v", err)
	}

	// abort if we cannot connect to consul
	err = testConsul(client)
	if err != nil {
		log.Fatalf("Could not connect to consul: %v", err)
	}

	return client
}

func testConsul(client *consul.Client) error {
	kv := client.KV()
	_, _, err := kv.Get("mantl-install", nil)
	return err
}

func sync(inst *install.Install, force bool) {
	var err error
	if inst == nil {
		client := consulClient()
		inst, err = install.NewInstall(client, nil, nil, nil)
		if err != nil {
			log.Fatalf("Could not create install client: %v", err)
		}
	}

	defaultSources := []*install.Source{
		&install.Source{
			Name:       "mantl",
			Path:       "https://github.com/CiscoCloud/mantl-universe.git",
			SourceType: install.Git,
			Branch:     "master",
			Index:      1,
		},
		&install.Source{
			Name:       "mesosphere",
			Path:       "https://github.com/mesosphere/universe.git",
			SourceType: install.Git,
			Index:      0,
		},
	}

	sources := []*install.Source{}

	configuredSources := viper.GetStringMap("sources")

	if len(configuredSources) > 0 {
		for name, val := range configuredSources {
			source := &install.Source{Name: name, SourceType: install.FileSystem}
			sourceConfig := val.(map[string]interface{})

			if path, ok := sourceConfig["path"].(string); ok {
				source.Path = path
			}

			if index, ok := sourceConfig["index"].(int64); ok {
				source.Index = int(index)
			}

			if sourceType, ok := sourceConfig["type"].(string); ok {
				if strings.EqualFold(sourceType, "git") {
					source.SourceType = install.Git
				}
			}

			if branch, ok := sourceConfig["branch"].(string); ok {
				source.Branch = branch
			}

			if source.IsValid() {
				sources = append(sources, source)
			} else {
				log.Warnf("Invalid source configuration for %s", name)
			}
		}
	}

	if len(sources) == 0 {
		sources = defaultSources
	}

	inst.SyncSources(sources, force)
}

func readConfigFile() {
	// read configuration file if specified
	configFile := viper.GetString("config-file")
	if configFile != "" {
		configFile = os.ExpandEnv(configFile)
		if _, err := os.Stat(configFile); err == nil {
			viper.SetConfigFile(configFile)
			err = viper.ReadInConfig()
			if err != nil {
				log.Warnf("Could not read configuration file: %v", err)
			}
		} else {
			log.Warnf("Could not find configuration file: %s", configFile)
		}
	}
}

func setupLogging() {
	switch viper.GetString("log-level") {
	case "debug":
		log.SetLevel(log.DebugLevel)
	case "info":
		log.SetLevel(log.InfoLevel)
	case "warn":
		log.SetLevel(log.WarnLevel)
	case "error":
		log.SetLevel(log.ErrorLevel)
	case "fatal":
		log.SetLevel(log.FatalLevel)
	default:
		log.WithField("log-level", viper.GetString("log-level")).Warning("invalid log level. defaulting to info.")
		log.SetLevel(log.InfoLevel)
	}

	switch viper.GetString("log-format") {
	case "text":
		log.SetFormatter(new(log.TextFormatter))
	case "json":
		log.SetFormatter(new(log.JSONFormatter))
	default:
		log.WithField("log-format", viper.GetString("log-format")).Warning("invalid log format. defaulting to text.")
		log.SetFormatter(new(log.TextFormatter))
	}
}
<|endoftext|>


<commit_before>// httpserver.go
package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
)

type Redirection struct {
	Path string
	URL  string
}

type Router struct {
	Host         string
	Port         int
	Redirections []Redirection
}

const DefaultPort = 8888
const DefaultHost = "0.0.0.0"

var logFlag = flag.String("log", "", "Define log file")
var routerFilePath = flag.String("router", "router.json", "Define router file")
var sslKeyFile = flag.String("sslKey", "", "Path to key file")
var sslCertFile = flag.String("sslCert", "", "Path to certificate file")

var logger *log.Logger

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		//		w.Header().Set("Access-Control-Allow-Origin", "3ds.com")
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Origin, Accept, Format, NoDefault, x-requested-method, x-requested-with, x-request")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		handler.ServeHTTP(w, r)
	})
}

func main() {
	flag.Parse()
	if *logFlag == "" {
		// use default logger
		logger = log.New(os.Stderr, "", log.LstdFlags)
		log.Println("Log to stderr")
	} else {
		logFile, err := os.Create(*logFlag)
		if err != nil {
			log.Println(err)
			return
		}
		defer logFile.Close()
		log.Printf("Log to %s", *logFlag)
		logger = log.New(logFile, "", log.LstdFlags)
	}
	var router Router
	router.Port = DefaultPort
	router.Host = DefaultHost
	routerFile, err := os.Open(*routerFilePath)
	if err != nil {
		log.Println(err)
		router.Redirections = []Redirection{{Path: "/", URL: "."}}
	} else {
		defer routerFile.Close()
		json.NewDecoder(routerFile).Decode(&router)
	}

	routerStr, _ := json.MarshalIndent(router, "", "  ")
	logger.Printf("Router: %s", routerStr)

	for _, redirection := range router.Redirections {
		if strings.HasPrefix(redirection.URL, "http") {
			redirectUrl, err := url.Parse(redirection.URL)
			if err != nil {
				log.Println(err)
				return
			}
			http.Handle(redirection.Path, http.StripPrefix(redirection.Path, httputil.NewSingleHostReverseProxy(redirectUrl)))
		} else {
			http.Handle(redirection.Path, http.StripPrefix(redirection.Path, http.FileServer(http.Dir(redirection.URL))))
		}

	}

	if *sslKeyFile != "" && *sslCertFile != "" {
		logger.Printf("Listen on port https://%s:%d", router.Host, router.Port)
		panic(http.ListenAndServeTLS(router.Host+":"+strconv.Itoa(router.Port), *sslCertFile, *sslKeyFile, Log(http.DefaultServeMux)))
	} else {
		logger.Printf("Listen on port http://%s:%d", router.Host, router.Port)
		panic(http.ListenAndServe(router.Host+":"+strconv.Itoa(router.Port), Log(http.DefaultServeMux)))
	}

}
<commit_msg>print host<commit_after>// httpserver.go
package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
)

type Redirection struct {
	Path string
	URL  string
}

type Router struct {
	Host         string
	Port         int
	Redirections []Redirection
}

const DefaultPort = 8888
const DefaultHost = "0.0.0.0"

var logFlag = flag.String("log", "", "Define log file")
var routerFilePath = flag.String("router", "router.json", "Define router file")
var sslKeyFile = flag.String("sslKey", "", "Path to key file")
var sslCertFile = flag.String("sslCert", "", "Path to certificate file")

var logger *log.Logger

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.Printf("%s %s %s%s", r.RemoteAddr, r.Method, r.Host, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func main() {
	flag.Parse()
	if *logFlag == "" {
		// use default logger
		logger = log.New(os.Stderr, "", log.LstdFlags)
		log.Println("Log to stderr")
	} else {
		logFile, err := os.Create(*logFlag)
		if err != nil {
			log.Println(err)
			return
		}
		defer logFile.Close()
		log.Printf("Log to %s", *logFlag)
		logger = log.New(logFile, "", log.LstdFlags)
	}
	var router Router
	router.Port = DefaultPort
	router.Host = DefaultHost
	routerFile, err := os.Open(*routerFilePath)
	if err != nil {
		log.Println(err)
		router.Redirections = []Redirection{{Path: "/", URL: "."}}
	} else {
		defer routerFile.Close()
		json.NewDecoder(routerFile).Decode(&router)
	}

	routerStr, _ := json.MarshalIndent(router, "", "  ")
	logger.Printf("Router: %s", routerStr)

	for _, redirection := range router.Redirections {
		if strings.HasPrefix(redirection.URL, "http") {
			redirectUrl, err := url.Parse(redirection.URL)
			if err != nil {
				log.Println(err)
				return
			}
			http.Handle(redirection.Path, http.StripPrefix(redirection.Path, httputil.NewSingleHostReverseProxy(redirectUrl)))
		} else {
			http.Handle(redirection.Path, http.StripPrefix(redirection.Path, http.FileServer(http.Dir(redirection.URL))))
		}

	}

	if *sslKeyFile != "" && *sslCertFile != "" {
		logger.Printf("Listen on port https://%s:%d", router.Host, router.Port)
		panic(http.ListenAndServeTLS(router.Host+":"+strconv.Itoa(router.Port), *sslCertFile, *sslKeyFile, Log(http.DefaultServeMux)))
	} else {
		logger.Printf("Listen on port http://%s:%d", router.Host, router.Port)
		panic(http.ListenAndServe(router.Host+":"+strconv.Itoa(router.Port), Log(http.DefaultServeMux)))
	}

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"crypto/tls"
	"fmt"
	"github.com/Zaibon/ircbot"
	"io"
	"log"
	"log"
	"net"
	"os"
)

func main() {
	cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")

	b := ircbot.NewIrcBot()
	b.Server = "irc.freenode.net"
	b.Port = "6667"
	b.Nick = "ZbiBot"
	b.User = b.Nick

	b.Channel = append(b.Channel, "#testgigx")

	b.Handlers["PING"] = ircbot.Pong
	b.Handlers["JOIN"] = ircbot.Join
	b.Handlers["PRIVMSG"] = ircbot.Respond

	b.Connect()

	b.Listen()
	b.HandleActionIn()
	b.HandleActionOut()

	b.Join()

	//TODO handle signal system to throw something in b.Exit
	<-b.Exit
	//and then disconenct
	b.Disconnect()
}
<commit_msg>error last commit<commit_after>package main

import (
	"github.com/Zaibon/ircbot"
	// "time"
)

func main() {

	b := ircbot.NewIrcBot()
	b.Server = "irc.freenode.net"
	b.Port = "6667"
	b.Nick = "ZbiBot"
	b.User = b.Nick

	b.Channel = append(b.Channel, "#testgigx")

	b.Handlers["PING"] = ircbot.Pong
	b.Handlers["JOIN"] = ircbot.Join
	b.Handlers["PRIVMSG"] = ircbot.Respond

	b.Connect()

	b.Listen()
	b.HandleActionIn()
	b.HandleActionOut()

	b.Join()

	//TODO handle signal system to throw something in b.Exit
	<-b.Exit
	//and then disconenct
	b.Disconnect()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/basgys/alterego/router"
)

var (
	rt                 *router.Router // Let's make it global
	requestLogging     bool
	redirectStatusCode int
)

func main() {
	// Basic configuration
	requestLogging = os.Getenv("REQUEST_LOGGING") == "true"
	rsc := os.Getenv("REDIRECT_STATUS_CODE")
	if rsc != "" {
		code, err := strconv.Atoi(rsc)
		if err != nil {
			fmt.Println("Cannot parse REDIRECT_STATUS_CODE", err)
			os.Exit(1)
		}
		redirectStatusCode = code
	} else {
		redirectStatusCode = http.StatusPermanentRedirect
	}

	// Build HTTP server addr
	ip := os.Getenv("IP")
	if ip == "" {
		ip = "127.0.0.1"
	}
	port := os.Getenv("PORT")
	if port == "" {
		port = "5080"
	}
	addr := strings.Join([]string{ip, port}, ":")

	// Build router
	rules, err := buildRules()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	printRules(rules)

	rt = &router.Router{
		Rules: rules,
	}

	// Attach routes
	mux := http.NewServeMux()
	mux.HandleFunc("/__health__", health)
	mux.HandleFunc("/", redirect)

	// Boot
	s := &http.Server{
		Addr:           addr,
		Handler:        mux,
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	fmt.Printf("\nRunning on %s...\n", addr)
	log.Fatal(s.ListenAndServe())
}

// health is called by the container health check
func health(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func redirect(w http.ResponseWriter, r *http.Request) {
	uri, err := url.ParseRequestURI(r.RequestURI)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	req := url.URL{
		Scheme:   "http",
		Host:     r.Host,
		Path:     uri.Path,
		RawQuery: uri.RawQuery,
	}

	// Find matching route
	redir, ok := rt.Match(&req)
	if !ok {
		if requestLogging {
			fmt.Printf("[%s] No match %s -> %d\n",
				time.Now().UTC().Format(time.RFC3339Nano),
				req.String(),
				http.StatusNotFound,
			)
		}
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if requestLogging {
		fmt.Printf("[%s] Redirect %s -> %s\n",
			time.Now().UTC().Format(time.RFC3339Nano),
			req.String(),
			redir.String(),
		)
	}
	http.Redirect(w, r, redir.String(), http.StatusTemporaryRedirect)
}

func buildRules() ([]router.Rule, error) {
	var rules []router.Rule
	items := strings.Split(os.Getenv("REDIRECTS"), ",")
	for i, item := range items {
		if item == "" {
			return nil, fmt.Errorf("empty redirect #%d", i)
		}

		env := strings.TrimSpace(item)
		l := strings.Split(os.Getenv(env), ",")
		if len(l) != 2 {
			return nil, fmt.Errorf("malformed redirection %s from env var %s", l, env)
		}
		src, err := url.Parse(l[0])
		if err != nil {
			return nil, fmt.Errorf("bad source url %s", l[0])
		}
		dst, err := url.Parse(l[1])
		if err != nil {
			return nil, fmt.Errorf("bad destination url %s", l[1])
		}

		rules = append(rules, router.Rule{
			Src: *src,
			Dst: *dst,
		})
	}
	return rules, nil
}

func printRules(rules []router.Rule) {
	w := new(tabwriter.Writer)
	buf := bytes.NewBuffer([]byte{})
	w.Init(buf, 0, 8, 0, '\t', 0)
	fmt.Fprintln(w, "Source \t Destination")

	for _, r := range rules {
		fmt.Fprintf(w, "%s \t %s\n", r.Src.String(), r.Dst.String())
	}

	w.Flush()
	fmt.Println("Rules:")
	fmt.Println(buf.String())
}
<commit_msg>Use dynamic status code<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/basgys/alterego/router"
)

var (
	rt                 *router.Router // Let's make it global
	requestLogging     bool
	redirectStatusCode int
)

func main() {
	// Basic configuration
	requestLogging = os.Getenv("REQUEST_LOGGING") == "true"
	rsc := os.Getenv("REDIRECT_STATUS_CODE")
	if rsc != "" {
		code, err := strconv.Atoi(rsc)
		if err != nil {
			fmt.Println("Cannot parse REDIRECT_STATUS_CODE", err)
			os.Exit(1)
		}
		redirectStatusCode = code
	} else {
		redirectStatusCode = http.StatusPermanentRedirect
	}

	// Build HTTP server addr
	ip := os.Getenv("IP")
	if ip == "" {
		ip = "127.0.0.1"
	}
	port := os.Getenv("PORT")
	if port == "" {
		port = "5080"
	}
	addr := strings.Join([]string{ip, port}, ":")

	// Build router
	rules, err := buildRules()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	printRules(rules)

	rt = &router.Router{
		Rules: rules,
	}

	// Attach routes
	mux := http.NewServeMux()
	mux.HandleFunc("/__health__", health)
	mux.HandleFunc("/", redirect)

	// Boot
	s := &http.Server{
		Addr:           addr,
		Handler:        mux,
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	fmt.Printf("\nRunning on %s...\n", addr)
	log.Fatal(s.ListenAndServe())
}

// health is called by the container health check
func health(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func redirect(w http.ResponseWriter, r *http.Request) {
	uri, err := url.ParseRequestURI(r.RequestURI)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	req := url.URL{
		Scheme:   "http",
		Host:     r.Host,
		Path:     uri.Path,
		RawQuery: uri.RawQuery,
	}

	// Find matching route
	redir, ok := rt.Match(&req)
	if !ok {
		if requestLogging {
			fmt.Printf("[%s] No match %s -> %d\n",
				time.Now().UTC().Format(time.RFC3339Nano),
				req.String(),
				http.StatusNotFound,
			)
		}
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if requestLogging {
		fmt.Printf("[%s] Redirect %s -> %s\n",
			time.Now().UTC().Format(time.RFC3339Nano),
			req.String(),
			redir.String(),
		)
	}
	http.Redirect(w, r, redir.String(), redirectStatusCode)
}

func buildRules() ([]router.Rule, error) {
	var rules []router.Rule
	items := strings.Split(os.Getenv("REDIRECTS"), ",")
	for i, item := range items {
		if item == "" {
			return nil, fmt.Errorf("empty redirect #%d", i)
		}

		env := strings.TrimSpace(item)
		l := strings.Split(os.Getenv(env), ",")
		if len(l) != 2 {
			return nil, fmt.Errorf("malformed redirection %s from env var %s", l, env)
		}
		src, err := url.Parse(l[0])
		if err != nil {
			return nil, fmt.Errorf("bad source url %s", l[0])
		}
		dst, err := url.Parse(l[1])
		if err != nil {
			return nil, fmt.Errorf("bad destination url %s", l[1])
		}

		rules = append(rules, router.Rule{
			Src: *src,
			Dst: *dst,
		})
	}
	return rules, nil
}

func printRules(rules []router.Rule) {
	w := new(tabwriter.Writer)
	buf := bytes.NewBuffer([]byte{})
	w.Init(buf, 0, 8, 0, '\t', 0)
	fmt.Fprintln(w, "Source \t Destination")

	for _, r := range rules {
		fmt.Fprintf(w, "%s \t %s\n", r.Src.String(), r.Dst.String())
	}

	w.Flush()
	fmt.Println("Rules:")
	fmt.Println(buf.String())
}
<|endoftext|>


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

// This program ensures source code files have copyright license headers.
// See usage with "addlicence -h".
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/sync/errgroup"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder    = flag.String("c", "Google LLC", "copyright holder")
	license   = flag.String("l", "apache", "license type: apache, bsd, mit")
	licensef  = flag.String("f", "", "license file")
	year      = flag.String("y", fmt.Sprint(time.Now().Year()), "copyright year(s)")
	verbose   = flag.Bool("v", false, "verbose mode: print the name of the files that are modified")
	checkonly = flag.Bool("check", false, "check only mode: verify presence of license headers and exit with non-zero code if missing")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	var t *template.Template
	if *licensef != "" {
		d, err := ioutil.ReadFile(*licensef)
		if err != nil {
			log.Printf("license file: %v", err)
			os.Exit(1)
		}
		t, err = template.New("").Parse(string(d))
		if err != nil {
			log.Printf("license file: %v", err)
			os.Exit(1)
		}
	} else {
		t = licenseTemplate[*license]
		if t == nil {
			log.Printf("unknown license: %s", *license)
			os.Exit(1)
		}
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg errgroup.Group
		for f := range ch {
			f := f // https://golang.org/doc/faq#closures_and_goroutines
			wg.Go(func() error {
				if *checkonly {
					// Check if file extension is known
					lic, err := licenseHeader(f.path, t, data)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if lic == nil { // Unknown fileExtension
						return nil
					}
					// Check if file has a license
					isMissingLicenseHeader, err := fileHasLicense(f.path)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if isMissingLicenseHeader {
						fmt.Printf("%s\n", f.path)
						return errors.New("missing license header")
					}
				} else {
					modified, err := addLicense(f.path, f.mode, t, data)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if *verbose && modified {
						log.Printf("%s modified", f.path)
					}
				}
				return nil
			})
		}
		err := wg.Wait()
		close(done)
		if err != nil {
			os.Exit(1)
		}
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *copyrightData) (bool, error) {
	var lic []byte
	var err error
	lic, err = licenseHeader(path, tmpl, data)
	if err != nil || lic == nil {
		return false, err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return false, err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return true, ioutil.WriteFile(path, b, fmode)
}

// fileHasLicense reports whether the file at path contains a license header.
func fileHasLicense(path string) (bool, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return false, err
	}
	return true, nil
}

func licenseHeader(path string, tmpl *template.Template, data *copyrightData) ([]byte, error) {
	var lic []byte
	var err error
	switch fileExtension(path) {
	default:
		return nil, nil
	case ".c", ".h":
		lic, err = prefix(tmpl, data, "/*", " * ", " */")
	case ".js", ".jsx", ".tsx", ".css", ".tf", ".ts":
		lic, err = prefix(tmpl, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart", ".groovy", ".kt", ".kts":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml", ".dockerfile", "dockerfile", ".rb", "gemfile":
		lic, err = prefix(tmpl, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(tmpl, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(tmpl, data, "", "% ", "")
	case ".hs", ".sql":
		lic, err = prefix(tmpl, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(tmpl, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".ml", ".mli", ".mll", ".mly":
		lic, err = prefix(tmpl, data, "(**", "   ", "*)")
	}
	return lic, err
}

func fileExtension(name string) string {
	if v := filepath.Ext(name); v != "" {
		return strings.ToLower(v)
	}
	return strings.ToLower(filepath.Base(name))
}

var head = []string{
	"#!",                       // shell script
	"<?xml",                    // XML declaratioon
	"<!doctype",                // HTML doctype
	"# encoding:",              // Ruby encoding
	"# frozen_string_literal:", // Ruby interpreter instruction
	"<?php",                    // PHP opening tag
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	first := strings.ToLower(string(line))
	for _, h := range head {
		if strings.HasPrefix(first, h) {
			return line
		}
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<commit_msg>Fix typo<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This program ensures source code files have copyright license headers.
// See usage with "addlicense -h".
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/sync/errgroup"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder    = flag.String("c", "Google LLC", "copyright holder")
	license   = flag.String("l", "apache", "license type: apache, bsd, mit")
	licensef  = flag.String("f", "", "license file")
	year      = flag.String("y", fmt.Sprint(time.Now().Year()), "copyright year(s)")
	verbose   = flag.Bool("v", false, "verbose mode: print the name of the files that are modified")
	checkonly = flag.Bool("check", false, "check only mode: verify presence of license headers and exit with non-zero code if missing")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	var t *template.Template
	if *licensef != "" {
		d, err := ioutil.ReadFile(*licensef)
		if err != nil {
			log.Printf("license file: %v", err)
			os.Exit(1)
		}
		t, err = template.New("").Parse(string(d))
		if err != nil {
			log.Printf("license file: %v", err)
			os.Exit(1)
		}
	} else {
		t = licenseTemplate[*license]
		if t == nil {
			log.Printf("unknown license: %s", *license)
			os.Exit(1)
		}
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg errgroup.Group
		for f := range ch {
			f := f // https://golang.org/doc/faq#closures_and_goroutines
			wg.Go(func() error {
				if *checkonly {
					// Check if file extension is known
					lic, err := licenseHeader(f.path, t, data)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if lic == nil { // Unknown fileExtension
						return nil
					}
					// Check if file has a license
					isMissingLicenseHeader, err := fileHasLicense(f.path)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if isMissingLicenseHeader {
						fmt.Printf("%s\n", f.path)
						return errors.New("missing license header")
					}
				} else {
					modified, err := addLicense(f.path, f.mode, t, data)
					if err != nil {
						log.Printf("%s: %v", f.path, err)
						return err
					}
					if *verbose && modified {
						log.Printf("%s modified", f.path)
					}
				}
				return nil
			})
		}
		err := wg.Wait()
		close(done)
		if err != nil {
			os.Exit(1)
		}
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *copyrightData) (bool, error) {
	var lic []byte
	var err error
	lic, err = licenseHeader(path, tmpl, data)
	if err != nil || lic == nil {
		return false, err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return false, err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return true, ioutil.WriteFile(path, b, fmode)
}

// fileHasLicense reports whether the file at path contains a license header.
func fileHasLicense(path string) (bool, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return false, err
	}
	return true, nil
}

func licenseHeader(path string, tmpl *template.Template, data *copyrightData) ([]byte, error) {
	var lic []byte
	var err error
	switch fileExtension(path) {
	default:
		return nil, nil
	case ".c", ".h":
		lic, err = prefix(tmpl, data, "/*", " * ", " */")
	case ".js", ".jsx", ".tsx", ".css", ".tf", ".ts":
		lic, err = prefix(tmpl, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart", ".groovy", ".kt", ".kts":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml", ".dockerfile", "dockerfile", ".rb", "gemfile":
		lic, err = prefix(tmpl, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(tmpl, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(tmpl, data, "", "% ", "")
	case ".hs", ".sql":
		lic, err = prefix(tmpl, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(tmpl, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(tmpl, data, "", "// ", "")
	case ".ml", ".mli", ".mll", ".mly":
		lic, err = prefix(tmpl, data, "(**", "   ", "*)")
	}
	return lic, err
}

func fileExtension(name string) string {
	if v := filepath.Ext(name); v != "" {
		return strings.ToLower(v)
	}
	return strings.ToLower(filepath.Base(name))
}

var head = []string{
	"#!",                       // shell script
	"<?xml",                    // XML declaratioon
	"<!doctype",                // HTML doctype
	"# encoding:",              // Ruby encoding
	"# frozen_string_literal:", // Ruby interpreter instruction
	"<?php",                    // PHP opening tag
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	first := strings.ToLower(string(line))
	for _, h := range head {
		if strings.HasPrefix(first, h) {
			return line
		}
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<|endoftext|>