<commit_before>package scope

import (
	"fmt"

	"github.com/anchore/stereoscope/pkg/file"
	"github.com/anchore/stereoscope/pkg/image"
)

type Scope struct {
	Option   Option
	resolver FileResolver
	Image    *image.Image
}

func NewScope(img *image.Image, option Option) (Scope, error) {
	if img == nil {
		return Scope{}, fmt.Errorf("no image given")
	}

	resolver, err := getFileResolver(img, option)
	if err != nil {
		return Scope{}, fmt.Errorf("could not determine file resolver: %w", err)
	}

	return Scope{
		Option:   option,
		resolver: resolver,
		Image:    img,
	}, nil
}

func (s Scope) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
	return s.resolver.FilesByPath(paths...)
}

func (s Scope) FilesByGlob(patterns ...string) ([]file.Reference, error) {
	return s.resolver.FilesByGlob(patterns...)
}
<commit_msg>scope: add directory-based scoping<commit_after>package scope

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

	"github.com/anchore/imgbom/internal/log"
	"github.com/anchore/stereoscope/pkg/file"
	"github.com/anchore/stereoscope/pkg/image"
)

type DirectoryScope struct {
	Option Option
	Path   string
}

func (s DirectoryScope) String() string {
	return fmt.Sprintf("dir://%s", s.Path)
}

func (s DirectoryScope) FilesByPath(userPaths ...file.Path) ([]file.Reference, error) {
	var references = make([]file.Reference, 0)

	for _, userPath := range userPaths {
		resolvedPath := path.Join(s.Path, string(userPath))
		_, err := os.Stat(resolvedPath)
		if os.IsNotExist(err) {
			continue
		} else if err != nil {
			log.Errorf("path (%s) is not valid: %v", resolvedPath, err)
		}
		filePath := file.Path(resolvedPath)
		references = append(references, file.NewFileReference(filePath))
	}

	return references, nil
}

func fileContents(path file.Path) ([]byte, error) {
	contents, err := ioutil.ReadFile(string(path))

	if err != nil {
		return nil, err
	}
	return contents, nil
}

func (s DirectoryScope) FilesByGlob(patterns ...string) ([]file.Reference, error) {
	result := make([]file.Reference, 0)

	for _, pattern := range patterns {
		pathPattern := path.Join(s.Path, pattern)
		matches, err := filepath.Glob(pathPattern)
		if err != nil {
			return result, err
		}
		for _, match := range matches {
			fileMeta, err := os.Stat(match)
			if err != nil {
				continue
			}
			if fileMeta.IsDir() {
				continue
			}
			matchedPath := file.Path(match)
			result = append(result, file.NewFileReference(matchedPath))
		}
	}

	return result, nil
}

func (s DirectoryScope) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
	refContents := make(map[file.Reference]string)
	for _, fileRef := range f {
		contents, err := fileContents(fileRef.Path)
		if err != nil {
			return refContents, fmt.Errorf("could not read contents of file: %s", fileRef.Path)
		}
		refContents[fileRef] = string(contents)
	}
	return refContents, nil
}

type ImageScope struct {
	Option   Option
	resolver FileResolver
	Image    *image.Image
}

func NewDirScope(path string, option Option) (DirectoryScope, error) {
	return DirectoryScope{
		Option: option,
		Path:   path,
	}, nil
}

func NewImageScope(img *image.Image, option Option) (ImageScope, error) {
	if img == nil {
		return ImageScope{}, fmt.Errorf("no image given")
	}

	resolver, err := getFileResolver(img, option)
	if err != nil {
		return ImageScope{}, fmt.Errorf("could not determine file resolver: %w", err)
	}

	return ImageScope{
		Option:   option,
		resolver: resolver,
		Image:    img,
	}, nil
}

func (s ImageScope) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
	return s.resolver.FilesByPath(paths...)
}

func (s ImageScope) FilesByGlob(patterns ...string) ([]file.Reference, error) {
	return s.resolver.FilesByGlob(patterns...)
}

func (s ImageScope) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
	return s.Image.MultipleFileContentsByRef(f...)
}
<|endoftext|>


<commit_before>// Package vblexer implements a VBScript lexer.
package vblexer

import (
	"github.com/ancientlore/vbscribble/vbscanner"
	"io"
	"strconv"
	"strings"
	"time"
)

//go:generate stringer -type=TokenType

// TokenType describes the type of token read by the scanner
type TokenType int

// Token types
const (
	EOF                 TokenType = iota // end of file
	STATEMENT                            // language statements (reserved words)
	FUNCTION                             // builtin functions
	KEYWORD                              // keywords - nothing, null, empty
	KEYWORD_BOOL                         // boolean keywords - true and false
	COLOR_CONSTANT                       // constants for colors, like vbRed
	COMPARE_CONSTANT                     // constants to specify binary or text comparison
	DATE_CONSTANT                        // constants relating to dates, like vbSunday
	DATEFORMAT_CONSTANT                  // constants for specifying date formats
	MISC_CONSTANT                        // miscellaenous constants
	MSGBOX_CONSTANT                      // constants for message boxes like MbOk
	STRING_CONSTANT                      // constant strings like vbCrLf
	TRISTATE_CONSTANT                    // constants for tristate use
	VARTYPE_CONSTANT                     // constants for variant types
	IDENTIFIER                           // identifiers
	STRING                               // string literals
	INT                                  // integer literals
	FLOAT                                // float literals
	DATE                                 // date literals
	COMMENT                              // comments
	HTML                                 // HTML fragments in the ASP
	CHAR                                 // random characters like parens
	EOL                                  // end of line
	OP                                   // operator
	CONTINUATION                         // continuation character (underscore)
)

// Lex uses a scanner to read and classify VBScript tokens
type Lex struct {
	s        vbscanner.Scanner
	Filename string
	Line     int
}

// Init prepares the lexer for use.
func (lex *Lex) Init(src io.Reader, fname string, initialMode vbscanner.Mode) {
	lex.s.Init(src, initialMode)
	lex.Filename = fname
	lex.Line = 1
}

// Lex returns the next token in the steam and classifies it. The values returned are
// the token type, the converted value, and the raw value as a string.
func (lex *Lex) Lex() (TokenType, interface{}, string) {
	tok, value := lex.s.Scan()

	// return tok.String(), value
	if tok == vbscanner.EOF {
		return EOF, nil, value
	}

	switch tok {
	case vbscanner.Ident:
		stoken := strings.ToLower(value)
		switch stoken {
		// statements
		case "call", "class", "const", "dim", "do", "loop", "erase", "execute", "executeglobal", "exit", "for", "each", "next", "function", "if", "then", "else", "elseif", "on", "error", "resume", "goto", "option", "explicit", "private", "public", "property", "let", "get", "set", "redim", "randomize", "rem", "select", "case", "stop", "sub", "while", "wend", "with", "end", "raise", "new":
			return STATEMENT, strings.Title(stoken), value

		// functions
		case "abs", "array", "asc", "atn", "cbool", "cbyte", "ccur", "cdate", "cdbl", "chr", "cint", "clng", "conversions", "cos", "createobject", "csng", "cstr", "date", "dateadd", "datediff", "datepart", "dateserial", "datevalue", "day", "escape", "eval", "exp", "filter", "formatcurrency", "formatdatetime", "formatnumber", "formatpercent", "getlocale", "getobject", "getref", "hex", "hour", "inputbox", "instr", "instrrev", "int", "fix", "isarray", "isdate", "isempty", "isnull", "isnumeric", "isobject", "join", "lbound", "lcase", "left", "len", "loadpicture", "log", "ltrim", "rtrim", "trim", "maths", "mid", "minute", "month", "monthname", "msgbox", "now", "oct", "replace", "rgb", "right", "rnd", "round", "scriptengine", "scriptenginebuildversion", "scriptenginemajorversion", "scriptengineminorversion", "second", "setlocale", "sgn", "sin", "space", "split", "sqr", "strcomp", "string", "strreverse", "tan", "time", "timer", "timeserial", "timevalue", "typename", "ubound", "ucase", "unescape", "vartype", "weekday", "weekdayname", "year":
			return FUNCTION, formatFunction(stoken), value

		// Keywords
		case "null", "empty", "nothing":
			return KEYWORD, strings.Title(stoken), value
		case "true", "false":
			b, err := strconv.ParseBool(stoken)
			if err != nil {
				panic(err)
			}
			return KEYWORD_BOOL, b, value

		// constants
		case "vbblack", "vbred", "vbgreen", "vbyellow", "vbblue", "vbmagenta", "vbcyan", "vbwhite":
			return COLOR_CONSTANT, formatConstant(stoken), value
		case "vbbinarycompare", "vbtextcompare":
			return COMPARE_CONSTANT, formatConstant(stoken), value
		case "vbsunday", "vbmonday", "vbtuesday", "vbwednesday", "vbthursday", "vbfriday", "vbsaturday", "vbusesystemdayofweek", "vbfirstjan1", "vbfirstfourdays", "vbfirstfullweek":
			return DATE_CONSTANT, formatConstant(stoken), value
		case "vbgeneraldate", "vblongdate", "vbshortdate", "vblongtime", "vbshorttime":
			return DATEFORMAT_CONSTANT, formatConstant(stoken), value
		case "vbobjecterror":
			return MISC_CONSTANT, formatConstant(stoken), value
		case "vbokonly", "vbokcancel", "vbabortretryignore", "vbyesnocancel", "vbyesno", "vbretrycancel", "vbcritical", "vbquestion", "vbexclamation", "vbinformation", "vbdefaultbutton1", "vbdefaultbutton2", "vbdefaultbutton3", "vbdefaultbutton4", "vbapplicationmodal", "vbsystemmodal", "vbok", "vbcancel", "vbabort", "vbretry", "vbignore", "vbyes", "vbno":
			return MSGBOX_CONSTANT, formatConstant(stoken), value
		case "vbcr", "vbcrlf", "vbformfeed", "vblf", "vbnewline", "vbnullchar", "vbnullstring", "vbtab", "vbverticaltab":
			return STRING_CONSTANT, formatConstant(stoken), value
		case "vbusedefault", "vbtrue", "vbfalse":
			return TRISTATE_CONSTANT, formatConstant(stoken), value
		case "vbempty", "vbnull", "vbinteger", "vblong", "vbsingle", "vbdouble", "vbcurrency", "vbdate", "vbstring", "vbobject", "vberror", "vbboolean", "vbvariant", "vbdataobject", "vbdecimal", "vbbyte", "vbarray":
			return VARTYPE_CONSTANT, formatConstant(stoken), value
		default:
			return IDENTIFIER, value, value
		}

	// Values
	case vbscanner.String:
		return STRING, value, value
	case vbscanner.Integer:
		base := 10
		str := strings.ToLower(value)
		if strings.HasPrefix(str, "&h") {
			base = 16
			str = strings.TrimPrefix(str, "&h")
		} else if strings.HasPrefix(strings.ToLower(str), "&o") {
			base = 8
			str = strings.TrimPrefix(str, "&o")
		}
		i, err := strconv.ParseInt(str, base, 64)
		if err != nil {
			panic(err)
		}
		return INT, i, value
	case vbscanner.Float:
		f, err := strconv.ParseFloat(value, 64)
		if err != nil {
			panic(err)
		}
		return FLOAT, f, value
	case vbscanner.Date:
		formats := []string{
			"02-Jan-2006 15:04:05",  // #31-Dec-1999 21:26:00#
			"02-JAN-2006 15:04:05",  // #31-DEC-1999 21:26:00#
			"2006-01-02 15:04:05",   // #1999-12-31 21:26:00#
			"01/02/2006 3:04:05 PM", // #12/31/1999 9:26:00 PM#
			"01/02/2006 3:04:05 pm", // #12/31/1999 9:26:00 pm#
			"02-Jan-2006",           // #31-Dec-1999#
			"02-JAN-2006",           // #31-DEC-1999#
			"2006-01-02",            // #1999-12-31#
			"01/02/2006",            // #12/31/1999#
			"01-02-2006",            // #12-31-1999#
			"15:04:05",              // #21:26:00#
		}
		loc, _ := time.LoadLocation("America/New_York")
		for _, fmt := range formats {
			t, err := time.ParseInLocation(fmt, value, loc)
			if err == nil {
				return DATE, t, value
			}
		}
		return DATE, value, value
	case vbscanner.Comment:
		return COMMENT, value, value
	case vbscanner.Html:
		return HTML, value, value
	case vbscanner.Char:
		if value == "_" {
			return CONTINUATION, value, value
		} else {
			return CHAR, value, value
		}
	case vbscanner.EOL:
		lex.Line++
		return EOL, value, value
	case vbscanner.Op:
		return OP, strings.Title(value), value
	}

	panic("How did we get here?")
	// return "IDENT", value
}
<commit_msg>Added missing statement tokens<commit_after>// Package vblexer implements a VBScript lexer.
package vblexer

import (
	"github.com/ancientlore/vbscribble/vbscanner"
	"io"
	"strconv"
	"strings"
	"time"
)

//go:generate stringer -type=TokenType

// TokenType describes the type of token read by the scanner
type TokenType int

// Token types
const (
	EOF                 TokenType = iota // end of file
	STATEMENT                            // language statements (reserved words)
	FUNCTION                             // builtin functions
	KEYWORD                              // keywords - nothing, null, empty
	KEYWORD_BOOL                         // boolean keywords - true and false
	COLOR_CONSTANT                       // constants for colors, like vbRed
	COMPARE_CONSTANT                     // constants to specify binary or text comparison
	DATE_CONSTANT                        // constants relating to dates, like vbSunday
	DATEFORMAT_CONSTANT                  // constants for specifying date formats
	MISC_CONSTANT                        // miscellaenous constants
	MSGBOX_CONSTANT                      // constants for message boxes like MbOk
	STRING_CONSTANT                      // constant strings like vbCrLf
	TRISTATE_CONSTANT                    // constants for tristate use
	VARTYPE_CONSTANT                     // constants for variant types
	IDENTIFIER                           // identifiers
	STRING                               // string literals
	INT                                  // integer literals
	FLOAT                                // float literals
	DATE                                 // date literals
	COMMENT                              // comments
	HTML                                 // HTML fragments in the ASP
	CHAR                                 // random characters like parens
	EOL                                  // end of line
	OP                                   // operator
	CONTINUATION                         // continuation character (underscore)
)

// Lex uses a scanner to read and classify VBScript tokens
type Lex struct {
	s        vbscanner.Scanner
	Filename string
	Line     int
}

// Init prepares the lexer for use.
func (lex *Lex) Init(src io.Reader, fname string, initialMode vbscanner.Mode) {
	lex.s.Init(src, initialMode)
	lex.Filename = fname
	lex.Line = 1
}

// Lex returns the next token in the steam and classifies it. The values returned are
// the token type, the converted value, and the raw value as a string.
func (lex *Lex) Lex() (TokenType, interface{}, string) {
	tok, value := lex.s.Scan()

	// return tok.String(), value
	if tok == vbscanner.EOF {
		return EOF, nil, value
	}

	switch tok {
	case vbscanner.Ident:
		stoken := strings.ToLower(value)
		switch stoken {
		// statements
		case "call", "class", "const", "dim", "do", "loop", "erase", "execute", "executeglobal", "exit", "for", "each", "next", "function", "if", "then", "else", "elseif", "on", "error", "resume", "goto", "option", "explicit", "private", "public", "property", "let", "get", "set", "redim", "randomize", "rem", "select", "case", "stop", "sub", "while", "wend", "with", "end", "raise", "new", "to", "step", "byref", "byval", "is", "in":
			return STATEMENT, strings.Title(stoken), value

		// functions
		case "abs", "array", "asc", "atn", "cbool", "cbyte", "ccur", "cdate", "cdbl", "chr", "cint", "clng", "conversions", "cos", "createobject", "csng", "cstr", "date", "dateadd", "datediff", "datepart", "dateserial", "datevalue", "day", "escape", "eval", "exp", "filter", "formatcurrency", "formatdatetime", "formatnumber", "formatpercent", "getlocale", "getobject", "getref", "hex", "hour", "inputbox", "instr", "instrrev", "int", "fix", "isarray", "isdate", "isempty", "isnull", "isnumeric", "isobject", "join", "lbound", "lcase", "left", "len", "loadpicture", "log", "ltrim", "rtrim", "trim", "maths", "mid", "minute", "month", "monthname", "msgbox", "now", "oct", "replace", "rgb", "right", "rnd", "round", "scriptengine", "scriptenginebuildversion", "scriptenginemajorversion", "scriptengineminorversion", "second", "setlocale", "sgn", "sin", "space", "split", "sqr", "strcomp", "string", "strreverse", "tan", "time", "timer", "timeserial", "timevalue", "typename", "ubound", "ucase", "unescape", "vartype", "weekday", "weekdayname", "year":
			return FUNCTION, formatFunction(stoken), value

		// Keywords
		case "null", "empty", "nothing":
			return KEYWORD, strings.Title(stoken), value
		case "true", "false":
			b, err := strconv.ParseBool(stoken)
			if err != nil {
				panic(err)
			}
			return KEYWORD_BOOL, b, value

		// constants
		case "vbblack", "vbred", "vbgreen", "vbyellow", "vbblue", "vbmagenta", "vbcyan", "vbwhite":
			return COLOR_CONSTANT, formatConstant(stoken), value
		case "vbbinarycompare", "vbtextcompare":
			return COMPARE_CONSTANT, formatConstant(stoken), value
		case "vbsunday", "vbmonday", "vbtuesday", "vbwednesday", "vbthursday", "vbfriday", "vbsaturday", "vbusesystemdayofweek", "vbfirstjan1", "vbfirstfourdays", "vbfirstfullweek":
			return DATE_CONSTANT, formatConstant(stoken), value
		case "vbgeneraldate", "vblongdate", "vbshortdate", "vblongtime", "vbshorttime":
			return DATEFORMAT_CONSTANT, formatConstant(stoken), value
		case "vbobjecterror":
			return MISC_CONSTANT, formatConstant(stoken), value
		case "vbokonly", "vbokcancel", "vbabortretryignore", "vbyesnocancel", "vbyesno", "vbretrycancel", "vbcritical", "vbquestion", "vbexclamation", "vbinformation", "vbdefaultbutton1", "vbdefaultbutton2", "vbdefaultbutton3", "vbdefaultbutton4", "vbapplicationmodal", "vbsystemmodal", "vbok", "vbcancel", "vbabort", "vbretry", "vbignore", "vbyes", "vbno":
			return MSGBOX_CONSTANT, formatConstant(stoken), value
		case "vbcr", "vbcrlf", "vbformfeed", "vblf", "vbnewline", "vbnullchar", "vbnullstring", "vbtab", "vbverticaltab":
			return STRING_CONSTANT, formatConstant(stoken), value
		case "vbusedefault", "vbtrue", "vbfalse":
			return TRISTATE_CONSTANT, formatConstant(stoken), value
		case "vbempty", "vbnull", "vbinteger", "vblong", "vbsingle", "vbdouble", "vbcurrency", "vbdate", "vbstring", "vbobject", "vberror", "vbboolean", "vbvariant", "vbdataobject", "vbdecimal", "vbbyte", "vbarray":
			return VARTYPE_CONSTANT, formatConstant(stoken), value
		default:
			return IDENTIFIER, value, value
		}

	// Values
	case vbscanner.String:
		return STRING, value, value
	case vbscanner.Integer:
		base := 10
		str := strings.ToLower(value)
		if strings.HasPrefix(str, "&h") {
			base = 16
			str = strings.TrimPrefix(str, "&h")
		} else if strings.HasPrefix(strings.ToLower(str), "&o") {
			base = 8
			str = strings.TrimPrefix(str, "&o")
		}
		i, err := strconv.ParseInt(str, base, 64)
		if err != nil {
			panic(err)
		}
		return INT, i, value
	case vbscanner.Float:
		f, err := strconv.ParseFloat(value, 64)
		if err != nil {
			panic(err)
		}
		return FLOAT, f, value
	case vbscanner.Date:
		formats := []string{
			"02-Jan-2006 15:04:05",  // #31-Dec-1999 21:26:00#
			"02-JAN-2006 15:04:05",  // #31-DEC-1999 21:26:00#
			"2006-01-02 15:04:05",   // #1999-12-31 21:26:00#
			"01/02/2006 3:04:05 PM", // #12/31/1999 9:26:00 PM#
			"01/02/2006 3:04:05 pm", // #12/31/1999 9:26:00 pm#
			"02-Jan-2006",           // #31-Dec-1999#
			"02-JAN-2006",           // #31-DEC-1999#
			"2006-01-02",            // #1999-12-31#
			"01/02/2006",            // #12/31/1999#
			"01-02-2006",            // #12-31-1999#
			"15:04:05",              // #21:26:00#
		}
		loc, _ := time.LoadLocation("America/New_York")
		for _, fmt := range formats {
			t, err := time.ParseInLocation(fmt, value, loc)
			if err == nil {
				return DATE, t, value
			}
		}
		return DATE, value, value
	case vbscanner.Comment:
		return COMMENT, value, value
	case vbscanner.Html:
		return HTML, value, value
	case vbscanner.Char:
		if value == "_" {
			return CONTINUATION, value, value
		} else {
			return CHAR, value, value
		}
	case vbscanner.EOL:
		lex.Line++
		return EOL, value, value
	case vbscanner.Op:
		return OP, strings.Title(value), value
	}

	panic("How did we get here?")
	// return "IDENT", value
}
<|endoftext|>


<commit_before>package clock

import (
	"reflect"
	"testing"
)

// Clock type API:
//
// Time(hour, minute int) Clock    // a "constructor"
// (Clock) String() string         // a "stringer"
// (Clock) Add(minutes)
//
// Add should also handle subtraction by accepting negative values.
// To satisfy the readme requirement about clocks being equal, values of
// your Clock type need to work with the == operator.
//
// It might help to study the time.Time type in the standard library
// (https://golang.org/pkg/time/#Time) as a model.  See how constructors there
// (Date and Now) return Time values rather than pointers.  Note also how
// most time.Time methods have value receivers rather that pointer recievers.
// For more background on this read
// https://github.com/golang/go/wiki/CodeReviewComments#receiver-type.

const testVersion = 2

// Retired testVersions
// (none) 79937f6d58e25ebafe12d1cb4a9f88f4de70cfd6
// 1      8d0cb8b617be2e36b2ca5ad2034e5f80f2372924

func TestCreateClock(t *testing.T) {
	if TestVersion != testVersion {
		t.Fatalf("Found TestVersion = %v, want %v", TestVersion, testVersion)
	}
	for _, n := range timeTests {
		if got := Time(n.h, n.m); got.String() != n.want {
			t.Fatalf("Time(%d, %d) = %q, want %q", n.h, n.m, got, n.want)
		}
	}
	t.Log(len(timeTests), "test cases")
}

func TestAddMinutes(t *testing.T) {
	for _, a := range addTests {
		if got := Time(a.h, a.m).Add(a.a); got.String() != a.want {
			t.Fatalf("Time(%d, %d).Add(%d) = %q, want %q",
				a.h, a.m, a.a, got, a.want)
		}
	}
	t.Log(len(addTests), "test cases")
}

func TestCompareClocks(t *testing.T) {
	for _, e := range eqTests {
		clock1 := Time(e.c1.h, e.c1.m)
		clock2 := Time(e.c2.h, e.c2.m)
		got := clock1 == clock2
		if got != e.want {
			t.Log("Clock1:", clock1)
			t.Log("Clock2:", clock2)
			t.Logf("Clock1 == Clock2 is %t, want %t", got, e.want)
			if reflect.DeepEqual(clock1, clock2) {
				t.Log("(Hint: see comments in clock_test.go.)")
			}
			t.FailNow()
		}
	}
	t.Log(len(eqTests), "test cases")
}
<commit_msg>Change clock.Add prototype in comment<commit_after>package clock

import (
	"reflect"
	"testing"
)

// Clock type API:
//
// Time(hour, minute int) Clock    // a "constructor"
// (Clock) String() string         // a "stringer"
// (Clock) Add(minutes int) Clock
//
// Add should also handle subtraction by accepting negative values.
// To satisfy the readme requirement about clocks being equal, values of
// your Clock type need to work with the == operator.
//
// It might help to study the time.Time type in the standard library
// (https://golang.org/pkg/time/#Time) as a model.  See how constructors there
// (Date and Now) return Time values rather than pointers.  Note also how
// most time.Time methods have value receivers rather that pointer recievers.
// For more background on this read
// https://github.com/golang/go/wiki/CodeReviewComments#receiver-type.

const testVersion = 2

// Retired testVersions
// (none) 79937f6d58e25ebafe12d1cb4a9f88f4de70cfd6
// 1      8d0cb8b617be2e36b2ca5ad2034e5f80f2372924

func TestCreateClock(t *testing.T) {
	if TestVersion != testVersion {
		t.Fatalf("Found TestVersion = %v, want %v", TestVersion, testVersion)
	}
	for _, n := range timeTests {
		if got := Time(n.h, n.m); got.String() != n.want {
			t.Fatalf("Time(%d, %d) = %q, want %q", n.h, n.m, got, n.want)
		}
	}
	t.Log(len(timeTests), "test cases")
}

func TestAddMinutes(t *testing.T) {
	for _, a := range addTests {
		if got := Time(a.h, a.m).Add(a.a); got.String() != a.want {
			t.Fatalf("Time(%d, %d).Add(%d) = %q, want %q",
				a.h, a.m, a.a, got, a.want)
		}
	}
	t.Log(len(addTests), "test cases")
}

func TestCompareClocks(t *testing.T) {
	for _, e := range eqTests {
		clock1 := Time(e.c1.h, e.c1.m)
		clock2 := Time(e.c2.h, e.c2.m)
		got := clock1 == clock2
		if got != e.want {
			t.Log("Clock1:", clock1)
			t.Log("Clock2:", clock2)
			t.Logf("Clock1 == Clock2 is %t, want %t", got, e.want)
			if reflect.DeepEqual(clock1, clock2) {
				t.Log("(Hint: see comments in clock_test.go.)")
			}
			t.FailNow()
		}
	}
	t.Log(len(eqTests), "test cases")
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"os"

	"github.com/eddyzags/kafkactl/api/client"
	"github.com/eddyzags/kafkactl/brokers"

	"github.com/spf13/cobra"
)

var brokersStopCmd = &cobra.Command{
	Use:   "stop",
	Short: "Stop a broker",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			fmt.Fprintf(os.Stderr, "The broker id or expr is missing\n")
			os.Exit(1)
		}

		if apiURL == "" {
			fmt.Println("Cannot find kafka scheduler url. Using default: 0.0.0.0:7070")
		}

		c := client.NewClient(apiURL)

		if err := brokers.Stop(c, args[0]); err != nil {
			fmt.Fprintf(os.Stderr, "kafkactl: Unexpected error occured \"%v\"\n", err)
			os.Exit(1)
		}
	},
}

func init() {
	brokersCmd.AddCommand(brokersStopCmd)
}
<commit_msg>cmd: PersistentFlags added to brokerStopCmd<commit_after>// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"os"

	"github.com/eddyzags/kafkactl/api/client"
	"github.com/eddyzags/kafkactl/brokers"

	"github.com/spf13/cobra"
)

var (
	brokersStopForce   bool
	brokersStopTimeout string
)

var brokersStopCmd = &cobra.Command{
	Use:   "stop",
	Short: "Stop a broker",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			fmt.Fprintf(os.Stderr, "The broker id or expr is missing\n")
			os.Exit(1)
		}

		if apiURL == "" {
			fmt.Println("Cannot find kafka scheduler url. Using default: 0.0.0.0:7070")
		}

		c := client.NewClient(apiURL)

		if err := brokers.Stop(c, args[0], brokersStopTimeout, brokersStopForce); err != nil {
			fmt.Fprintf(os.Stderr, "kafkactl: Unexpected error occured \"%v\"\n", err)
			os.Exit(1)
		}
	},
}

func init() {
	brokersCmd.AddCommand(brokersStopCmd)

	brokersStopCmd.PersistentFlags().BoolVarP(&brokersStopForce, "force", "", false, "Forcibly stop")
	brokersStopCmd.PersistentFlags().StringVarP(&brokersStopTimeout, "timeout", "", "", "Timeout 30s, 1m, 1h). 0s - no timeout")
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"os"
	"os/signal"
	"runtime/pprof"
	"strings"
	"syscall"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/chihaya/chihaya/frontend/http"
	"github.com/chihaya/chihaya/frontend/udp"
	"github.com/chihaya/chihaya/middleware"
	"github.com/chihaya/chihaya/pkg/log"
	"github.com/chihaya/chihaya/pkg/prometheus"
	"github.com/chihaya/chihaya/pkg/stop"
	"github.com/chihaya/chihaya/storage"
)

// Run represents the state of a running instance of Chihaya.
type Run struct {
	configFilePath string
	peerStore      storage.PeerStore
	logic          *middleware.Logic
	sg             *stop.Group
}

// NewRun runs an instance of Chihaya.
func NewRun(configFilePath string) (*Run, error) {
	r := &Run{
		configFilePath: configFilePath,
	}

	return r, r.Start(nil)
}

// Start begins an instance of Chihaya.
// It is optional to provide an instance of the peer store to avoid the
// creation of a new one.
func (r *Run) Start(ps storage.PeerStore) error {
	configFile, err := ParseConfigFile(r.configFilePath)
	if err != nil {
		return errors.New("failed to read config: " + err.Error())
	}
	cfg := configFile.Chihaya

	r.sg = stop.NewGroup()

	log.Info("starting Prometheus server", log.Fields{"addr": cfg.PrometheusAddr})
	r.sg.Add(prometheus.NewServer(cfg.PrometheusAddr))

	if ps == nil {
		ps, err = storage.NewPeerStore(cfg.Storage.Name, cfg.Storage.Config)
		if err != nil {
			return errors.New("failed to create memory storage: " + err.Error())
		}
		log.Info("started storage", ps.LogFields())
	}
	r.peerStore = ps

	preHooks, postHooks, err := cfg.CreateHooks()
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}
	log.Info("starting middleware", log.Fields{
		"preHooks":  cfg.PreHooks.Names(),
		"postHooks": cfg.PostHooks.Names(),
	})
	r.logic = middleware.NewLogic(cfg.Config, r.peerStore, preHooks, postHooks)

	if cfg.HTTPConfig.Addr != "" {
		log.Info("starting HTTP frontend", cfg.HTTPConfig.LogFields())
		httpfe, err := http.NewFrontend(r.logic, cfg.HTTPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(httpfe)
	}

	if cfg.UDPConfig.Addr != "" {
		log.Info("starting UDP frontend", cfg.UDPConfig.LogFields())
		udpfe, err := udp.NewFrontend(r.logic, cfg.UDPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(udpfe)
	}

	return nil
}

func combineErrors(prefix string, errs []error) error {
	var errStrs []string
	for _, err := range errs {
		errStrs = append(errStrs, err.Error())
	}

	return errors.New(prefix + ": " + strings.Join(errStrs, "; "))
}

// Stop shuts down an instance of Chihaya.
func (r *Run) Stop(keepPeerStore bool) (storage.PeerStore, error) {
	log.Debug("stopping frontends and prometheus endpoint")
	if errs := r.sg.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down frontends", errs)
	}

	log.Debug("stopping logic")
	if errs := r.logic.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down middleware", errs)
	}

	if !keepPeerStore {
		log.Debug("stopping peer store")
		if err, closed := <-r.peerStore.Stop(); !closed {
			return nil, err
		}
		r.peerStore = nil
	}

	return r.peerStore, nil
}

// RunCmdFunc implements a Cobra command that runs an instance of Chihaya and
// handles reloading and shutdown via process signals.
func RunCmdFunc(cmd *cobra.Command, args []string) error {
	configFilePath, err := cmd.Flags().GetString("config")
	if err != nil {
		return err
	}

	r, err := NewRun(configFilePath)
	if err != nil {
		return err
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	reload := make(chan os.Signal)
	signal.Notify(reload, syscall.SIGUSR1)

	for {
		select {
		case <-reload:
			log.Info("reloading; received SIGUSR1")
			peerStore, err := r.Stop(true)
			if err != nil {
				return err
			}

			if err := r.Start(peerStore); err != nil {
				return err
			}
		case <-quit:
			log.Info("shutting down; received SIGINT/SIGTERM")
			if _, err := r.Stop(false); err != nil {
				return err
			}

			return nil
		}
	}
}

func main() {
	var rootCmd = &cobra.Command{
		Use:   "chihaya",
		Short: "BitTorrent Tracker",
		Long:  "A customizable, multi-protocol BitTorrent Tracker",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			jsonLog, err := cmd.Flags().GetBool("json")
			if err != nil {
				return err
			}
			if jsonLog {
				log.SetFormatter(&logrus.JSONFormatter{})
			}

			debugLog, err := cmd.Flags().GetBool("debug")
			if err != nil {
				return err
			}
			if debugLog {
				log.Info("enabling debug logging")
				log.SetDebug(true)
			}

			cpuProfilePath, err := cmd.Flags().GetString("cpuprofile")
			if err != nil {
				return err
			}
			if cpuProfilePath != "" {
				log.Info("enabling CPU profiling", log.Fields{"path": cpuProfilePath})
				f, err := os.Create(cpuProfilePath)
				if err != nil {
					return err
				}
				pprof.StartCPUProfile(f)
			}

			return nil
		},
		RunE: RunCmdFunc,
		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
			// StopCPUProfile() noops when not profiling.
			pprof.StopCPUProfile()
			return nil
		},
	}
	rootCmd.Flags().String("config", "/etc/chihaya.yaml", "location of configuration file")
	rootCmd.Flags().String("cpuprofile", "", "location to save a CPU profile")
	rootCmd.Flags().Bool("debug", false, "enable debug logging")
	rootCmd.Flags().Bool("json", false, "enable json logging")

	if err := rootCmd.Execute(); err != nil {
		log.Fatal("failed when executing root cobra command: " + err.Error())
	}
}
<commit_msg>cmd/chihaya: log which storage is in  use<commit_after>package main

import (
	"errors"
	"os"
	"os/signal"
	"runtime/pprof"
	"strings"
	"syscall"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/chihaya/chihaya/frontend/http"
	"github.com/chihaya/chihaya/frontend/udp"
	"github.com/chihaya/chihaya/middleware"
	"github.com/chihaya/chihaya/pkg/log"
	"github.com/chihaya/chihaya/pkg/prometheus"
	"github.com/chihaya/chihaya/pkg/stop"
	"github.com/chihaya/chihaya/storage"
)

// Run represents the state of a running instance of Chihaya.
type Run struct {
	configFilePath string
	peerStore      storage.PeerStore
	logic          *middleware.Logic
	sg             *stop.Group
}

// NewRun runs an instance of Chihaya.
func NewRun(configFilePath string) (*Run, error) {
	r := &Run{
		configFilePath: configFilePath,
	}

	return r, r.Start(nil)
}

// Start begins an instance of Chihaya.
// It is optional to provide an instance of the peer store to avoid the
// creation of a new one.
func (r *Run) Start(ps storage.PeerStore) error {
	configFile, err := ParseConfigFile(r.configFilePath)
	if err != nil {
		return errors.New("failed to read config: " + err.Error())
	}
	cfg := configFile.Chihaya

	r.sg = stop.NewGroup()

	log.Info("starting Prometheus server", log.Fields{"addr": cfg.PrometheusAddr})
	r.sg.Add(prometheus.NewServer(cfg.PrometheusAddr))

	if ps == nil {
		log.Info("starting storage", log.Fields{"name": cfg.Storage.Name})
		ps, err = storage.NewPeerStore(cfg.Storage.Name, cfg.Storage.Config)
		if err != nil {
			return errors.New("failed to create storage: " + err.Error())
		}
		log.Info("started storage", ps.LogFields())
	}
	r.peerStore = ps

	preHooks, postHooks, err := cfg.CreateHooks()
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}
	log.Info("starting middleware", log.Fields{
		"preHooks":  cfg.PreHooks.Names(),
		"postHooks": cfg.PostHooks.Names(),
	})
	r.logic = middleware.NewLogic(cfg.Config, r.peerStore, preHooks, postHooks)

	if cfg.HTTPConfig.Addr != "" {
		log.Info("starting HTTP frontend", cfg.HTTPConfig.LogFields())
		httpfe, err := http.NewFrontend(r.logic, cfg.HTTPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(httpfe)
	}

	if cfg.UDPConfig.Addr != "" {
		log.Info("starting UDP frontend", cfg.UDPConfig.LogFields())
		udpfe, err := udp.NewFrontend(r.logic, cfg.UDPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(udpfe)
	}

	return nil
}

func combineErrors(prefix string, errs []error) error {
	var errStrs []string
	for _, err := range errs {
		errStrs = append(errStrs, err.Error())
	}

	return errors.New(prefix + ": " + strings.Join(errStrs, "; "))
}

// Stop shuts down an instance of Chihaya.
func (r *Run) Stop(keepPeerStore bool) (storage.PeerStore, error) {
	log.Debug("stopping frontends and prometheus endpoint")
	if errs := r.sg.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down frontends", errs)
	}

	log.Debug("stopping logic")
	if errs := r.logic.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down middleware", errs)
	}

	if !keepPeerStore {
		log.Debug("stopping peer store")
		if err, closed := <-r.peerStore.Stop(); !closed {
			return nil, err
		}
		r.peerStore = nil
	}

	return r.peerStore, nil
}

// RunCmdFunc implements a Cobra command that runs an instance of Chihaya and
// handles reloading and shutdown via process signals.
func RunCmdFunc(cmd *cobra.Command, args []string) error {
	configFilePath, err := cmd.Flags().GetString("config")
	if err != nil {
		return err
	}

	r, err := NewRun(configFilePath)
	if err != nil {
		return err
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	reload := make(chan os.Signal)
	signal.Notify(reload, syscall.SIGUSR1)

	for {
		select {
		case <-reload:
			log.Info("reloading; received SIGUSR1")
			peerStore, err := r.Stop(true)
			if err != nil {
				return err
			}

			if err := r.Start(peerStore); err != nil {
				return err
			}
		case <-quit:
			log.Info("shutting down; received SIGINT/SIGTERM")
			if _, err := r.Stop(false); err != nil {
				return err
			}

			return nil
		}
	}
}

func main() {
	var rootCmd = &cobra.Command{
		Use:   "chihaya",
		Short: "BitTorrent Tracker",
		Long:  "A customizable, multi-protocol BitTorrent Tracker",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			jsonLog, err := cmd.Flags().GetBool("json")
			if err != nil {
				return err
			}
			if jsonLog {
				log.SetFormatter(&logrus.JSONFormatter{})
			}

			debugLog, err := cmd.Flags().GetBool("debug")
			if err != nil {
				return err
			}
			if debugLog {
				log.Info("enabling debug logging")
				log.SetDebug(true)
			}

			cpuProfilePath, err := cmd.Flags().GetString("cpuprofile")
			if err != nil {
				return err
			}
			if cpuProfilePath != "" {
				log.Info("enabling CPU profiling", log.Fields{"path": cpuProfilePath})
				f, err := os.Create(cpuProfilePath)
				if err != nil {
					return err
				}
				pprof.StartCPUProfile(f)
			}

			return nil
		},
		RunE: RunCmdFunc,
		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
			// StopCPUProfile() noops when not profiling.
			pprof.StopCPUProfile()
			return nil
		},
	}
	rootCmd.Flags().String("config", "/etc/chihaya.yaml", "location of configuration file")
	rootCmd.Flags().String("cpuprofile", "", "location to save a CPU profile")
	rootCmd.Flags().Bool("debug", false, "enable debug logging")
	rootCmd.Flags().Bool("json", false, "enable json logging")

	if err := rootCmd.Execute(); err != nil {
		log.Fatal("failed when executing root cobra command: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"time"

	"github.com/spf13/cobra"
)

// Version is inserted at build using --ldflags -X
var Version = "(unknown version)"

const (
	socketName = "/var/run/edgectl.socket"
	logfile    = "/tmp/edgectl.log"
	apiVersion = 1
)

var displayVersion = fmt.Sprintf("v%s (api v%d)", Version, apiVersion)

const failedToConnect = "Unable to connect to the daemon (See \"edgectl help daemon\")"

var daemonHelp = `The Edge Control Daemon is a long-lived background component that manages
connections and network state.

Launch the Edge Control Daemon:
    sudo edgectl daemon

Examine the Daemon's log output in
    ` + logfile + `
to troubleshoot problems.
`

// edgectl is the full path to the Edge Control binary
var edgectl string

var simpleTransport = &http.Transport{
	// #nosec G402
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	Proxy:           nil,
	DialContext: (&net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 1 * time.Second,
		DualStack: true,
	}).DialContext,
	DisableKeepAlives: true,
}

var hClient = &http.Client{
	Transport: simpleTransport,
	Timeout:   15 * time.Second,
}

func main() {
	// Figure out our executable and save it
	if executable, err := os.Executable(); err != nil {
		fmt.Fprintf(os.Stderr, "Internal error: %v", err)
		os.Exit(1)
	} else {
		edgectl = executable
	}

	rootCmd := getRootCommand()

	var cg []CmdGroup
	if DaemonWorks() {
		cg = []CmdGroup{
			CmdGroup{
				GroupName: "Management Commands",
				CmdNames:  []string{"install", "login", "license"},
			},
			CmdGroup{
				GroupName: "Development Commands",
				CmdNames:  []string{"status", "connect", "disconnect", "intercept"},
			},
			CmdGroup{
				GroupName: "Advanced Commands",
				CmdNames:  []string{"daemon", "pause", "resume", "quit"},
			},
			CmdGroup{
				GroupName: "Other Commands",
				CmdNames:  []string{"version", "help"},
			},
		}
	} else {
		cg = []CmdGroup{
			CmdGroup{
				GroupName: "Management Commands",
				CmdNames:  []string{"install", "login", "license"},
			},
			CmdGroup{
				GroupName: "Other Commands",
				CmdNames:  []string{"version", "help"},
			},
		}
	}

	usageFunc := NewCmdUsage(rootCmd, cg)
	rootCmd.SetUsageFunc(usageFunc)
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func getRootCommand() *cobra.Command {
	myName := "Edge Control"
	if !isServerRunning() {
		myName = "Edge Control (daemon unavailable)"
	}

	myHelp := myName + `
  https://www.getambassador.io/user-guide/install/
`

	rootCmd := &cobra.Command{
		Use:          "edgectl",
		Short:        myName,
		Long:         myHelp,
		SilenceUsage: true, // https://github.com/spf13/cobra/issues/340
	}
	_ = rootCmd.PersistentFlags().Bool(
		"no-report", false, "turn off anonymous crash reports and log submission on failure",
	)

	// Hidden/internal commands. These are called by Edge Control itself from
	// the correct context and execute in-place immediately.

	rootCmd.AddCommand(&cobra.Command{
		Use:    "daemon-foreground",
		Short:  "Launch Edge Control Daemon in the foreground (debug)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsDaemon(args[0], args[1])
		},
	})
	teleproxyCmd := &cobra.Command{
		Use:    "teleproxy",
		Short:  "Impersonate Teleproxy (for internal use)",
		Hidden: true,
	}
	teleproxyCmd.AddCommand(&cobra.Command{
		Use:    "intercept",
		Short:  "Impersonate Teleproxy Intercept (for internal use)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsTeleproxyIntercept(args[0], args[1])
		},
	})
	teleproxyCmd.AddCommand(&cobra.Command{
		Use:    "bridge",
		Short:  "Impersonate Teleproxy Bridge (for internal use)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsTeleproxyBridge(args[0], args[1])
		},
	})
	rootCmd.AddCommand(teleproxyCmd)

	// Client commands. These are never sent to the daemon.

	if DaemonWorks() {
		daemonCmd := &cobra.Command{
			Use:   "daemon",
			Short: "Launch Edge Control Daemon in the background (sudo)",
			Long:  daemonHelp,
			Args:  cobra.ExactArgs(0),
			RunE:  launchDaemon,
		}
		_ = daemonCmd.Flags().String(
			"dns", "",
			"DNS IP address to intercept. Defaults to the first nameserver listed in /etc/resolv.conf.",
		)
		_ = daemonCmd.Flags().String(
			"fallback", "",
			"DNS fallback, how non-cluster DNS queries are resolved. Defaults to Google DNS (8.8.8.8).",
		)
		rootCmd.AddCommand(daemonCmd)
	}
	loginCmd := &cobra.Command{
		Use:   "login [flags] HOSTNAME",
		Short: "Log in to the Ambassador Edge Policy Console",
		Args:  cobra.ExactArgs(1),
		RunE:  aesLogin,
	}
	_ = loginCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = loginCmd.Flags().StringP(
		"namespace", "n", "ambassador",
		"The Kubernetes namespace to use. Defaults to ambassador.",
	)
	_ = loginCmd.Flags().Bool("url", false, "Just show the URL (don't launch a browser)")
	_ = loginCmd.Flags().Bool("token", false, "Also display the login token")
	rootCmd.AddCommand(loginCmd)
	licenseCmd := &cobra.Command{
		Use:   "license [flags] LICENSE_KEY",
		Short: "Set or update the Ambassador Edge Stack license key",
		Args:  cobra.ExactArgs(1),
		RunE:  aesLicense,
	}
	_ = licenseCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = licenseCmd.Flags().StringP(
		"namespace", "n", "ambassador",
		"The Kubernetes namespace to use. Defaults to ambassador.",
	)
	rootCmd.AddCommand(licenseCmd)
	rootCmd.AddCommand(aesInstallCmd())

	// Daemon commands. These should be forwarded to the daemon.

	if DaemonWorks() {
		nilDaemon := &Daemon{}
		daemonCmd := nilDaemon.getRootCommand(nil, nil, nil)
		walkSubcommands(daemonCmd)
		rootCmd.AddCommand(daemonCmd.Commands()...)
		rootCmd.PersistentFlags().AddFlagSet(daemonCmd.PersistentFlags())
	} else {
		rootCmd.AddCommand(&cobra.Command{
			Use:   "version",
			Short: "Show program's version number and exit",
			Args:  cobra.ExactArgs(0),
			RunE: func(_ *cobra.Command, _ []string) error {
				fmt.Println("Client", displayVersion)
				fmt.Println("Daemon unavailable on this platform")
				return nil
			},
		})
	}

	rootCmd.InitDefaultHelpCmd()

	return rootCmd
}

func walkSubcommands(cmd *cobra.Command) {
	for _, subCmd := range cmd.Commands() {
		walkSubcommands(subCmd)
	}
	if cmd.RunE != nil {
		cmd.RunE = forwardToDaemon
	}
}

func forwardToDaemon(cmd *cobra.Command, _ []string) error {
	err := mainViaDaemon()
	if err != nil {
		// The version command is special because it must emit the client
		// version if the daemon is unavailable.
		if cmd.Use == "version" {
			fmt.Println("Client", displayVersion)
		}
		fmt.Println(failedToConnect)
	}
	return err
}
<commit_msg>Updating location of install docs<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"time"

	"github.com/spf13/cobra"
)

// Version is inserted at build using --ldflags -X
var Version = "(unknown version)"

const (
	socketName = "/var/run/edgectl.socket"
	logfile    = "/tmp/edgectl.log"
	apiVersion = 1
)

var displayVersion = fmt.Sprintf("v%s (api v%d)", Version, apiVersion)

const failedToConnect = "Unable to connect to the daemon (See \"edgectl help daemon\")"

var daemonHelp = `The Edge Control Daemon is a long-lived background component that manages
connections and network state.

Launch the Edge Control Daemon:
    sudo edgectl daemon

Examine the Daemon's log output in
    ` + logfile + `
to troubleshoot problems.
`

// edgectl is the full path to the Edge Control binary
var edgectl string

var simpleTransport = &http.Transport{
	// #nosec G402
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	Proxy:           nil,
	DialContext: (&net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 1 * time.Second,
		DualStack: true,
	}).DialContext,
	DisableKeepAlives: true,
}

var hClient = &http.Client{
	Transport: simpleTransport,
	Timeout:   15 * time.Second,
}

func main() {
	// Figure out our executable and save it
	if executable, err := os.Executable(); err != nil {
		fmt.Fprintf(os.Stderr, "Internal error: %v", err)
		os.Exit(1)
	} else {
		edgectl = executable
	}

	rootCmd := getRootCommand()

	var cg []CmdGroup
	if DaemonWorks() {
		cg = []CmdGroup{
			CmdGroup{
				GroupName: "Management Commands",
				CmdNames:  []string{"install", "login", "license"},
			},
			CmdGroup{
				GroupName: "Development Commands",
				CmdNames:  []string{"status", "connect", "disconnect", "intercept"},
			},
			CmdGroup{
				GroupName: "Advanced Commands",
				CmdNames:  []string{"daemon", "pause", "resume", "quit"},
			},
			CmdGroup{
				GroupName: "Other Commands",
				CmdNames:  []string{"version", "help"},
			},
		}
	} else {
		cg = []CmdGroup{
			CmdGroup{
				GroupName: "Management Commands",
				CmdNames:  []string{"install", "login", "license"},
			},
			CmdGroup{
				GroupName: "Other Commands",
				CmdNames:  []string{"version", "help"},
			},
		}
	}

	usageFunc := NewCmdUsage(rootCmd, cg)
	rootCmd.SetUsageFunc(usageFunc)
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func getRootCommand() *cobra.Command {
	myName := "Edge Control"
	if !isServerRunning() {
		myName = "Edge Control (daemon unavailable)"
	}

	myHelp := myName + `
  https://www.getambassador.io/docs/latest/topics/install/
`

	rootCmd := &cobra.Command{
		Use:          "edgectl",
		Short:        myName,
		Long:         myHelp,
		SilenceUsage: true, // https://github.com/spf13/cobra/issues/340
	}
	_ = rootCmd.PersistentFlags().Bool(
		"no-report", false, "turn off anonymous crash reports and log submission on failure",
	)

	// Hidden/internal commands. These are called by Edge Control itself from
	// the correct context and execute in-place immediately.

	rootCmd.AddCommand(&cobra.Command{
		Use:    "daemon-foreground",
		Short:  "Launch Edge Control Daemon in the foreground (debug)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsDaemon(args[0], args[1])
		},
	})
	teleproxyCmd := &cobra.Command{
		Use:    "teleproxy",
		Short:  "Impersonate Teleproxy (for internal use)",
		Hidden: true,
	}
	teleproxyCmd.AddCommand(&cobra.Command{
		Use:    "intercept",
		Short:  "Impersonate Teleproxy Intercept (for internal use)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsTeleproxyIntercept(args[0], args[1])
		},
	})
	teleproxyCmd.AddCommand(&cobra.Command{
		Use:    "bridge",
		Short:  "Impersonate Teleproxy Bridge (for internal use)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		RunE: func(_ *cobra.Command, args []string) error {
			return RunAsTeleproxyBridge(args[0], args[1])
		},
	})
	rootCmd.AddCommand(teleproxyCmd)

	// Client commands. These are never sent to the daemon.

	if DaemonWorks() {
		daemonCmd := &cobra.Command{
			Use:   "daemon",
			Short: "Launch Edge Control Daemon in the background (sudo)",
			Long:  daemonHelp,
			Args:  cobra.ExactArgs(0),
			RunE:  launchDaemon,
		}
		_ = daemonCmd.Flags().String(
			"dns", "",
			"DNS IP address to intercept. Defaults to the first nameserver listed in /etc/resolv.conf.",
		)
		_ = daemonCmd.Flags().String(
			"fallback", "",
			"DNS fallback, how non-cluster DNS queries are resolved. Defaults to Google DNS (8.8.8.8).",
		)
		rootCmd.AddCommand(daemonCmd)
	}
	loginCmd := &cobra.Command{
		Use:   "login [flags] HOSTNAME",
		Short: "Log in to the Ambassador Edge Policy Console",
		Args:  cobra.ExactArgs(1),
		RunE:  aesLogin,
	}
	_ = loginCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = loginCmd.Flags().StringP(
		"namespace", "n", "ambassador",
		"The Kubernetes namespace to use. Defaults to ambassador.",
	)
	_ = loginCmd.Flags().Bool("url", false, "Just show the URL (don't launch a browser)")
	_ = loginCmd.Flags().Bool("token", false, "Also display the login token")
	rootCmd.AddCommand(loginCmd)
	licenseCmd := &cobra.Command{
		Use:   "license [flags] LICENSE_KEY",
		Short: "Set or update the Ambassador Edge Stack license key",
		Args:  cobra.ExactArgs(1),
		RunE:  aesLicense,
	}
	_ = licenseCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = licenseCmd.Flags().StringP(
		"namespace", "n", "ambassador",
		"The Kubernetes namespace to use. Defaults to ambassador.",
	)
	rootCmd.AddCommand(licenseCmd)
	rootCmd.AddCommand(aesInstallCmd())

	// Daemon commands. These should be forwarded to the daemon.

	if DaemonWorks() {
		nilDaemon := &Daemon{}
		daemonCmd := nilDaemon.getRootCommand(nil, nil, nil)
		walkSubcommands(daemonCmd)
		rootCmd.AddCommand(daemonCmd.Commands()...)
		rootCmd.PersistentFlags().AddFlagSet(daemonCmd.PersistentFlags())
	} else {
		rootCmd.AddCommand(&cobra.Command{
			Use:   "version",
			Short: "Show program's version number and exit",
			Args:  cobra.ExactArgs(0),
			RunE: func(_ *cobra.Command, _ []string) error {
				fmt.Println("Client", displayVersion)
				fmt.Println("Daemon unavailable on this platform")
				return nil
			},
		})
	}

	rootCmd.InitDefaultHelpCmd()

	return rootCmd
}

func walkSubcommands(cmd *cobra.Command) {
	for _, subCmd := range cmd.Commands() {
		walkSubcommands(subCmd)
	}
	if cmd.RunE != nil {
		cmd.RunE = forwardToDaemon
	}
}

func forwardToDaemon(cmd *cobra.Command, _ []string) error {
	err := mainViaDaemon()
	if err != nil {
		// The version command is special because it must emit the client
		// version if the daemon is unavailable.
		if cmd.Use == "version" {
			fmt.Println("Client", displayVersion)
		}
		fmt.Println(failedToConnect)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/nightlyone/lockfile"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

// FIXME(nightlyone) Hook up passive monitoring solution here
func monitor(state, msg string) {}

// Avoid thundering herd problem on remote services used by this command. Spectrum will be 0, if this is not an issue.
func SpreadWait(spectrum time.Duration) {
	// Seed random generator with current process ID
	rand.Seed(int64(os.Getpid()))
	time.Sleep(time.Duration(rand.Int63n(int64(spectrum))))
}

// Ok states, that execution went well. Logs debug output and reports ok to monitoring.
func Ok() {
	log.Println("Ok")
	monitor("OK", "")
}

// NotAvailable states, that the command could not be started successfully. It might not be installed or has other problems.
func NotAvailable(err error) {
	s := fmt.Sprintln("Cannot start command: ", err)
	log.Println("FATAL:", s)
	monitor("UNKNOWN", s)
}

// TimedOut states, that the command took to long and reports failure to the monitoring.
func TimedOut() {
	s := "execution took to long"
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

// Busy states, that the command hangs and reports failure to the monitoring. Those tasks should be automatically killed, if it happens often.
func Busy() {
	s := "previous invokation of command still runs"
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

// Failed states, that the command didn't execute sucessfully and reports failure to the monitoring. Also Logs error output.
func Failed(err error) {
	s := fmt.Sprintln("Failed to execute: ", err)
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

var interval time.Duration = 1 * time.Minute
var spectrum time.Duration

func main() {
	var cmd *exec.Cmd

	// FIXME(mlafeldt) add command-line options for
	//                 - execution interval (optional)
	//                 - monitoring command (optional)
	//                 - kill or wait on busy state (optional)
	//                 - help
	log.SetFlags(0)
	flag.Parse()
	if flag.NArg() < 1 {
		log.Fatal("FATAL: no command to execute")
		return
	}

	command := flag.Arg(0)

	if spectrum >= interval {
		log.Fatal("FATAL: no spectrum >= interval, no time left for actual command execution")
		return
	}

	if spectrum == 0*time.Minute {
		spectrum = interval / 10
	}

	// FIXME(nightlyone) try two intervals instead of one?
	timeout := time.AfterFunc(interval, func() {
		TimedOut()
		if cmd != nil && cmd.Process != nil {
			cmd.Process.Kill()
		}
		os.Exit(0)
	})

	SpreadWait(spectrum)

	// Ensures that only one of these command runs concurrently on this machine.
	// Also cleans up stale locks of dead instances.
	base := filepath.Base(command)
	lock_dir := os.TempDir()
	os.Mkdir(filepath.Join(lock_dir, base), 0700)
	lock, _ := lockfile.New(filepath.Join(lock_dir, base, base+".lock"))
	if err := lock.TryLock(); err != nil {
		if err != lockfile.ErrBusy {
			log.Printf("ERROR: locking %s: reason: %v\n", lock, err)
		}
		timeout.Stop()
		Busy()
		return
	}
	defer lock.Unlock()

	// FIXME(nightlyone) capture at least cmd.Stderr, and optionally cmd.Stdout
	cmd = exec.Command(command, flag.Args()[1:]...)

	if err := cmd.Start(); err != nil {
		timeout.Stop()
		NotAvailable(err)
		return
	}

	if err := cmd.Wait(); err != nil {
		timeout.Stop()
		Failed(err)
	} else {
		timeout.Stop()
		Ok()
	}
}
<commit_msg>Fix some typos<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/nightlyone/lockfile"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

// FIXME(nightlyone) Hook up passive monitoring solution here
func monitor(state, msg string) {}

// Avoid thundering herd problem on remote services used by this command. Spectrum will be 0, if this is not an issue.
func SpreadWait(spectrum time.Duration) {
	// Seed random generator with current process ID
	rand.Seed(int64(os.Getpid()))
	time.Sleep(time.Duration(rand.Int63n(int64(spectrum))))
}

// Ok states, that execution went well. Logs debug output and reports ok to monitoring.
func Ok() {
	log.Println("Ok")
	monitor("OK", "")
}

// NotAvailable states, that the command could not be started successfully. It might not be installed or has other problems.
func NotAvailable(err error) {
	s := fmt.Sprintln("Cannot start command: ", err)
	log.Println("FATAL:", s)
	monitor("UNKNOWN", s)
}

// TimedOut states, that the command took too long and reports failure to the monitoring.
func TimedOut() {
	s := "execution took too long"
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

// Busy states, that the command hangs and reports failure to the monitoring. Those tasks should be automatically killed, if it happens often.
func Busy() {
	s := "previous invocation of command still running"
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

// Failed states, that the command didn't execute successfully and reports failure to the monitoring. Also Logs error output.
func Failed(err error) {
	s := fmt.Sprintln("Failed to execute: ", err)
	log.Println("FATAL:", s)
	monitor("CRITICAL", s)
}

var interval time.Duration = 1 * time.Minute
var spectrum time.Duration

func main() {
	var cmd *exec.Cmd

	// FIXME(mlafeldt) add command-line options for
	//                 - execution interval (optional)
	//                 - monitoring command (optional)
	//                 - kill or wait on busy state (optional)
	//                 - help
	log.SetFlags(0)
	flag.Parse()
	if flag.NArg() < 1 {
		log.Fatal("FATAL: no command to execute")
		return
	}

	command := flag.Arg(0)

	if spectrum >= interval {
		log.Fatal("FATAL: no spectrum >= interval, no time left for actual command execution")
		return
	}

	if spectrum == 0*time.Minute {
		spectrum = interval / 10
	}

	// FIXME(nightlyone) try two intervals instead of one?
	timeout := time.AfterFunc(interval, func() {
		TimedOut()
		if cmd != nil && cmd.Process != nil {
			cmd.Process.Kill()
		}
		os.Exit(0)
	})

	SpreadWait(spectrum)

	// Ensures that only one of these command runs concurrently on this machine.
	// Also cleans up stale locks of dead instances.
	base := filepath.Base(command)
	lock_dir := os.TempDir()
	os.Mkdir(filepath.Join(lock_dir, base), 0700)
	lock, _ := lockfile.New(filepath.Join(lock_dir, base, base+".lock"))
	if err := lock.TryLock(); err != nil {
		if err != lockfile.ErrBusy {
			log.Printf("ERROR: locking %s: reason: %v\n", lock, err)
		}
		timeout.Stop()
		Busy()
		return
	}
	defer lock.Unlock()

	// FIXME(nightlyone) capture at least cmd.Stderr, and optionally cmd.Stdout
	cmd = exec.Command(command, flag.Args()[1:]...)

	if err := cmd.Start(); err != nil {
		timeout.Stop()
		NotAvailable(err)
		return
	}

	if err := cmd.Wait(); err != nil {
		timeout.Stop()
		Failed(err)
	} else {
		timeout.Stop()
		Ok()
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 Slotix s.r.o. <dm@slotix.sk>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

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

	"github.com/slotix/dataflowkit/fetch"
	"github.com/slotix/dataflowkit/healthcheck"

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

//flag vars
var (
	//VERSION               string // VERSION is set during build
	DFKFetch          string //Fetch service address
	fetchProxy        string //Proxy address http://username:password@proxy-host:port
	chrome            string
	chromeTrace       bool
	chromeScriptsPath string

	storageType     string
	ignoreCacheInfo bool
	diskvBaseDir    string

	cassandraHost string

	excludeResources []string
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "dataflowkit",
	Short: "Dataflow Kit html fetcher",
	Long:  `Dataflow Kit fetch service retrieves html pages from websites and passes content to DFK parser service.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Checking services ... ")
		services := []healthcheck.Checker{
			healthcheck.ChromeConn{
				Host: chrome,
			},
		}
		if storageType == "Cassandra" {
			services = append(services, healthcheck.CassandraConn{
				Host: cassandraHost})
		}
		status := healthcheck.CheckServices(services...)
		allAlive := true

		for k, v := range status {
			fmt.Printf("%s: %s\n", k, v)
			if v != "Ok" {
				allAlive = false
			}
		}
		if allAlive {
			fmt.Printf("Storage %s\n", storageType)
			fetchServer := viper.GetString("DFK_FETCH")
			serverCfg := fetch.Config{
				Host: fetchServer, //"localhost:5000",
			}
			htmlServer := fetch.Start(serverCfg)
			defer htmlServer.Stop()

			sigChan := make(chan os.Signal, 1)
			signal.Notify(sigChan, os.Interrupt)
			<-sigChan

			fmt.Println("main : shutting down")
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(version string) {
	VERSION = version
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	//flags and configuration settings. They are global for the application.

	RootCmd.Flags().StringVarP(&DFKFetch, "DFK_FETCH", "f", "127.0.0.1:8000", "HTTP listen address")
	RootCmd.Flags().StringVarP(&chrome, "CHROME", "c", "http://127.0.0.1:9222", "Headless Chrome URL address. It is used for fetching JS driven web pages")
	RootCmd.Flags().BoolVarP(&chromeTrace, "CHROME_TRACE", "", false, "Traces Headless Chrome requests/responses.")
	RootCmd.Flags().StringVarP(&chromeScriptsPath, "CHROME_SCRIPTS", "", "./chrome", "Path to chrome scripts")

	RootCmd.Flags().StringVarP(&fetchProxy, "PROXY", "p", "", "Proxy address http://username:password@proxy-host:port")

	//set here default type of storage
	RootCmd.Flags().StringVarP(&storageType, "STORAGE_TYPE", "", "Diskv", "Storage type. Types: Diskv, Cassandra")
	RootCmd.Flags().StringVarP(&diskvBaseDir, "DISKV_BASE_DIR", "", "diskv", "diskv base directory for storing fetch results")
	RootCmd.Flags().StringVarP(&cassandraHost, "CASSANDRA", "", "127.0.0.1", "Cassandra host address")

	RootCmd.Flags().StringSliceVar(&excludeResources, "EXCLUDERES", nil, "Exclude resources from fetch.")

	if os.Getenv("DFK_FETCH") != "" {
		viper.Set("DFK_FETCH", os.Getenv("DFK_FETCH"))
	} else {
		viper.BindPFlag("DFK_FETCH", RootCmd.Flags().Lookup("DFK_FETCH"))
		//os.Setenv("DFK_FETCH", DFKFetch)
	}

	if os.Getenv("DISKV_BASE_DIR") != "" {
		//viper.BindEnv("DISKV_BASE_DIR")
		viper.Set("DISKV_BASE_DIR", os.Getenv("DISKV_BASE_DIR"))
	} else {
		viper.BindPFlag("DISKV_BASE_DIR", RootCmd.Flags().Lookup("DISKV_BASE_DIR"))
	}

	viper.BindPFlag("PROXY", RootCmd.Flags().Lookup("PROXY"))
	viper.BindPFlag("CHROME", RootCmd.Flags().Lookup("CHROME"))
	viper.BindPFlag("CHROME_TRACE", RootCmd.Flags().Lookup("CHROME_TRACE"))
	viper.BindPFlag("CHROME_SCRIPTS", RootCmd.Flags().Lookup("CHROME_SCRIPTS"))
	viper.BindPFlag("STORAGE_TYPE", RootCmd.Flags().Lookup("STORAGE_TYPE"))
	viper.BindPFlag("DISKV_BASE_DIR", RootCmd.Flags().Lookup("DISKV_BASE_DIR"))
	viper.BindPFlag("CASSANDRA", RootCmd.Flags().Lookup("CASSANDRA"))

	viper.BindPFlag("EXCLUDERES", RootCmd.Flags().Lookup("EXCLUDERES"))

	path := filepath.Join(viper.GetString("CHROME_SCRIPTS"), "exclude.csv")
	dat, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
	}
	excludeResources = strings.Split(string(dat), ",")
}
<commit_msg>get CHROME and CASSANDRA variables from env initially<commit_after>// Copyright © 2017 Slotix s.r.o. <dm@slotix.sk>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

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

	"github.com/slotix/dataflowkit/fetch"
	"github.com/slotix/dataflowkit/healthcheck"

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

//flag vars
var (
	//VERSION               string // VERSION is set during build
	DFKFetch          string //Fetch service address
	fetchProxy        string //Proxy address http://username:password@proxy-host:port
	chrome            string
	chromeTrace       bool
	chromeScriptsPath string

	storageType     string
	ignoreCacheInfo bool
	diskvBaseDir    string

	cassandraHost string

	excludeResources []string
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "dataflowkit",
	Short: "Dataflow Kit html fetcher",
	Long:  `Dataflow Kit fetch service retrieves html pages from websites and passes content to DFK parser service.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Checking services ... ")
		services := []healthcheck.Checker{
			healthcheck.ChromeConn{
				Host: chrome,
			},
		}
		if storageType == "Cassandra" {
			services = append(services, healthcheck.CassandraConn{
				Host: cassandraHost})
		}
		status := healthcheck.CheckServices(services...)
		allAlive := true

		for k, v := range status {
			fmt.Printf("%s: %s\n", k, v)
			if v != "Ok" {
				allAlive = false
			}
		}
		if allAlive {
			fmt.Printf("Storage %s\n", storageType)
			fetchServer := viper.GetString("DFK_FETCH")
			serverCfg := fetch.Config{
				Host: fetchServer, //"localhost:5000",
			}
			htmlServer := fetch.Start(serverCfg)
			defer htmlServer.Stop()

			sigChan := make(chan os.Signal, 1)
			signal.Notify(sigChan, os.Interrupt)
			<-sigChan

			fmt.Println("main : shutting down")
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(version string) {
	VERSION = version
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	//flags and configuration settings. They are global for the application.

	RootCmd.Flags().StringVarP(&DFKFetch, "DFK_FETCH", "f", "127.0.0.1:8000", "HTTP listen address")
	RootCmd.Flags().StringVarP(&chrome, "CHROME", "c", "http://127.0.0.1:9222", "Headless Chrome URL address. It is used for fetching JS driven web pages")
	RootCmd.Flags().BoolVarP(&chromeTrace, "CHROME_TRACE", "", false, "Traces Headless Chrome requests/responses.")
	RootCmd.Flags().StringVarP(&chromeScriptsPath, "CHROME_SCRIPTS", "", "./chrome", "Path to chrome scripts")

	RootCmd.Flags().StringVarP(&fetchProxy, "PROXY", "p", "", "Proxy address http://username:password@proxy-host:port")

	//set here default type of storage
	RootCmd.Flags().StringVarP(&storageType, "STORAGE_TYPE", "", "Diskv", "Storage type. Types: Diskv, Cassandra")
	RootCmd.Flags().StringVarP(&diskvBaseDir, "DISKV_BASE_DIR", "", "diskv", "diskv base directory for storing fetch results")
	RootCmd.Flags().StringVarP(&cassandraHost, "CASSANDRA", "", "127.0.0.1", "Cassandra host address")

	RootCmd.Flags().StringSliceVar(&excludeResources, "EXCLUDERES", nil, "Exclude resources from fetch.")

	if os.Getenv("DFK_FETCH") != "" {
		viper.Set("DFK_FETCH", os.Getenv("DFK_FETCH"))
	} else {
		viper.BindPFlag("DFK_FETCH", RootCmd.Flags().Lookup("DFK_FETCH"))
		//os.Setenv("DFK_FETCH", DFKFetch)
	}

	if os.Getenv("CHROME") != "" {
		viper.Set("CHROME", os.Getenv("CHROME"))
	} else {
		viper.BindPFlag("CHROME", RootCmd.Flags().Lookup("CHROME"))
	}

	if os.Getenv("CASSANDRA") != "" {
		viper.Set("CASSANDRA", os.Getenv("CASSANDRA"))
	} else {
		viper.BindPFlag("CASSANDRA", RootCmd.Flags().Lookup("CASSANDRA"))
	}

	if os.Getenv("DISKV_BASE_DIR") != "" {
		//viper.BindEnv("DISKV_BASE_DIR")
		viper.Set("DISKV_BASE_DIR", os.Getenv("DISKV_BASE_DIR"))
	} else {
		viper.BindPFlag("DISKV_BASE_DIR", RootCmd.Flags().Lookup("DISKV_BASE_DIR"))
	}

	viper.BindPFlag("PROXY", RootCmd.Flags().Lookup("PROXY"))
	viper.BindPFlag("CHROME", RootCmd.Flags().Lookup("CHROME"))
	viper.BindPFlag("CHROME_TRACE", RootCmd.Flags().Lookup("CHROME_TRACE"))
	viper.BindPFlag("CHROME_SCRIPTS", RootCmd.Flags().Lookup("CHROME_SCRIPTS"))
	viper.BindPFlag("STORAGE_TYPE", RootCmd.Flags().Lookup("STORAGE_TYPE"))
	viper.BindPFlag("DISKV_BASE_DIR", RootCmd.Flags().Lookup("DISKV_BASE_DIR"))
	viper.BindPFlag("CASSANDRA", RootCmd.Flags().Lookup("CASSANDRA"))

	viper.BindPFlag("EXCLUDERES", RootCmd.Flags().Lookup("EXCLUDERES"))

	path := filepath.Join(viper.GetString("CHROME_SCRIPTS"), "exclude.csv")
	dat, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
	}
	excludeResources = strings.Split(string(dat), ",")
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"os"

	"github.com/segmentio/conf"
)

type updateConfig struct {
	Verbose bool `conf:"v" help:"Enable verbose mode."`
}

func update(ctx context.Context, args []string) {
	c := initConfig{}

	ld := conf.Loader{
		Name:    "goapp update",
		Args:    args,
		Usage:   "[options...]",
		Sources: []conf.Source{conf.NewEnvSource("GOAPP", os.Environ()...)},
	}

	_, args = conf.LoadWith(&c, ld)
	verbose = c.Verbose

	cmd := []string{
		"go", "get", "-u",
	}

	if verbose {
		cmd = append(cmd, "-v")
	}

	cmd = append(cmd, "github.com/maxence-charriere/app/cmd/goapp")

	log("updating to the latest version")
	if err := execute(ctx, cmd[0], cmd[1:]...); err != nil {
		fail("%s", err)
	}

	success("update succeeded")
}
<commit_msg>Update update.go<commit_after>package main

import (
	"context"
	"os"

	"github.com/segmentio/conf"
)

type updateConfig struct {
	Verbose bool `conf:"v" help:"Enable verbose mode."`
}

func update(ctx context.Context, args []string) {
	c := initConfig{}

	ld := conf.Loader{
		Name:    "goapp update",
		Args:    args,
		Usage:   "[options...]",
		Sources: []conf.Source{conf.NewEnvSource("GOAPP", os.Environ()...)},
	}

	conf.LoadWith(&c, ld)
	verbose = c.Verbose

	cmd := []string{
		"go", "get", "-u",
	}

	if verbose {
		cmd = append(cmd, "-v")
	}

	cmd = append(cmd, "github.com/maxence-charriere/app/cmd/goapp")

	log("updating to the latest version")
	if err := execute(ctx, cmd[0], cmd[1:]...); err != nil {
		fail("%s", err)
	}

	success("update succeeded")
}
<|endoftext|>


<commit_before>package main

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

	"github.com/docopt/docopt-go"
	"github.com/hooklift/iso9660"
)

// Version holds the CLI version and is set in compilation time.
var Version string

func main() {
	usage := `ISO9660 extractor.
Usage:
  iso9660 <image-path> <destination-path>
  iso9660 -h | --help
  iso9660 --version
`

	args, err := docopt.Parse(usage, nil, true, Version, false)
	if err != nil {
		panic(err)
	}

	file, err := os.Open(args["<image-path>"].(string))
	if err != nil {
		panic(err)
	}

	r, err := iso9660.NewReader(file)
	if err != nil {
		panic(err)
	}

	destPath := args["<destination-path>"].(string)
	if destPath == "" {
		destPath = "."
	}

	for {
		f, err := r.Next()
		if err == io.EOF {
			break
		}

		if err != nil {
			panic(err)
		}

		fp := filepath.Join(destPath, f.Name())
		if f.IsDir() {
			if err := os.MkdirAll(fp, f.Mode()); err != nil {
				panic(err)
			}
			continue
		}

		parentDir, _ := filepath.Split(fp)
		if err := os.MkdirAll(parentDir, f.Mode()); err != nil {
			panic(err)
		}

		fmt.Printf("Extracting %s...\n", fp)

		freader := f.Sys().(io.Reader)
		ff, err := os.OpenFile(fp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, f.Mode())
		if err != nil {
			panic(err)
		}
		defer func() {
			if err := ff.Close(); err != nil {
				panic(err)
			}
		}()

		if _, err := io.Copy(ff, freader); err != nil {
			panic(err)
		}
	}
}
<commit_msg>Update main.go to close files immediately after copy.<commit_after>package main

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

	"github.com/docopt/docopt-go"
	"github.com/hooklift/iso9660"
)

// Version holds the CLI version and is set in compilation time.
var Version string

func main() {
	usage := `ISO9660 extractor.
Usage:
  iso9660 <image-path> <destination-path>
  iso9660 -h | --help
  iso9660 --version
`

	args, err := docopt.Parse(usage, nil, true, Version, false)
	if err != nil {
		panic(err)
	}

	file, err := os.Open(args["<image-path>"].(string))
	if err != nil {
		panic(err)
	}

	r, err := iso9660.NewReader(file)
	if err != nil {
		panic(err)
	}

	destPath := args["<destination-path>"].(string)
	if destPath == "" {
		destPath = "."
	}

	for {
		f, err := r.Next()
		if err == io.EOF {
			break
		}

		if err != nil {
			panic(err)
		}

		fp := filepath.Join(destPath, f.Name())
		if f.IsDir() {
			if err := os.MkdirAll(fp, f.Mode()); err != nil {
				panic(err)
			}
			continue
		}

		parentDir, _ := filepath.Split(fp)
		if err := os.MkdirAll(parentDir, f.Mode()); err != nil {
			panic(err)
		}

		fmt.Printf("Extracting %s...\n", fp)

		freader := f.Sys().(io.Reader)
		ff, err := os.OpenFile(fp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, f.Mode())
		if err != nil {
			panic(err)
		}

		if _, err := io.Copy(ff, freader); err != nil {
			panic(err)
		}

		if err := ff.Close(); err != nil {
			panic(err)
		}

	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2022 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	"net/http"
	"time"

	"github.com/charmbracelet/bubbles/table"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/pkg/console"
)

var licenseInfoCmd = cli.Command{
	Name:         "info",
	Usage:        "display license information",
	OnUsageError: onUsageError,
	Action:       mainLicenseInfo,
	Before:       setGlobalsFromContext,
	Flags:        append(supportGlobalFlags, subnetCommonFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} ALIAS

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}

EXAMPLES:
  1. Display license configuration for cluster with alias 'play'
     {{.Prompt}} {{.HelpName}} play
`,
}

const (
	licInfoMsgTag   = "licenseInfoMessage"
	licInfoErrTag   = "licenseInfoError"
	licInfoFieldTag = "licenseInfoField"
	licInfoValTag   = "licenseValueField"
)

type liTableModel struct {
	table table.Model
}

func (m liTableModel) Init() tea.Cmd { return nil }

func (m liTableModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	m.table, cmd = m.table.Update(msg)
	return m, cmd
}

func (m liTableModel) View() string {
	return lipgloss.NewStyle().
		BorderStyle(lipgloss.NormalBorder()).
		BorderForeground(lipgloss.Color("240")).Render(m.table.View())
}

type licInfoMessage struct {
	Status string  `json:"status"`
	Info   licInfo `json:"info,omitempty"`
	Error  string  `json:"error,omitempty"`
}

type licInfo struct {
	LicenseID    string     `json:"license_id,omitempty"`    // Unique ID of the license
	Organization string     `json:"org,omitempty"`           // Subnet organization name
	Plan         string     `json:"plan,omitempty"`          // Subnet plan
	IssuedAt     *time.Time `json:"issued_at,omitempty"`     // Time of license issue
	ExpiresAt    *time.Time `json:"expires_at,omitempty"`    // Time of license expiry
	DeploymentID string     `json:"deployment_id,omitempty"` // Cluster deployment ID
	Message      string     `json:"message,omitempty"`       // Message to be displayed
	APIKey       string     `json:"api_key,omitempty"`       // API Key of the org account
}

func licInfoField(s string) string {
	return console.Colorize(licInfoFieldTag, s)
}

func licInfoVal(s string) string {
	return console.Colorize(licInfoValTag, s)
}

func licInfoMsg(s string) string {
	return console.Colorize(licInfoMsgTag, s)
}

func licInfoErr(s string) string {
	return console.Colorize(licInfoErrTag, s)
}

// String colorized license info
func (li licInfoMessage) String() string {
	if len(li.Error) > 0 {
		return licInfoErr(li.Error)
	}

	if len(li.Info.Message) > 0 {
		return licInfoMsg(li.Info.Message)
	}

	return getLicInfoStr(li.Info)
}

// JSON jsonified license info
func (li licInfoMessage) JSON() string {
	jsonBytes, e := json.MarshalIndent(li, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(jsonBytes)
}

func getLicInfoStr(li licInfo) string {
	columns := []table.Column{
		{Title: "License", Width: 20},
		{Title: "", Width: 45},
	}

	rows := []table.Row{
		{licInfoField("Organization"), licInfoVal(li.Organization)},
		{licInfoField("Plan"), licInfoVal(li.Plan)},
		{licInfoField("Issued"), licInfoVal(li.IssuedAt.Format(http.TimeFormat))},
		{licInfoField("Expires"), licInfoVal(li.ExpiresAt.Format(http.TimeFormat))},
	}

	if len(li.LicenseID) > 0 {
		rows = append(rows, table.Row{licInfoField("License ID"), licInfoVal(li.LicenseID)})
	}
	if len(li.DeploymentID) > 0 {
		rows = append(rows, table.Row{licInfoField("Deployment ID"), licInfoVal(li.DeploymentID)})
	}
	if len(li.APIKey) > 0 {
		rows = append(rows, table.Row{licInfoField("API Key"), licInfoVal(li.APIKey)})
	}

	t := table.New(
		table.WithColumns(columns),
		table.WithRows(rows),
		table.WithFocused(true),
		table.WithHeight(len(rows)),
	)

	s := table.DefaultStyles()
	s.Header = s.Header.
		BorderStyle(lipgloss.NormalBorder()).
		BorderForeground(lipgloss.Color("240")).
		BorderBottom(true).
		Bold(false)
	s.Selected = s.Selected.Bold(false)

	t.SetStyles(s)
	p := tea.NewProgram(liTableModel{t})
	go p.Start()
	time.Sleep(time.Second) // allow time for the table to be rendered
	p.Quit()

	return ""
}

func getAGPLMessage() string {
	return `License: GNU AGPL v3 <https://www.gnu.org/licenses/agpl-3.0.txt>
If you are distributing or hosting MinIO along with your proprietary application as combined works, you may require a commercial license included in the Standard and Enterprise subscription plans. (https://min.io/signup?ref=mc)`
}

func initLicInfoColors() {
	console.SetColor(licInfoMsgTag, color.New(color.FgGreen, color.Bold))
	console.SetColor(licInfoErrTag, color.New(color.FgRed, color.Bold))
	console.SetColor(licInfoFieldTag, color.New(color.FgCyan))
	console.SetColor(licInfoValTag, color.New(color.FgWhite))
}

func mainLicenseInfo(ctx *cli.Context) error {
	if len(ctx.Args()) != 1 {
		showCommandHelpAndExit(ctx, "info", 1) // last argument is exit code
	}

	initLicInfoColors()

	aliasedURL := ctx.Args().Get(0)
	alias, _ := initSubnetConnectivity(ctx, aliasedURL, false)

	apiKey, lic, e := getSubnetCreds(alias)
	fatalIf(probe.NewError(e), "Error in checking cluster registration status")

	var lim licInfoMessage
	if len(lic) > 0 {
		lim = getLicInfoMsg(lic)
	} else if len(apiKey) > 0 {
		lim = licInfoMessage{
			Status: "success",
			Info: licInfo{
				Message: fmt.Sprintf("%s is registered with SUBNET. License info not available.", alias),
			},
		}
	} else {
		// Not registered. Default to AGPLv3
		lim = licInfoMessage{
			Status: "success",
			Info: licInfo{
				Plan:    "AGPLv3",
				Message: getAGPLMessage(),
			},
		}
	}

	printMsg(lim)
	return nil
}

func getLicInfoMsg(lic string) licInfoMessage {
	li, e := parseLicense(lic)
	if e != nil {
		return licErrMsg(e)
	}
	return licInfoMessage{
		Status: "success",
		Info: licInfo{
			LicenseID:    li.LicenseID,
			Organization: li.Organization,
			Plan:         li.Plan,
			IssuedAt:     &li.IssuedAt,
			ExpiresAt:    &li.ExpiresAt,
			DeploymentID: li.DeploymentID,
			APIKey:       li.APIKey,
		},
	}
}

func licErrMsg(e error) licInfoMessage {
	return licInfoMessage{
		Status: "error",
		Error:  e.Error(),
	}
}
<commit_msg>avoid unnecessary tea program for 'license info' (#4306)<commit_after>// Copyright (c) 2015-2022 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	"net/http"
	"time"

	"github.com/charmbracelet/bubbles/table"
	"github.com/charmbracelet/lipgloss"
	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/pkg/console"
)

var licenseInfoCmd = cli.Command{
	Name:         "info",
	Usage:        "display license information",
	OnUsageError: onUsageError,
	Action:       mainLicenseInfo,
	Before:       setGlobalsFromContext,
	Flags:        append(supportGlobalFlags, subnetCommonFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} ALIAS

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}

EXAMPLES:
  1. Display license configuration for cluster with alias 'play'
     {{.Prompt}} {{.HelpName}} play
`,
}

const (
	licInfoMsgTag   = "licenseInfoMessage"
	licInfoErrTag   = "licenseInfoError"
	licInfoFieldTag = "licenseInfoField"
	licInfoValTag   = "licenseValueField"
)

type licInfoMessage struct {
	Status string  `json:"status"`
	Info   licInfo `json:"info,omitempty"`
	Error  string  `json:"error,omitempty"`
}

type licInfo struct {
	LicenseID    string     `json:"license_id,omitempty"`    // Unique ID of the license
	Organization string     `json:"org,omitempty"`           // Subnet organization name
	Plan         string     `json:"plan,omitempty"`          // Subnet plan
	IssuedAt     *time.Time `json:"issued_at,omitempty"`     // Time of license issue
	ExpiresAt    *time.Time `json:"expires_at,omitempty"`    // Time of license expiry
	DeploymentID string     `json:"deployment_id,omitempty"` // Cluster deployment ID
	Message      string     `json:"message,omitempty"`       // Message to be displayed
	APIKey       string     `json:"api_key,omitempty"`       // API Key of the org account
}

func licInfoField(s string) string {
	return console.Colorize(licInfoFieldTag, s)
}

func licInfoVal(s string) string {
	return console.Colorize(licInfoValTag, s)
}

func licInfoMsg(s string) string {
	return console.Colorize(licInfoMsgTag, s)
}

func licInfoErr(s string) string {
	return console.Colorize(licInfoErrTag, s)
}

// String colorized license info
func (li licInfoMessage) String() string {
	if len(li.Error) > 0 {
		return licInfoErr(li.Error)
	}

	if len(li.Info.Message) > 0 {
		return licInfoMsg(li.Info.Message)
	}

	return getLicInfoStr(li.Info)
}

// JSON jsonified license info
func (li licInfoMessage) JSON() string {
	jsonBytes, e := json.MarshalIndent(li, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(jsonBytes)
}

func getLicInfoStr(li licInfo) string {
	columns := []table.Column{
		{Title: "License", Width: 20},
		{Title: "", Width: 45},
	}

	rows := []table.Row{
		{licInfoField("Organization"), licInfoVal(li.Organization)},
		{licInfoField("Plan"), licInfoVal(li.Plan)},
		{licInfoField("Issued"), licInfoVal(li.IssuedAt.Format(http.TimeFormat))},
		{licInfoField("Expires"), licInfoVal(li.ExpiresAt.Format(http.TimeFormat))},
	}

	if len(li.LicenseID) > 0 {
		rows = append(rows, table.Row{licInfoField("License ID"), licInfoVal(li.LicenseID)})
	}
	if len(li.DeploymentID) > 0 {
		rows = append(rows, table.Row{licInfoField("Deployment ID"), licInfoVal(li.DeploymentID)})
	}
	if len(li.APIKey) > 0 {
		rows = append(rows, table.Row{licInfoField("API Key"), licInfoVal(li.APIKey)})
	}

	t := table.New(
		table.WithColumns(columns),
		table.WithRows(rows),
		table.WithFocused(true),
		table.WithHeight(len(rows)),
	)

	s := table.DefaultStyles()
	s.Header = s.Header.
		BorderStyle(lipgloss.NormalBorder()).
		BorderForeground(lipgloss.Color("240")).
		BorderBottom(true).
		Bold(false)
	s.Selected = s.Selected.Bold(false)
	t.SetStyles(s)

	return lipgloss.NewStyle().
		BorderStyle(lipgloss.NormalBorder()).
		BorderForeground(lipgloss.Color("240")).Render(t.View())
}

func getAGPLMessage() string {
	return `License: GNU AGPL v3 <https://www.gnu.org/licenses/agpl-3.0.txt>
If you are distributing or hosting MinIO along with your proprietary application as combined works, you may require a commercial license included in the Standard and Enterprise subscription plans. (https://min.io/signup?ref=mc)`
}

func initLicInfoColors() {
	console.SetColor(licInfoMsgTag, color.New(color.FgGreen, color.Bold))
	console.SetColor(licInfoErrTag, color.New(color.FgRed, color.Bold))
	console.SetColor(licInfoFieldTag, color.New(color.FgCyan))
	console.SetColor(licInfoValTag, color.New(color.FgWhite))
}

func mainLicenseInfo(ctx *cli.Context) error {
	if len(ctx.Args()) != 1 {
		showCommandHelpAndExit(ctx, "info", 1) // last argument is exit code
	}

	initLicInfoColors()

	aliasedURL := ctx.Args().Get(0)
	alias, _ := initSubnetConnectivity(ctx, aliasedURL, false)

	apiKey, lic, e := getSubnetCreds(alias)
	fatalIf(probe.NewError(e), "Error in checking cluster registration status")

	var lim licInfoMessage
	if len(lic) > 0 {
		lim = getLicInfoMsg(lic)
	} else if len(apiKey) > 0 {
		lim = licInfoMessage{
			Status: "success",
			Info: licInfo{
				Message: fmt.Sprintf("%s is registered with SUBNET. License info not available.", alias),
			},
		}
	} else {
		// Not registered. Default to AGPLv3
		lim = licInfoMessage{
			Status: "success",
			Info: licInfo{
				Plan:    "AGPLv3",
				Message: getAGPLMessage(),
			},
		}
	}

	printMsg(lim)
	return nil
}

func getLicInfoMsg(lic string) licInfoMessage {
	li, e := parseLicense(lic)
	if e != nil {
		return licErrMsg(e)
	}
	return licInfoMessage{
		Status: "success",
		Info: licInfo{
			LicenseID:    li.LicenseID,
			Organization: li.Organization,
			Plan:         li.Plan,
			IssuedAt:     &li.IssuedAt,
			ExpiresAt:    &li.ExpiresAt,
			DeploymentID: li.DeploymentID,
			APIKey:       li.APIKey,
		},
	}
}

func licErrMsg(e error) licInfoMessage {
	return licInfoMessage{
		Status: "error",
		Error:  e.Error(),
	}
}
<|endoftext|>


<commit_before>package parse

import (
	"encoding/json"
	"fmt"

	"github.com/tsaikd/KDGoLib/cliutil/cmder"
	"github.com/tsaikd/go-raml-parser/parser"
	"github.com/tsaikd/go-raml-parser/parser/parserConfig"
	"gopkg.in/urfave/cli.v2"
)

// Module info
var Module = cmder.NewModule("parse").
	SetUsage("Parse RAML file and show API in json format").
	AddFlag(
		&cli.StringFlag{
			Name:        "f",
			Aliases:     []string{"ramlfile"},
			Value:       "api.raml",
			Usage:       "Source RAML file",
			Destination: &ramlFile,
		},
		&cli.BoolFlag{
			Name:        "checkRAMLVersion",
			Usage:       "Check RAML Version",
			Destination: &checkRAMLVersion,
		},
	).
	SetAction(action)

var ramlFile string
var checkRAMLVersion bool

func action(c *cli.Context) (err error) {
	parser := parser.NewParser()

	if err = parser.Config(parserConfig.CheckRAMLVersion, checkRAMLVersion); err != nil {
		return
	}

	rootdoc, err := parser.ParseFile(ramlFile)
	if err != nil {
		return
	}

	jsondata, err := json.MarshalIndent(rootdoc, "", "  ")
	if err != nil {
		return
	}
	fmt.Println(string(jsondata))

	return
}
<commit_msg>cmd/parser: add option allowIntToBeNum<commit_after>package parse

import (
	"encoding/json"
	"fmt"

	"github.com/tsaikd/KDGoLib/cliutil/cmder"
	"github.com/tsaikd/go-raml-parser/parser"
	"github.com/tsaikd/go-raml-parser/parser/parserConfig"
	"gopkg.in/urfave/cli.v2"
)

// Module info
var Module = cmder.NewModule("parse").
	SetUsage("Parse RAML file and show API in json format").
	AddFlag(
		&cli.StringFlag{
			Name:        "f",
			Aliases:     []string{"ramlfile"},
			Value:       "api.raml",
			Usage:       "Source RAML file",
			Destination: &ramlFile,
		},
		&cli.BoolFlag{
			Name:        "checkRAMLVersion",
			Usage:       "Check RAML Version",
			Destination: &checkRAMLVersion,
		},
		&cli.BoolFlag{
			Name:        "allowIntToBeNum",
			Usage:       "Allow integer type to be number type when checking",
			Destination: &allowIntToBeNum,
		},
	).
	SetAction(action)

var ramlFile string
var checkRAMLVersion bool
var allowIntToBeNum bool

var checkOptions = []parser.CheckValueOption{}

func action(c *cli.Context) (err error) {
	ramlParser := parser.NewParser()

	if allowIntToBeNum {
		checkOptions = append(checkOptions, parser.CheckValueOptionAllowIntegerToBeNumber(true))
	}

	if err = ramlParser.Config(parserConfig.CheckRAMLVersion, checkRAMLVersion); err != nil {
		return
	}

	if err = ramlParser.Config(parserConfig.CheckValueOptions, checkOptions); err != nil {
		return
	}

	rootdoc, err := ramlParser.ParseFile(ramlFile)
	if err != nil {
		return
	}

	jsondata, err := json.MarshalIndent(rootdoc, "", "  ")
	if err != nil {
		return
	}
	fmt.Println(string(jsondata))

	return
}
<|endoftext|>