<commit_before>package php

import (
	"fmt"
	"strings"
	"unicode"

	"stephensearles.com/php/token"
)

const shortPHPBegin = "<?"
const longPHPBegin = "<?php"
const phpEnd = "?>"

const eof = -1

// lexHTML consumes and emits an html t until it
// finds a php begin
func lexHTML(l *lexer) stateFn {
	for {
		if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
			if l.pos > l.start {
				l.emit(token.HTML)
			}
			return lexPHPBegin
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(token.HTML)
	}
	l.emit(token.EOF)
	return nil
}

func lexPHPBegin(l *lexer) stateFn {
	if strings.HasPrefix(l.input[l.pos:], longPHPBegin) {
		l.pos += len(longPHPBegin)
	}
	if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
		l.pos += len(shortPHPBegin)
	}
	l.emit(token.PHPBegin)
	return lexPHP
}

func lexPHP(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		secondR := l.peek()
		l.backup()
		if unicode.IsDigit(secondR) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "<<<") {
		return lexDoc
	}

	if strings.HasPrefix(l.input[l.pos:], "?>") {
		return lexPHPEnd
	}

	if strings.HasPrefix(l.input[l.pos:], "#") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "//") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "/*") {
		return lexBlockComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	if l.peek() == '`' {
		return lexShellCommand
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	for _, tokenString := range token.TokenList {
		t := token.TokenMap[tokenString]
		potentialToken := l.input[l.pos:]
		if len(potentialToken) > len(tokenString) {
			potentialToken = potentialToken[:len(tokenString)]
		}
		if strings.HasPrefix(strings.ToLower(potentialToken), tokenString) {
			prev := l.previous()
			if isKeyword(t, tokenString) && prev == '$' {
				break
			}
			l.pos += len(tokenString)
			if isKeyword(t, tokenString) && l.accept(alphabet+underscore+digits) {
				l.backup() // to account for the character consumed by accept
				l.pos -= len(tokenString)
				break
			}
			l.emit(t)
			return lexPHP
		}
	}

	l.acceptRun(alphabet + underscore + digits + "\\")
	l.emit(token.Identifier)
	return lexPHP
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") {
		if l.accept("x") {
			// hexadecimal
			l.acceptRun(digits + "abcdef")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
	}
	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexPHP
}

func lexShellCommand(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '`':
			l.emit(token.ShellCommand)
			return lexPHP
		case eof:
			l.emit(token.ShellCommand)
			return nil
		}
	}
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const digits = "0123456789"
const underscore = "_"

func lexIdentifier(l *lexer) stateFn {
	l.accept("$")
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)
	l.emit(token.VariableOperator)
	return lexPHP
}

// lexPHPEnd lexes the end of a PHP section returning the context to HTML
func lexPHPEnd(l *lexer) stateFn {
	l.pos += len(phpEnd)
	l.emit(token.PHPEnd)
	return lexHTML
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.Index(l.input[l.pos:], "\n") + 1
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}
	// don't lex php end
	if phpEndLength := strings.Index(l.input[l.pos:l.pos+lineLength], phpEnd); phpEndLength >= 0 && phpEndLength < lineLength {
		lineLength = phpEndLength
	}
	l.pos += lineLength
	l.ignore()
	return lexPHP
}

func lexBlockComment(l *lexer) stateFn {
	commentLength := strings.Index(l.input[l.pos:], "*/") + 2
	if commentLength == 1 {
		// the file ends before we find */
		commentLength = len(l.input[l.pos:])
	}
	l.pos += commentLength
	l.ignore()
	return lexPHP
}

func lexDoc(l *lexer) stateFn {
	var nowDoc bool
	l.pos += len("<<<")
	if strings.HasPrefix(l.input[l.pos:], "'") {
		nowDoc = true
		l.pos += len("'")
	}
	labelPos := l.pos
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)
	endMarker := fmt.Sprintf("\n%s", l.input[labelPos:l.pos])
	if nowDoc {
		l.accept("'")
	}
	l.accept("\n")
	for !strings.HasPrefix(l.input[l.pos:], endMarker) {
		l.next()
	}
	l.pos += len(endMarker)
	l.emit(token.StringLiteral)
	return lexPHP
}
<commit_msg>Added support for capitals in hex number literals<commit_after>package php

import (
	"fmt"
	"strings"
	"unicode"

	"stephensearles.com/php/token"
)

const shortPHPBegin = "<?"
const longPHPBegin = "<?php"
const phpEnd = "?>"

const eof = -1

// lexHTML consumes and emits an html t until it
// finds a php begin
func lexHTML(l *lexer) stateFn {
	for {
		if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
			if l.pos > l.start {
				l.emit(token.HTML)
			}
			return lexPHPBegin
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(token.HTML)
	}
	l.emit(token.EOF)
	return nil
}

func lexPHPBegin(l *lexer) stateFn {
	if strings.HasPrefix(l.input[l.pos:], longPHPBegin) {
		l.pos += len(longPHPBegin)
	}
	if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
		l.pos += len(shortPHPBegin)
	}
	l.emit(token.PHPBegin)
	return lexPHP
}

func lexPHP(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		secondR := l.peek()
		l.backup()
		if unicode.IsDigit(secondR) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "<<<") {
		return lexDoc
	}

	if strings.HasPrefix(l.input[l.pos:], "?>") {
		return lexPHPEnd
	}

	if strings.HasPrefix(l.input[l.pos:], "#") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "//") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "/*") {
		return lexBlockComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	if l.peek() == '`' {
		return lexShellCommand
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	for _, tokenString := range token.TokenList {
		t := token.TokenMap[tokenString]
		potentialToken := l.input[l.pos:]
		if len(potentialToken) > len(tokenString) {
			potentialToken = potentialToken[:len(tokenString)]
		}
		if strings.HasPrefix(strings.ToLower(potentialToken), tokenString) {
			prev := l.previous()
			if isKeyword(t, tokenString) && prev == '$' {
				break
			}
			l.pos += len(tokenString)
			if isKeyword(t, tokenString) && l.accept(alphabet+underscore+digits) {
				l.backup() // to account for the character consumed by accept
				l.pos -= len(tokenString)
				break
			}
			l.emit(t)
			return lexPHP
		}
	}

	l.acceptRun(alphabet + underscore + digits + "\\")
	l.emit(token.Identifier)
	return lexPHP
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") {
		if l.accept("x") {
			// hexadecimal
			l.acceptRun(digits + "abcdefABCDEF")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
	}
	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexPHP
}

func lexShellCommand(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '`':
			l.emit(token.ShellCommand)
			return lexPHP
		case eof:
			l.emit(token.ShellCommand)
			return nil
		}
	}
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const digits = "0123456789"
const underscore = "_"

func lexIdentifier(l *lexer) stateFn {
	l.accept("$")
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)
	l.emit(token.VariableOperator)
	return lexPHP
}

// lexPHPEnd lexes the end of a PHP section returning the context to HTML
func lexPHPEnd(l *lexer) stateFn {
	l.pos += len(phpEnd)
	l.emit(token.PHPEnd)
	return lexHTML
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.Index(l.input[l.pos:], "\n") + 1
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}
	// don't lex php end
	if phpEndLength := strings.Index(l.input[l.pos:l.pos+lineLength], phpEnd); phpEndLength >= 0 && phpEndLength < lineLength {
		lineLength = phpEndLength
	}
	l.pos += lineLength
	l.ignore()
	return lexPHP
}

func lexBlockComment(l *lexer) stateFn {
	commentLength := strings.Index(l.input[l.pos:], "*/") + 2
	if commentLength == 1 {
		// the file ends before we find */
		commentLength = len(l.input[l.pos:])
	}
	l.pos += commentLength
	l.ignore()
	return lexPHP
}

func lexDoc(l *lexer) stateFn {
	var nowDoc bool
	l.pos += len("<<<")
	if strings.HasPrefix(l.input[l.pos:], "'") {
		nowDoc = true
		l.pos += len("'")
	}
	labelPos := l.pos
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)
	endMarker := fmt.Sprintf("\n%s", l.input[labelPos:l.pos])
	if nowDoc {
		l.accept("'")
	}
	l.accept("\n")
	for !strings.HasPrefix(l.input[l.pos:], endMarker) {
		l.next()
	}
	l.pos += len(endMarker)
	l.emit(token.StringLiteral)
	return lexPHP
}
<|endoftext|>


<commit_before>package cpu

import (
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCpu_times(t *testing.T) {
	v, err := CPUTimes(false)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Error("could not get CPUs ", err)
	}
	empty := CPUTimesStat{}
	for _, vv := range v {
		if vv == empty {
			t.Errorf("could not get CPU User: %v", vv)
		}
	}
}

func TestCpu_counts(t *testing.T) {
	v, err := CPUCounts(true)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if v == 0 {
		t.Errorf("could not get CPU counts: %v", v)
	}
}

func TestCPUTimeStat_String(t *testing.T) {
	v := CPUTimesStat{
		CPU:    "cpu0",
		User:   100.1,
		System: 200.1,
		Idle:   300.1,
	}
	e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0.0,"iowait":0.0,"irq":0.0,"softirq":0.0,"steal":0.0,"guest":0.0,"guest_nice":0.0,"stolen":0.0}`
	if e != fmt.Sprintf("%v", v) {
		t.Errorf("CPUTimesStat string is invalid: %v", v)
	}
}

func TestCpuInfo(t *testing.T) {
	v, err := CPUInfo()
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Errorf("could not get CPU Info")
	}
	for _, vv := range v {
		if vv.ModelName == "" {
			t.Errorf("could not get CPU Info: %v", vv)
		}
	}
}

func testCPUPercent(t *testing.T, percpu bool) {
	v, err := CPUPercent(time.Millisecond, percpu)
	if err != nil {
		t.Errorf("error %v", err)
	}
	numcpu := runtime.NumCPU()
	if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
		t.Fatalf("wrong number of entries from CPUPercent: %v", v)
	}
	for i := 0; i < 100; i++ {
		duration := time.Duration(10) * time.Microsecond
		v, err := CPUPercent(duration, percpu)
		if err != nil {
			t.Errorf("error %v", err)
		}
		for _, percent := range v {
			if percent < 0.0 || percent > 100.0*float64(numcpu) {
				t.Fatalf("CPUPercent value is invalid: %f", percent)
			}
		}
	}
}

func TestCPUPercent(t *testing.T) {
	testCPUPercent(t, false)
}

func TestCPUPercentPerCpu(t *testing.T) {
	testCPUPercent(t, true)
}
<commit_msg>cpu: skip percpu percent test if windows. Windows can only get one CPU info currently.<commit_after>package cpu

import (
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCpu_times(t *testing.T) {
	v, err := CPUTimes(false)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Error("could not get CPUs ", err)
	}
	empty := CPUTimesStat{}
	for _, vv := range v {
		if vv == empty {
			t.Errorf("could not get CPU User: %v", vv)
		}
	}
}

func TestCpu_counts(t *testing.T) {
	v, err := CPUCounts(true)
	if err != nil {
		t.Errorf("error %v", err)
	}
	if v == 0 {
		t.Errorf("could not get CPU counts: %v", v)
	}
}

func TestCPUTimeStat_String(t *testing.T) {
	v := CPUTimesStat{
		CPU:    "cpu0",
		User:   100.1,
		System: 200.1,
		Idle:   300.1,
	}
	e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0.0,"iowait":0.0,"irq":0.0,"softirq":0.0,"steal":0.0,"guest":0.0,"guest_nice":0.0,"stolen":0.0}`
	if e != fmt.Sprintf("%v", v) {
		t.Errorf("CPUTimesStat string is invalid: %v", v)
	}
}

func TestCpuInfo(t *testing.T) {
	v, err := CPUInfo()
	if err != nil {
		t.Errorf("error %v", err)
	}
	if len(v) == 0 {
		t.Errorf("could not get CPU Info")
	}
	for _, vv := range v {
		if vv.ModelName == "" {
			t.Errorf("could not get CPU Info: %v", vv)
		}
	}
}

func testCPUPercent(t *testing.T, percpu bool) {
	numcpu := runtime.NumCPU()
	if runtime.GOOS != "windows" {
		v, err := CPUPercent(time.Millisecond, percpu)
		if err != nil {
			t.Errorf("error %v", err)
		}
		if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
			t.Fatalf("wrong number of entries from CPUPercent: %v", v)
		}
	}
	for i := 0; i < 100; i++ {
		duration := time.Duration(10) * time.Microsecond
		v, err := CPUPercent(duration, percpu)
		if err != nil {
			t.Errorf("error %v", err)
		}
		for _, percent := range v {
			if percent < 0.0 || percent > 100.0*float64(numcpu) {
				t.Fatalf("CPUPercent value is invalid: %f", percent)
			}
		}
	}
}

func TestCPUPercent(t *testing.T) {
	testCPUPercent(t, false)
}

func TestCPUPercentPerCpu(t *testing.T) {
	testCPUPercent(t, true)
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

import (
	"github.com/DevMine/repotool/model"
	"github.com/DevMine/srcanlzr/src/ast"
)

// Supported VCS (Version Control System)
const (
	Git = "git"
	Hg  = "mercurial"
	SVN = "subversion"
	Bzr = "bazaar"
	CVS = "cvs"
)

var suppVCS = []string{
	Git,
	Hg,
	SVN,
	Bzr,
	CVS,
}

// Supported visiblities
const (
	PublicVisibility    = "public"
	PackageVisibility   = "package"
	ProtectedVisibility = "protected"
	PrivateVisibility   = "private"
)

var suppVisibility = []string{
	PublicVisibility,
	PackageVisibility,
	ProtectedVisibility,
	PrivateVisibility,
}

// Type names
const (
	TypeMapName         = "MAP"
	TypeStructName      = "STRUCT"
	TypeArrayName       = "ARRAY"
	TypeFuncName        = "FUNC"
	TypeInterfaceName   = "INTERFACE"
	TypeUnsupportedName = "UNSUPPORTED"
)

// Supported programming languages
const (
	Go     = "go"
	Ruby   = "ruby"
	Python = "python"
	C      = "c"
	Java   = "java"
	Scala  = "scala"
)

var suppLang = []string{
	Go,
	Ruby,
	Python,
	C,
	Java,
	Scala,
}

// Supported paradigms
const (
	Structured     = "structured"
	Imperative     = "imperative"
	Procedural     = "procedural"
	Compiled       = "compiled"
	Concurrent     = "concurrent"
	Functional     = "functional"
	ObjectOriented = "object oriented"
	Generic        = "generic"
	Reflective     = "reflective"
)

var suppParadigms = []string{
	Structured,
	Imperative,
	Procedural,
	Compiled,
	Concurrent,
	Functional,
	ObjectOriented,
	Generic,
	Reflective,
}

// A Language represents a programming language.
type Language struct {
	// The programming language name (e.g. go, ruby, java, etc.)
	//
	// The name must match one of the supported programming languages defined in
	// the constants.
	Lang string `json:"language"` // TODO rename into name

	// The paradigms of the programming language (e.g. structured, imperative,
	// object oriented, etc.)
	//
	// The name must match one of the supported paradigms defined in the
	// constants.
	Paradigms []string `json:"paradigms"`
}

// Project is the root of the src API and therefore it must be at the root of
// the JSON.
//
// It contains the metadata of a project and the list of all packages.
type Project struct {
	// The name of the project. Since it may be something really difficult to
	// guess, it should generally be the name of the folder containing the
	// project.
	Name string `json:"name"`

	// The repository in which the project is hosted, or nil. This field is not
	// meant to be filled by one of the language parsers. Only repotool should
	// take care of it. For more details, see:
	//    https://github.com/DevMine/repotool
	//
	// Since this field uses an external type, it is not unmarshalled by
	// src.Unmarshal itself but by the standard json.Unmarshal function.
	// To do so, its unmarshalling is defered using json.RawMessage.
	// See the RepoRaw field.
	Repo *model.Repository `json:"repository"`

	// The list of all programming languages used by the project. Each language
	// must be added by the corresponding language parsers if and only if the
	// project contains at least one line of code written in this language.
	Langs []*Language `json:"languages"`

	// List of all packages of the project. We call "package" every folder that
	// contains at least one source file.
	Packages []*Package `json:"packages"`

	// The total number of lines of code in the whole project, independently of
	// the language.
	LoC int64 `json:"loc"`
}

// Package holds information about a package, which is, basically, just a
// folder that contains at least one source file.
type Package struct {
	// The package documentation, or nil.
	// TODO support docucmentation for multiple languages.
	Doc []string `json:"doc,omitempty"`

	// The package name. This should be the name of the parent folder.
	Name string `json:"name"`

	// The full path of the package. The path must be relative to the root of
	// the project and never be an absolute path.
	Path string `json:"path"`

	// The list of all source files contained in the package.
	SrcFiles []*SrcFile `json:"source_files"`

	// The total number of lines of code of the package.
	LoC int64 `json:"loc"`
}

// SrcFile holds information about a source file.
type SrcFile struct {
	// The path of the source file, relative to the root of the project.
	Path string `json:"path"`

	// Programming language used.
	Lang *Language `json:"language"`

	// List of the imports used by the srouce file.
	Imports []string `json:"imports,omitempty"`

	// Types definition
	TypeSpecs []*ast.TypeSpec `json:"type_specifiers,omitempty"`

	// Structures definition
	// TODO rename JSON key into structures
	Structs []*ast.StructType `json:"structs,omitempty"`

	// List of constants defined at the file level (e.g. global constants)
	Constants []*ast.GlobalDecl `json:"constants,omitempty"`

	// List of variables defined at the file level (e.g. global variables)
	Vars []*ast.GlobalDecl `json:"variables,omitempty"`

	// List of functions
	Funcs []*ast.FuncDecl `json:"functions,omitempty"`

	// List of interfaces
	Interfaces []*ast.Interface `json:"interfaces,omitempty"`

	// List of classes
	Classes []*ast.ClassDecl `json:"classes,omitempty"`

	// List of enums
	Enums []*ast.EnumDecl `json:"enums,omitempty"`

	// List of traits
	// See http://en.wikipedia.org/wiki/Trait_%28computer_programming%29
	Traits []*ast.Trait `json:"traits,omitempty"`

	// The total number of lines of code.
	LoC int64 `json:"loc"`
}
<commit_msg>src/encoder: implement JSON encoding using std lib<commit_after>// Copyright 2014-2015 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

import (
	"encoding/json"
	"io"
	"os"

	"github.com/DevMine/repotool/model"
	"github.com/DevMine/srcanlzr/src/ast"
)

// Supported VCS (Version Control System)
const (
	Git = "git"
	Hg  = "mercurial"
	SVN = "subversion"
	Bzr = "bazaar"
	CVS = "cvs"
)

var suppVCS = []string{
	Git,
	Hg,
	SVN,
	Bzr,
	CVS,
}

// Supported visiblities
const (
	PublicVisibility    = "public"
	PackageVisibility   = "package"
	ProtectedVisibility = "protected"
	PrivateVisibility   = "private"
)

var suppVisibility = []string{
	PublicVisibility,
	PackageVisibility,
	ProtectedVisibility,
	PrivateVisibility,
}

// Type names
const (
	TypeMapName         = "MAP"
	TypeStructName      = "STRUCT"
	TypeArrayName       = "ARRAY"
	TypeFuncName        = "FUNC"
	TypeInterfaceName   = "INTERFACE"
	TypeUnsupportedName = "UNSUPPORTED"
)

// Supported programming languages
const (
	Go     = "go"
	Ruby   = "ruby"
	Python = "python"
	C      = "c"
	Java   = "java"
	Scala  = "scala"
)

var suppLang = []string{
	Go,
	Ruby,
	Python,
	C,
	Java,
	Scala,
}

// Supported paradigms
const (
	Structured     = "structured"
	Imperative     = "imperative"
	Procedural     = "procedural"
	Compiled       = "compiled"
	Concurrent     = "concurrent"
	Functional     = "functional"
	ObjectOriented = "object oriented"
	Generic        = "generic"
	Reflective     = "reflective"
)

var suppParadigms = []string{
	Structured,
	Imperative,
	Procedural,
	Compiled,
	Concurrent,
	Functional,
	ObjectOriented,
	Generic,
	Reflective,
}

// A Language represents a programming language.
type Language struct {
	// The programming language name (e.g. go, ruby, java, etc.)
	//
	// The name must match one of the supported programming languages defined in
	// the constants.
	Lang string `json:"language"` // TODO rename into name

	// The paradigms of the programming language (e.g. structured, imperative,
	// object oriented, etc.)
	//
	// The name must match one of the supported paradigms defined in the
	// constants.
	Paradigms []string `json:"paradigms"`
}

// Package holds information about a package, which is, basically, just a
// folder that contains at least one source file.
type Package struct {
	// The package documentation, or nil.
	// TODO support docucmentation for multiple languages.
	Doc []string `json:"doc,omitempty"`

	// The package name. This should be the name of the parent folder.
	Name string `json:"name"`

	// The full path of the package. The path must be relative to the root of
	// the project and never be an absolute path.
	Path string `json:"path"`

	// The list of all source files contained in the package.
	SrcFiles []*SrcFile `json:"source_files"`

	// The total number of lines of code of the package.
	LoC int64 `json:"loc"`
}

// SrcFile holds information about a source file.
type SrcFile struct {
	// The path of the source file, relative to the root of the project.
	Path string `json:"path"`

	// Programming language used.
	Lang *Language `json:"language"`

	// List of the imports used by the srouce file.
	Imports []string `json:"imports,omitempty"`

	// Types definition
	TypeSpecs []*ast.TypeSpec `json:"type_specifiers,omitempty"`

	// Structures definition
	// TODO rename JSON key into structures
	Structs []*ast.StructType `json:"structs,omitempty"`

	// List of constants defined at the file level (e.g. global constants)
	Constants []*ast.GlobalDecl `json:"constants,omitempty"`

	// List of variables defined at the file level (e.g. global variables)
	Vars []*ast.GlobalDecl `json:"variables,omitempty"`

	// List of functions
	Funcs []*ast.FuncDecl `json:"functions,omitempty"`

	// List of interfaces
	Interfaces []*ast.Interface `json:"interfaces,omitempty"`

	// List of classes
	Classes []*ast.ClassDecl `json:"classes,omitempty"`

	// List of enums
	Enums []*ast.EnumDecl `json:"enums,omitempty"`

	// List of traits
	// See http://en.wikipedia.org/wiki/Trait_%28computer_programming%29
	Traits []*ast.Trait `json:"traits,omitempty"`

	// The total number of lines of code.
	LoC int64 `json:"loc"`
}

// Project is the root of the src API and therefore it must be at the root of
// the JSON.
//
// It contains the metadata of a project and the list of all packages.
type Project struct {
	// The name of the project. Since it may be something really difficult to
	// guess, it should generally be the name of the folder containing the
	// project.
	Name string `json:"name"`

	// The repository in which the project is hosted, or nil. This field is not
	// meant to be filled by one of the language parsers. Only repotool should
	// take care of it. For more details, see:
	//    https://github.com/DevMine/repotool
	//
	// Since this field uses an external type, it is not unmarshalled by
	// src.Unmarshal itself but by the standard json.Unmarshal function.
	// To do so, its unmarshalling is defered using json.RawMessage.
	// See the RepoRaw field.
	Repo *model.Repository `json:"repository"`

	// The list of all programming languages used by the project. Each language
	// must be added by the corresponding language parsers if and only if the
	// project contains at least one line of code written in this language.
	Langs []*Language `json:"languages"`

	// List of all packages of the project. We call "package" every folder that
	// contains at least one source file.
	Packages []*Package `json:"packages"`

	// The total number of lines of code in the whole project, independently of
	// the language.
	LoC int64 `json:"loc"`
}

// Encode writes JSON representation of the project into w.
//
// For now, encoding still make use of the json package of the standard libary.
func (p *Project) Encode(w io.Writer) error {
	enc := json.NewEncoder(w)
	return enc.Encode(p)
}

// Encode writes JSON representation of the project into a file located at path.
func (p *Project) EncodeToFile(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	return p.Encode(f)
}
<|endoftext|>


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

// +build !nodevstat

package collector

import (
	"errors"
	"fmt"

	"github.com/prometheus/client_golang/prometheus"
)

/*
#cgo LDFLAGS: -ldevstat
#include <devstat.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
	char		device[DEVSTAT_NAME_LEN];
	int		unit;
	uint64_t	bytes;
	uint64_t	transfers;
	uint64_t	blocks;
} Stats;

int _get_ndevs() {
	struct statinfo current;
	int num_devices;

	current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
	if (current.dinfo == NULL)
		return -2;

	checkversion();

	if (getdevs(&current) == -1)
		return -1;

	return current.dinfo->numdevs;
}

Stats _get_stats(int i) {
	struct statinfo current;
	int num_devices;

	current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
	getdevs(&current);

	num_devices = current.dinfo->numdevs;
	Stats stats;

	uint64_t total_bytes, total_transfers, total_blocks;
	long double kb_per_transfer, transfers_per_second, mb_per_second, blocks_per_second, ms_per_transaction;

	strcpy(stats.device, current.dinfo->devices[i].device_name);
	stats.unit = current.dinfo->devices[i].unit_number;
	compute_stats(&current.dinfo->devices[i],
		NULL,
		1.0,
		&total_bytes,
		&total_transfers,
		&total_blocks,
		&kb_per_transfer,
		&transfers_per_second,
		&mb_per_second,
		&blocks_per_second,
		&ms_per_transaction);

	stats.bytes = total_bytes;
	stats.transfers = total_transfers;
	stats.blocks = total_blocks;

        return stats;
}
*/
import "C"

const (
	devstatSubsystem = "devstat"
)

type devstatCollector struct {
	bytesDesc     *prometheus.Desc
	transfersDesc *prometheus.Desc
	blocksDesc    *prometheus.Desc
}

func init() {
	Factories["devstat"] = NewDevstatCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// Device stats.
func NewDevstatCollector() (Collector, error) {
	return &devstatCollector{
		bytesDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "bytes_total"),
			"The total number of bytes transferred for reads and writes on the device.",
			[]string{"device"}, nil,
		),
		transfersDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "transfers_total"),
			"The total number of transactions completed.",
			[]string{"device"}, nil,
		),
		blocksDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "blocks_total"),
			"The total number of bytes given in terms of the devices blocksize.",
			[]string{"device"}, nil,
		),
	}, nil
}

func (c *devstatCollector) Update(ch chan<- prometheus.Metric) error {
	count := C._get_ndevs()
	if count == -1 {
		return errors.New("getdevs() failed")
	}
	if count == -2 {
		return errors.New("calloc() failed")
	}

	for i := C.int(0); i < count; i++ {
		stats := C._get_stats(i)
		device := fmt.Sprintf("%s%d", C.GoString(&stats.device[0]), stats.unit)

		ch <- prometheus.MustNewConstMetric(c.bytesDesc, prometheus.CounterValue, float64(stats.bytes), device)
		ch <- prometheus.MustNewConstMetric(c.transfersDesc, prometheus.CounterValue, float64(stats.transfers), device)
		ch <- prometheus.MustNewConstMetric(c.blocksDesc, prometheus.CounterValue, float64(stats.blocks), device)
	}

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

// +build !nodevstat

package collector

import (
	"errors"
	"fmt"

	"github.com/prometheus/client_golang/prometheus"
)

/*
#cgo LDFLAGS: -ldevstat
#include <devstat.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
	char		device[DEVSTAT_NAME_LEN];
	int		unit;
	uint64_t	bytes;
	uint64_t	transfers;
	uint64_t	blocks;
} Stats;

int _get_ndevs() {
	struct statinfo current;
	int num_devices;

	current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
	if (current.dinfo == NULL)
		return -2;

	checkversion();

	if (getdevs(&current) == -1)
		return -1;

	return current.dinfo->numdevs;
}

Stats _get_stats(int i) {
	struct statinfo current;
	int num_devices;

	current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
	getdevs(&current);

	num_devices = current.dinfo->numdevs;
	Stats stats;

	uint64_t total_bytes, total_transfers, total_blocks;
	long double kb_per_transfer, transfers_per_second, mb_per_second, blocks_per_second, ms_per_transaction;

	strcpy(stats.device, current.dinfo->devices[i].device_name);
	stats.unit = current.dinfo->devices[i].unit_number;
	compute_stats(&current.dinfo->devices[i],
		NULL,
		1.0,
		&total_bytes,
		&total_transfers,
		&total_blocks,
		&kb_per_transfer,
		&transfers_per_second,
		&mb_per_second,
		&blocks_per_second,
		&ms_per_transaction);

	stats.bytes = total_bytes;
	stats.transfers = total_transfers;
	stats.blocks = total_blocks;

        return stats;
}
*/
import "C"

const (
	devstatSubsystem = "devstat"
)

type devstatCollector struct {
	bytesDesc     *prometheus.Desc
	transfersDesc *prometheus.Desc
	blocksDesc    *prometheus.Desc
}

func init() {
	Factories["devstat"] = NewDevstatCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// Device stats.
func NewDevstatCollector() (Collector, error) {
	return &devstatCollector{
		bytesDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "bytes_total"),
			"The total number of bytes transferred for reads and writes on the device.",
			[]string{"device"}, nil,
		),
		transfersDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "transfers_total"),
			"The total number of transactions completed.",
			[]string{"device"}, nil,
		),
		blocksDesc: prometheus.NewDesc(
			prometheus.BuildFQName(Namespace, devstatSubsystem, "blocks_total"),
			"The total number of bytes given in terms of the devices blocksize.",
			[]string{"device"}, nil,
		),
	}, nil
}

func (c *devstatCollector) Update(ch chan<- prometheus.Metric) error {
	count := C._get_ndevs()
	if count == -1 {
		return errors.New("getdevs() failed")
	}
	if count == -2 {
		return errors.New("calloc() failed")
	}

	for i := C.int(0); i < count; i++ {
		stats := C._get_stats(i)
		device := fmt.Sprintf("%s%d", C.GoString(&stats.device[0]), stats.unit)

		ch <- prometheus.MustNewConstMetric(c.bytesDesc, prometheus.CounterValue, float64(stats.bytes), device)
		ch <- prometheus.MustNewConstMetric(c.transfersDesc, prometheus.CounterValue, float64(stats.transfers), device)
		ch <- prometheus.MustNewConstMetric(c.blocksDesc, prometheus.CounterValue, float64(stats.blocks), device)
	}

	return nil
}
<|endoftext|>


<commit_before>package pruneCmd

import (
	// Stdlib
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"

	// Internal
	"github.com/salsaflow/salsaflow/app"
	"github.com/salsaflow/salsaflow/config"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules"
	"github.com/salsaflow/salsaflow/prompt"

	// Other
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: `
  prune [-local_only] [-remote_only]`,
	Short: "prune delivered story branches",
	Long: `
  Delete local and remote branches that are associated with stories
  that are potentially finished are can be pruned.

  Only the branches owned by the caller are offered for deletion.
	`,
	Action: run,
}

var (
	flagLocalOnly  bool
	flagRemoteOnly bool
)

func init() {
	// Register flags.
	Command.Flags.BoolVar(&flagLocalOnly, "local_only", flagLocalOnly,
		"prune local branches only")
	Command.Flags.BoolVar(&flagRemoteOnly, "remote_only", flagRemoteOnly,
		"prune remote branches only")

	// Register global flags.
	appflags.RegisterGlobalFlags(&Command.Flags)
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	app.MustInit()

	if err := runMain(); err != nil {
		log.Fatalln("\nError: " + err.Error())
	}
}

func runMain() (err error) {
	var (
		task   string
		stderr *bytes.Buffer
	)
	defer func() {
		// Print error details.
		if err != nil {
			log.FailWithDetails(task, stderr)
		}
	}()

	// Fetch the remote repository unless we are restricted to the local branches only.
	if !flagLocalOnly {
		task = "Fetch the remote repository"
		log.Run(task)
		stderr, err = git.UpdateRemotes(config.OriginName)
		if err != nil {
			return
		}
	}

	// Get the list of story references.
	task = "Collect all story branches"
	log.Run(task)
	localRefs, remoteRefs, stderr, err := git.ListStoryRefs()
	if err != nil {
		return
	}

	var refs []string
	switch {
	case flagLocalOnly:
		refs = localRefs
	case flagRemoteOnly:
		refs = remoteRefs
	default:
		refs = append(localRefs, remoteRefs...)
	}

	if len(refs) == 0 {
		task = ""
		log.Println("\nNo story branches found, exiting...")
		return
	}

	// Collect all the story IDs.
	idMap := make(map[string]struct{})
	for _, ref := range refs {
		// This cannot fail here since we got the refs using ListStoryRefs.
		id, _ := git.RefToStoryId(ref)
		idMap[id] = struct{}{}
	}

	var ids []string
	for id := range idMap {
		ids = append(ids, id)
	}

	// Get the list of active story IDs.
	activeIds, err := modules.GetIssueTracker().SelectActiveStoryIds(ids)
	if err != nil {
		return
	}
	ids = activeIds

	// Select only the refs that can be safely deleted.
	refs = selectInactiveRefs(refs, ids)

	if len(refs) == 0 {
		task = ""
		log.Println("\nThere are no branches to be deleted, exiting...")
		return
	}

	// Sort the refs.
	sort.Sort(sort.StringSlice(refs))

	// Prompt the user to confirm the delete operation.
	var (
		toDeleteLocally  []string
		toDeleteRemotely []string
		ok               bool
	)

	// Go through the local branches.
	if strings.HasPrefix(refs[0], "refs/heads/") {
		fmt.Println("\n---> Local branches\n")
	}
	for len(refs) > 0 {
		ref := refs[0]
		if !strings.HasPrefix(ref, "refs/heads/") {
			break
		}
		branch := ref[len("refs/heads/"):]
		question := fmt.Sprintf("Delete local branch '%v'", branch)
		ok, err = prompt.Confirm(question)
		if err != nil {
			return
		}
		if ok {
			toDeleteLocally = append(toDeleteLocally, branch)
		}
		refs = refs[1:]
	}

	// All that is left are remote branches.
	if len(refs) != 0 {
		fmt.Println("\n---> Remote branches\n")
	}
	for _, ref := range refs {
		branch := ref[len("refs/remotes/origin/"):]
		question := fmt.Sprintf("Delete remote branch '%v'", branch)
		ok, err = prompt.Confirm(question)
		if err != nil {
			return
		}
		if ok {
			toDeleteRemotely = append(toDeleteRemotely, branch)
		}
	}
	fmt.Println()

	if len(toDeleteLocally) == 0 && len(toDeleteRemotely) == 0 {
		task = ""
		fmt.Println("No branches selected, exiting...")
		return
	}

	// Delete the local branches.
	if len(toDeleteLocally) != 0 {
		task = "Delete the chosen local branches"
		log.Run(task)

		// Remember the position of the branches to be deleted.
		// This is used in case we need to perform a rollback.
		var (
			currentPositions []string
			hexsha           string
		)
		for _, branchName := range toDeleteLocally {
			hexsha, stderr, err = git.Hexsha("refs/heads/" + branchName)
			if err != nil {
				return
			}
			currentPositions = append(currentPositions, hexsha)
		}

		// Delete the selected local branches.
		args := append([]string{"-d"}, toDeleteLocally...)
		stderr, err = git.Branch(args...)
		if err != nil {
			return
		}
		defer func(taskMsg string) {
			// On error, try to restore the local branches that were deleted.
			if err != nil {
				log.Rollback(taskMsg)
				for i, branchName := range toDeleteLocally {
					out, ex := git.ResetKeep(branchName, currentPositions[i])
					if ex != nil {
						log.FailWithDetails(task, out)
					}
				}
			}
		}(task)
	}

	// Delete the remote branches.
	if len(toDeleteRemotely) != 0 {
		task = "Delete the chosen remote branches"
		log.Run(task)
		var refs []string
		for _, branchName := range toDeleteRemotely {
			refs = append(refs, ":"+branchName)
		}
		stderr, err = git.Push(config.OriginName, refs...)
	}
	return
}

// selectInactiveRefs returns the list of refs that can be safely deleted.
// A reference can be safely deleted when the associated story is closed.
func selectInactiveRefs(refs, activeIds []string) (inactiveRefs []string) {
	var suffixes []string
	for _, id := range activeIds {
		suffixes = append(suffixes, "/"+id)
	}

	var inactive []string
RefLoop:
	for _, ref := range refs {
		for _, suffix := range suffixes {
			if strings.HasSuffix(ref, suffix) {
				continue RefLoop
			}
		}
		inactive = append(inactive, ref)
	}

	return inactive
}
<commit_msg>Delete the old repo prune implementation<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package main

import (
	"github.com/gorhill/cronexpr"
	"log"
	"time"
)

type Job struct {
	expr *cronexpr.Expression
	cmd  string
	next time.Time
}

func scheduleLoop() {
	jobs := make([]*Job, 0, len(config.Schedule))
	for exprStr, cmd := range config.Schedule {
		expr := cronexpr.MustParse(exprStr)
		jobs = append(jobs, &Job{
			expr,
			cmd,
			expr.Next(time.Now()),
		})
	}

	go func() {
		for {
			<-time.Tick(time.Second)
			for _, j := range jobs {
				if time.Now().Before(j.next) {
					continue
				}
				log.Printf("Executing scheduled command '%s'...", j.cmd)
				execCmd(j.cmd)
				j.next = j.expr.Next(time.Now())
			}
		}
	}()
}
<commit_msg>Fix time.Timer-leak in scheduler<commit_after>// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package main

import (
	"github.com/gorhill/cronexpr"
	"log"
	"time"
)

type Job struct {
	expr *cronexpr.Expression
	cmd  string
	next time.Time
}

func scheduleLoop() {
	jobs := make([]*Job, 0, len(config.Schedule))
	for exprStr, cmd := range config.Schedule {
		expr := cronexpr.MustParse(exprStr)
		jobs = append(jobs, &Job{
			expr,
			cmd,
			expr.Next(time.Now()),
		})
	}

	go func() {
		for range time.Tick(time.Second) {
			for _, j := range jobs {
				if time.Now().Before(j.next) {
					continue
				}
				log.Printf("Executing scheduled command '%s'...", j.cmd)
				execCmd(j.cmd)
				j.next = j.expr.Next(time.Now())
			}
		}
	}()
}
<|endoftext|>


<commit_before>/*
	ToDD task - download collectors

	Copyright 2016 Matt Oswalt. Use or modification of this
	source code is governed by the license provided here:
	https://github.com/toddproject/todd/blob/master/LICENSE
*/

package tasks

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
)

// DownloadAssetTask defines this particular task. It contains definitions not only for the task message, but
// also HTTPClient, filesystem, and i/o system abstractions to be more conducive to unit testing.
type DownloadAssetTask struct {
	BaseTask
	HTTPClient   *http.Client `json:"-"`
	Fs           FileSystem   `json:"-"`
	Ios          IoSystem     `json:"-"`
	CollectorDir string       `json:"-"`
	Assets       []string     `json:"assets"`
	TestletDir   string       `json:"-"`
}

// Run contains the logic necessary to perform this task on the agent. This particular task will download all required assets,
// copy them into the appropriate directory, and ensure that the execute permission is given to each collector file.
func (dat DownloadAssetTask) Run() error {

	// Iterate over the slice of collectors and download them.
	for x := range dat.Assets {

		assetUrl := dat.Assets[x]

		assetDir := ""

		switch {
		case strings.Contains(assetUrl, "factcollectors"):
			assetDir = dat.CollectorDir
		case strings.Contains(assetUrl, "testlets"):
			assetDir = dat.TestletDir
		default:
			log.Error("Invalid asset download URL received")
			os.Exit(1)
		}

		err := dat.downloadAsset(assetUrl, assetDir)
		if err != nil {
			log.Error("Problem downloading asset ", assetUrl)
		}
	}

	return nil
}

// downloadAsset will download an asset at the specified URL, into the specified directory
func (dat DownloadAssetTask) downloadAsset(url, directory string) error {

	tokens := strings.Split(url, "/")
	fileName := tokens[len(tokens)-1]
	fileName = fmt.Sprintf("%s/%s", directory, fileName)
	log.Info("Downloading ", url, " to ", fileName)

	// TODO: check file existence first with io.IsExist
	output, err := dat.Fs.Create(fileName)
	if err != nil {
		return errors.New(fmt.Sprintf("Error while creating", fileName, "-", err))
	}
	defer output.Close()

	response, err := dat.HTTPClient.Get(url)
	if err != nil {
		return errors.New(fmt.Sprintf("Error while downloading", url, "-", err))
	}
	defer response.Body.Close()

	n, err := dat.Ios.Copy(output, response.Body)
	if err != nil {
		return errors.New(fmt.Sprintf("Error while downloading", url, "-", err))
	}
	err = dat.Fs.Chmod(fileName, 0744)
	if err != nil {
		// For now, let's just throw a warning in the logs. It's not the end of the world if this fails; it may
		// be a limited issue. Worst case is the agent simply reports no facts.
		log.Warn("Problem setting execute permission on downloaded script")
	}

	log.Info(n, " bytes downloaded.")
	return nil
}
<commit_msg>Added more intelligent error handling when downloading assets<commit_after>/*
	ToDD task - download collectors

	Copyright 2016 Matt Oswalt. Use or modification of this
	source code is governed by the license provided here:
	https://github.com/toddproject/todd/blob/master/LICENSE
*/

package tasks

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
)

// DownloadAssetTask defines this particular task. It contains definitions not only for the task message, but
// also HTTPClient, filesystem, and i/o system abstractions to be more conducive to unit testing.
type DownloadAssetTask struct {
	BaseTask
	HTTPClient   *http.Client `json:"-"`
	Fs           FileSystem   `json:"-"`
	Ios          IoSystem     `json:"-"`
	CollectorDir string       `json:"-"`
	Assets       []string     `json:"assets"`
	TestletDir   string       `json:"-"`
}

// Run contains the logic necessary to perform this task on the agent. This particular task will download all required assets,
// copy them into the appropriate directory, and ensure that the execute permission is given to each collector file.
func (dat DownloadAssetTask) Run() error {

	// Iterate over the slice of collectors and download them.
	for x := range dat.Assets {

		assetUrl := dat.Assets[x]

		assetDir := ""

		switch {
		case strings.Contains(assetUrl, "factcollectors"):
			assetDir = dat.CollectorDir
		case strings.Contains(assetUrl, "testlets"):
			assetDir = dat.TestletDir
		default:
			errorMsg := "Invalid asset download URL received"
			log.Error(errorMsg)
			return errors.New(errorMsg)
		}

		err := dat.downloadAsset(assetUrl, assetDir)
		if err != nil {
			log.Error(err)
			return err
		}
	}

	return nil
}

// downloadAsset will download an asset at the specified URL, into the specified directory
func (dat DownloadAssetTask) downloadAsset(url, directory string) error {

	tokens := strings.Split(url, "/")
	fileName := tokens[len(tokens)-1]
	fileName = fmt.Sprintf("%s/%s", directory, fileName)
	log.Info("Downloading ", url, " to ", fileName)

	// TODO: What if this already exists? Consider checking file existence first with io.IsExist?
	output, err := dat.Fs.Create(fileName)
	if err != nil {
		return errors.New(fmt.Sprintf("Error while creating", fileName, "-", err))
	}
	defer output.Close()

	response, err := dat.HTTPClient.Get(url)
	defer response.Body.Close()
	if err != nil || response.StatusCode != 200 {

		// If we have a problem retrieving the testlet, we want to return immediately,
		// instead of writing an empty file to disk
		log.Error(fmt.Sprintf("Error while downloading '%s': %s", url, response.Status))
		return err
	}

	n, err := dat.Ios.Copy(output, response.Body)
	if err != nil {
		log.Error(fmt.Sprintf("Error while writing '%s' to disk", url))
		return err
	}
	err = dat.Fs.Chmod(fileName, 0744)
	if err != nil {

		// TODO(mierdin): currently unhandled; may want to do something further
		log.Warn("Problem setting execute permission on downloaded script")
	}

	log.Info(n, " bytes downloaded.")
	return nil
}
<|endoftext|>


<commit_before>package arduino

import (
	"context"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/edinnen/Thanksgiving_Intranet/analyzer/models"
	"github.com/edinnen/Thanksgiving_Intranet/analyzer/utils"
	"github.com/jmoiron/sqlx"
	"github.com/sirupsen/logrus"
)

// StreamData begins streaming readings from the arduino.
// Data will be read from the arduino, sent to the database,
// and pushed as a sever sent event.
func (arduino Connection) StreamData(ctx context.Context, dataStream chan models.CabinReading, wg *sync.WaitGroup) {
	var streaming bool
	defer wg.Done()

	// Command the arduino to start streaming
	success := arduino.Command(ctx, 4)
	if !success {
		// TODO: Try again a few times and don't panic?
		panic("Failed to initialize stream")
	}
	streaming = true
	logrus.Info("Arduino data stream instantiated")

	// Loop until done
	for {
		select {
		case <-ctx.Done():
			if streaming {
				arduino.CancelStreaming(ctx)
			}
			logrus.Info("Data streaming stopped!")
			return
		default:
			line, err := arduino.ReadLine(ctx, false)
			if err != nil {
				if !strings.Contains(fmt.Sprint(err), "shutdown") {
					logrus.Error(err)
					continue
				}
				continue
			}

			// Parse into a CabinReading struct
			reading, err := models.ParseCabinReading(line)
			if err != nil {
				logrus.Error("Failed to parse line")
				logrus.Error(err)
				continue
			}

			reading.SendToDB(arduino.DB, arduino.Mutex) // Insert the datapoint to our database
			dataStream <- reading                       // Pass CabinReading to the channel
			continue
		}
	}
}

// CancelStreaming sends a <8> command to the Arduino
// and disregards the response to toggle data streaming before shutdown.
func (arduino Connection) CancelStreaming(ctx context.Context) {
	arduino.Write("<8>")
}

// SendHistoricalToDB requests historical data to be streamed to us via serial.
// When a data point is read it is sent to the database.
// All historical files are commanded to be deleted off of
// the Arduino upon stream completion.
func (arduino Connection) SendHistoricalToDB(ctx context.Context, dataStream chan models.CabinReading, db *sqlx.DB) error {
	// Get file locations from arduino
	files, err := arduino.listRootDirectory(ctx)
	if err != nil {
		return err
	}
	// loop over files
FileLoop:
	for _, file := range files {
		success := make(chan bool, 1) // Create an execution blocker
		done := make(chan bool, 1)

		// Continually listen for the command response while we send the command
		go func() {
			err := arduino.readCommand(ctx, 1)
			if err != nil {
				// Release execution blocker and exit the goroutine
				logrus.Errorf("Error executing command: %v", err)
				success <- false
				close(success)
				return
			}
			success <- true
			close(success)
		}()

		// Read out lines of a file and send them to the database and events service
		go func() {
			// Wait for command success/fail notification
			shouldProgress := <-success
			if !shouldProgress {
				err = fmt.Errorf("Command for file %s failed", file)
				close(done)
				return
			}

			var file string

			//Label this loop so we can break it from within the for select statement
		readLoop:
			for {
				select {
				case <-ctx.Done():
					logrus.Debugf("Cancelling historical stream of %s due to shutdown", file)
					close(done)
					return
				default:
					var nr int
					buf := make([]byte, 8192)
					nr, err = arduino.Read(buf)
					if err != nil {
						logrus.Error(err)
						return
					}
					if err != nil {
						if err == io.EOF {
							break
						}
					}

					stringBuf := string(buf[:nr])
					file += stringBuf
					endSequence := regexp.MustCompile("<")
					if endSequence.MatchString(stringBuf) {
						break readLoop
					}
				}
			}

			lines := strings.Split(file, "\n")
			for _, line := range lines {
				if strings.Contains(line, "#") {
					continue
				}

				// Parse into a CabinReading struct
				reading, err := models.ParseCabinReading(line)
				if err != nil {
					if strings.Contains(line, "<") || line == "" {
						continue
					}
					logrus.Error("Failed to parse line:", line)
					continue
				}

				reading.SendToDB(arduino.DB, arduino.Mutex) // Insert the datapoint to our database
				dataStream <- reading                       // Pass CabinReading to the channel
			}
			close(done)
		}()

		rows, err := db.Query("SELECT name FROM historical_files;")
		if err != nil {
			logrus.Fatal(err)
		}
		for rows.Next() {
			var name string
			rows.Scan(&name)
			if name != "" {
				logrus.Debugf("Skipping download of %s. Already recieved.", name)
				continue FileLoop
			}
		}

		logrus.Debugf("Requesting %s", file)
		arduino.Write("<" + file + ">")
		<-done
		_, err = db.Exec("INSERT INTO historical_files (name) VALUES (?);", file)
		if err != nil {
			logrus.Errorf("failed to mark %s as read", file)
		}
		logrus.Info(file, " loaded into database")
	}
	return err
}

// SyncSystemTime synchronizes the RPi's system time with the Arduino.
func (arduino Connection) SyncSystemTime(ctx context.Context) error {
	success := arduino.Command(ctx, 5)
	if !success {
		return fmt.Errorf("failed to obtain response from command <5>")
	}

	line, err := arduino.ReadLine(ctx, true)
	if err != nil {
		return err
	}

	unix, err := strconv.ParseInt(line, 10, 64)
	if err != nil {
		return err
	}

	arduinoTime := time.Unix(unix, 0)
	err = utils.SetSystemDate(arduinoTime)
	if err != nil {
		return err
	}

	return nil
}

// listRootDirectory retrieves an array containing the filepaths to log files on the arduino.
func (arduino Connection) listRootDirectory(ctx context.Context) (filenames []string, err error) {
	if !arduino.Command(ctx, 2) {
		err = fmt.Errorf("Command failed")
		return
	}

	var data string
	for {
		var nr int
		buf := make([]byte, 8192)
		nr, err = arduino.Read(buf)
		if err != nil {
			logrus.Error(err)
			continue
		}
		stringBuf := string(buf[:nr])
		endSequence := regexp.MustCompile("<>")
		data += stringBuf
		if endSequence.MatchString(stringBuf) {
			break
		}
	}

	filenameRegex := regexp.MustCompile(`(?i)<(.*?\.csv)>`)
	matches := filenameRegex.FindAllStringSubmatch(data, -1)

	for _, match := range matches {
		filenames = append(filenames, match[1])
	}

	return
}
<commit_msg>Add where to select query<commit_after>package arduino

import (
	"context"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/edinnen/Thanksgiving_Intranet/analyzer/models"
	"github.com/edinnen/Thanksgiving_Intranet/analyzer/utils"
	"github.com/jmoiron/sqlx"
	"github.com/sirupsen/logrus"
)

// StreamData begins streaming readings from the arduino.
// Data will be read from the arduino, sent to the database,
// and pushed as a sever sent event.
func (arduino Connection) StreamData(ctx context.Context, dataStream chan models.CabinReading, wg *sync.WaitGroup) {
	var streaming bool
	defer wg.Done()

	// Command the arduino to start streaming
	success := arduino.Command(ctx, 4)
	if !success {
		// TODO: Try again a few times and don't panic?
		panic("Failed to initialize stream")
	}
	streaming = true
	logrus.Info("Arduino data stream instantiated")

	// Loop until done
	for {
		select {
		case <-ctx.Done():
			if streaming {
				arduino.CancelStreaming(ctx)
			}
			logrus.Info("Data streaming stopped!")
			return
		default:
			line, err := arduino.ReadLine(ctx, false)
			if err != nil {
				if !strings.Contains(fmt.Sprint(err), "shutdown") {
					logrus.Error(err)
					continue
				}
				continue
			}

			// Parse into a CabinReading struct
			reading, err := models.ParseCabinReading(line)
			if err != nil {
				logrus.Error("Failed to parse line")
				logrus.Error(err)
				continue
			}

			reading.SendToDB(arduino.DB, arduino.Mutex) // Insert the datapoint to our database
			dataStream <- reading                       // Pass CabinReading to the channel
			continue
		}
	}
}

// CancelStreaming sends a <8> command to the Arduino
// and disregards the response to toggle data streaming before shutdown.
func (arduino Connection) CancelStreaming(ctx context.Context) {
	arduino.Write("<8>")
}

// SendHistoricalToDB requests historical data to be streamed to us via serial.
// When a data point is read it is sent to the database.
// All historical files are commanded to be deleted off of
// the Arduino upon stream completion.
func (arduino Connection) SendHistoricalToDB(ctx context.Context, dataStream chan models.CabinReading, db *sqlx.DB) error {
	// Get file locations from arduino
	files, err := arduino.listRootDirectory(ctx)
	if err != nil {
		return err
	}
	// loop over files
FileLoop:
	for _, file := range files {
		success := make(chan bool, 1) // Create an execution blocker
		done := make(chan bool, 1)

		// Continually listen for the command response while we send the command
		go func() {
			err := arduino.readCommand(ctx, 1)
			if err != nil {
				// Release execution blocker and exit the goroutine
				logrus.Errorf("Error executing command: %v", err)
				success <- false
				close(success)
				return
			}
			success <- true
			close(success)
		}()

		// Read out lines of a file and send them to the database and events service
		go func() {
			// Wait for command success/fail notification
			shouldProgress := <-success
			if !shouldProgress {
				err = fmt.Errorf("Command for file %s failed", file)
				close(done)
				return
			}

			var file string

			//Label this loop so we can break it from within the for select statement
		readLoop:
			for {
				select {
				case <-ctx.Done():
					logrus.Debugf("Cancelling historical stream of %s due to shutdown", file)
					close(done)
					return
				default:
					var nr int
					buf := make([]byte, 8192)
					nr, err = arduino.Read(buf)
					if err != nil {
						logrus.Error(err)
						return
					}
					if err != nil {
						if err == io.EOF {
							break
						}
					}

					stringBuf := string(buf[:nr])
					file += stringBuf
					endSequence := regexp.MustCompile("<")
					if endSequence.MatchString(stringBuf) {
						break readLoop
					}
				}
			}

			lines := strings.Split(file, "\n")
			for _, line := range lines {
				if strings.Contains(line, "#") {
					continue
				}

				// Parse into a CabinReading struct
				reading, err := models.ParseCabinReading(line)
				if err != nil {
					if strings.Contains(line, "<") || line == "" {
						continue
					}
					logrus.Error("Failed to parse line:", line)
					continue
				}

				reading.SendToDB(arduino.DB, arduino.Mutex) // Insert the datapoint to our database
				dataStream <- reading                       // Pass CabinReading to the channel
			}
			close(done)
		}()

		rows, err := db.Query("SELECT name FROM historical_files WHERE name = ?;", file)
		if err != nil {
			logrus.Fatal(err)
		}
		for rows.Next() {
			var name string
			rows.Scan(&name)
			if name != "" {
				logrus.Debugf("Skipping download of %s. Already recieved.", name)
				continue FileLoop
			}
		}

		logrus.Debugf("Requesting %s", file)
		arduino.Write("<" + file + ">")
		<-done
		_, err = db.Exec("INSERT INTO historical_files (name) VALUES (?);", file)
		if err != nil {
			logrus.Errorf("failed to mark %s as read", file)
		}
		logrus.Info(file, " loaded into database")
	}
	return err
}

// SyncSystemTime synchronizes the RPi's system time with the Arduino.
func (arduino Connection) SyncSystemTime(ctx context.Context) error {
	success := arduino.Command(ctx, 5)
	if !success {
		return fmt.Errorf("failed to obtain response from command <5>")
	}

	line, err := arduino.ReadLine(ctx, true)
	if err != nil {
		return err
	}

	unix, err := strconv.ParseInt(line, 10, 64)
	if err != nil {
		return err
	}

	arduinoTime := time.Unix(unix, 0)
	err = utils.SetSystemDate(arduinoTime)
	if err != nil {
		return err
	}

	return nil
}

// listRootDirectory retrieves an array containing the filepaths to log files on the arduino.
func (arduino Connection) listRootDirectory(ctx context.Context) (filenames []string, err error) {
	if !arduino.Command(ctx, 2) {
		err = fmt.Errorf("Command failed")
		return
	}

	var data string
	for {
		var nr int
		buf := make([]byte, 8192)
		nr, err = arduino.Read(buf)
		if err != nil {
			logrus.Error(err)
			continue
		}
		stringBuf := string(buf[:nr])
		endSequence := regexp.MustCompile("<>")
		data += stringBuf
		if endSequence.MatchString(stringBuf) {
			break
		}
	}

	filenameRegex := regexp.MustCompile(`(?i)<(.*?\.csv)>`)
	matches := filenameRegex.FindAllStringSubmatch(data, -1)

	for _, match := range matches {
		filenames = append(filenames, match[1])
	}

	return
}
<|endoftext|>


<commit_before>package random

import (
	"crypto/rand"
	"math/big"
	"sort"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceString() *schema.Resource {
	return &schema.Resource{
		Create:        CreateString,
		Read:          ReadString,
		Delete:        schema.RemoveFromState,
		MigrateState:  resourceRandomStringMigrateState,
		SchemaVersion: 1,
		Schema: map[string]*schema.Schema{
			"keepers": {
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"length": {
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"special": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"upper": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"lower": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"number": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"min_numeric": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_upper": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_lower": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_special": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"override_special": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"result": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func CreateString(d *schema.ResourceData, meta interface{}) error {
	const numChars = "0123456789"
	const lowerChars = "abcdefghijklmnopqrstuvwxyz"
	const upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	var specialChars = "!@#$%&*()-_=+[]{}<>:?"

	length := d.Get("length").(int)
	upper := d.Get("upper").(bool)
	minUpper := d.Get("min_upper").(int)
	lower := d.Get("lower").(bool)
	minLower := d.Get("min_lower").(int)
	number := d.Get("number").(bool)
	minNumeric := d.Get("min_numeric").(int)
	special := d.Get("special").(bool)
	minSpecial := d.Get("min_special").(int)
	overrideSpecial := d.Get("override_special").(string)

	if overrideSpecial != "" {
		specialChars = overrideSpecial
	}

	var chars = string("")
	if upper {
		chars += upperChars
	}
	if lower {
		chars += lowerChars
	}
	if number {
		chars += numChars
	}
	if special {
		chars += specialChars
	}

	minMapping := map[string]int{
		numChars:     minNumeric,
		lowerChars:   minLower,
		upperChars:   minUpper,
		specialChars: minSpecial,
	}
	var result = make([]byte, 0, length)
	for k, v := range minMapping {
		s, err := generateRandomBytes(&k, v)
		if err != nil {
			return errwrap.Wrapf("error generating random bytes: {{err}}", err)
		}
		result = append(result, s...)
	}
	s, err := generateRandomBytes(&chars, length-len(result))
	if err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}
	result = append(result, s...)
	order := make([]byte, len(result))
	if _, err := rand.Read(order); err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}
	sort.Slice(result, func(i, j int) bool {
		return order[i] < order[j]
	})

	d.Set("result", string(result))
	d.SetId(string(result))
	return nil
}

func generateRandomBytes(charSet *string, length int) ([]byte, error) {
	bytes := make([]byte, length)
	setLen := big.NewInt(int64(len(*charSet)))
	for i := range bytes {
		idx, err := rand.Int(rand.Reader, setLen)
		if err != nil {
			return nil, err
		}
		bytes[i] = (*charSet)[idx.Int64()]
	}
	return bytes, nil
}

func ReadString(d *schema.ResourceData, meta interface{}) error {
	return nil
}
<commit_msg>Set ID to 'none' for random_string<commit_after>package random

import (
	"crypto/rand"
	"math/big"
	"sort"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceString() *schema.Resource {
	return &schema.Resource{
		Create:        CreateString,
		Read:          ReadString,
		Delete:        schema.RemoveFromState,
		MigrateState:  resourceRandomStringMigrateState,
		SchemaVersion: 1,
		Schema: map[string]*schema.Schema{
			"keepers": {
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"length": {
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"special": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"upper": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"lower": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"number": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
				ForceNew: true,
			},

			"min_numeric": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_upper": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_lower": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"min_special": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
				ForceNew: true,
			},

			"override_special": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"result": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func CreateString(d *schema.ResourceData, meta interface{}) error {
	const numChars = "0123456789"
	const lowerChars = "abcdefghijklmnopqrstuvwxyz"
	const upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	var specialChars = "!@#$%&*()-_=+[]{}<>:?"

	length := d.Get("length").(int)
	upper := d.Get("upper").(bool)
	minUpper := d.Get("min_upper").(int)
	lower := d.Get("lower").(bool)
	minLower := d.Get("min_lower").(int)
	number := d.Get("number").(bool)
	minNumeric := d.Get("min_numeric").(int)
	special := d.Get("special").(bool)
	minSpecial := d.Get("min_special").(int)
	overrideSpecial := d.Get("override_special").(string)

	if overrideSpecial != "" {
		specialChars = overrideSpecial
	}

	var chars = string("")
	if upper {
		chars += upperChars
	}
	if lower {
		chars += lowerChars
	}
	if number {
		chars += numChars
	}
	if special {
		chars += specialChars
	}

	minMapping := map[string]int{
		numChars:     minNumeric,
		lowerChars:   minLower,
		upperChars:   minUpper,
		specialChars: minSpecial,
	}
	var result = make([]byte, 0, length)
	for k, v := range minMapping {
		s, err := generateRandomBytes(&k, v)
		if err != nil {
			return errwrap.Wrapf("error generating random bytes: {{err}}", err)
		}
		result = append(result, s...)
	}
	s, err := generateRandomBytes(&chars, length-len(result))
	if err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}
	result = append(result, s...)
	order := make([]byte, len(result))
	if _, err := rand.Read(order); err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}
	sort.Slice(result, func(i, j int) bool {
		return order[i] < order[j]
	})

	d.Set("result", string(result))
	d.SetId("none")
	return nil
}

func generateRandomBytes(charSet *string, length int) ([]byte, error) {
	bytes := make([]byte, length)
	setLen := big.NewInt(int64(len(*charSet)))
	for i := range bytes {
		idx, err := rand.Int(rand.Reader, setLen)
		if err != nil {
			return nil, err
		}
		bytes[i] = (*charSet)[idx.Int64()]
	}
	return bytes, nil
}

func ReadString(d *schema.ResourceData, meta interface{}) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"

	ansi "github.com/BourgeoisBear/ansiart2utf8"
)

func main() {

	var oErr error

	// ERROR LOGGING
	pLogErr := log.New(os.Stderr, "", log.Lshortfile)
	defer func() {

		if oErr != nil {

			pLogErr.Output(2, oErr.Error())
			fmt.Fprint(os.Stdout, "\x1b[0m")
			os.Exit(1)
		}
	}()

	runtime.GOMAXPROCS(1)

	const SZ_HELP_PREFIX = `
ansiart2utf8
	Converts ANSI art to UTF-8 encoding, expands cursor forward ESC sequences
	into spaces, wraps/resets at a specified line width, sends result to STDOUT.

	Leave the [FILE] parameter empty to read from STDIN.

USAGE: ansiart2utf8 [OPTION]... [FILE]...

OPTIONS
`

	// HELP MESSAGE
	flag.Usage = func() {

		fmt.Fprint(os.Stdout, SZ_HELP_PREFIX)
		flag.PrintDefaults()
		fmt.Fprint(os.Stdout, "\n")
	}

	UM := ansi.UTF8Marshaller{}

	// COMMAND PARAMETERS
	pbDebug := flag.Bool("debug", false, `DEBUG MODE: line numbering + pipe @ \n`)
	flag.BoolVar(&UM.Translate2Xterm256, "x", false, "ANSI TO XTERM-256 COLOR SUBSTITUTION\n  (to overcome strange terminal color scheme palettes)")

	flag.UintVar(&UM.Width, "w", 80, "LINE WRAP WIDTH")
	flag.UintVar(&UM.MaxBytes, "bytes", 0, "MAXIMUM OUTPUT BYTES PER-ROW (0 = NO LIMIT)")

	flag.Parse()

	if !flag.Parsed() {

		oErr = errors.New("Invalid Parameters")
		return
	}

	if UM.Width < 1 {

		oErr = errors.New("LINE WIDTH must be > 0")
		return
	}

	// DEBUG LOGGING
	fnDebug := func(v ...interface{}) (int, error) {

		if *pbDebug {
			return fmt.Println(v...)
		}

		return 0, nil
	}

	// BUFFER OUTPUT
	pWriter := bufio.NewWriter(os.Stdout)
	UM.Writer = pWriter

	if *pbDebug {
		UM.Debug = fnDebug
	}

	// PROCESS INPUT FILES
	arFiles := flag.Args()
	if len(arFiles) == 0 {
		arFiles = append(arFiles, "-")
	}

	for _, szFname := range arFiles {

		if szFname == "-" {

			fnDebug("PROCESSING STDIN")
			oErr = UM.Encode(os.Stdin)
			if oErr != nil {
				return
			}

		} else {

			pF, oE2 := os.Open(szFname)
			if oE2 != nil {
				oErr = fmt.Errorf("UNABLE TO OPEN %s: %s", szFname, oE2.Error())
				return
			}

			fnDebug("PROCESSING ", szFname)
			oErr = UM.Encode(pF)
			pF.Close()
			if oErr != nil {
				return
			}
		}

		pWriter.WriteByte(ansi.CHR_LF)
		pWriter.Flush()
	}

	os.Exit(0)
}
<commit_msg>- STDIN adjustment<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"

	ansi "github.com/BourgeoisBear/ansiart2utf8"
)

func main() {

	var oErr error

	// ERROR LOGGING
	pLogErr := log.New(os.Stderr, "", log.Lshortfile)
	defer func() {

		if oErr != nil {

			pLogErr.Output(2, oErr.Error())
			fmt.Fprint(os.Stdout, "\x1b[0m")
			os.Exit(1)
		}
	}()

	runtime.GOMAXPROCS(1)

	const SZ_HELP_PREFIX = `
ansiart2utf8
	Converts ANSI art to UTF-8 encoding, expands cursor forward ESC sequences
	into spaces, wraps/resets at a specified line width, sends result to STDOUT.

	Leave the [FILE] parameter empty to read from STDIN.

USAGE: ansiart2utf8 [OPTION]... [FILE]...

OPTIONS
`

	// HELP MESSAGE
	flag.Usage = func() {

		fmt.Fprint(os.Stdout, SZ_HELP_PREFIX)
		flag.PrintDefaults()
		fmt.Fprint(os.Stdout, "\n")
	}

	UM := ansi.UTF8Marshaller{}

	// COMMAND PARAMETERS
	pbDebug := flag.Bool("debug", false, `DEBUG MODE: line numbering + pipe @ \n`)
	flag.BoolVar(&UM.Translate2Xterm256, "x", false, "ANSI TO XTERM-256 COLOR SUBSTITUTION\n  (to overcome strange terminal color scheme palettes)")

	flag.UintVar(&UM.Width, "w", 80, "LINE WRAP WIDTH")
	flag.UintVar(&UM.MaxBytes, "bytes", 0, "MAXIMUM OUTPUT BYTES PER-ROW (0 = NO LIMIT)")

	flag.Parse()

	if !flag.Parsed() {

		oErr = errors.New("Invalid Parameters")
		return
	}

	if UM.Width < 1 {

		oErr = errors.New("LINE WIDTH must be > 0")
		return
	}

	// DEBUG LOGGING
	fnDebug := func(v ...interface{}) (int, error) {

		if *pbDebug {
			return fmt.Println(v...)
		}

		return 0, nil
	}

	// BUFFER OUTPUT
	pWriter := bufio.NewWriter(os.Stdout)
	UM.Writer = pWriter

	if *pbDebug {
		UM.Debug = fnDebug
	}

	// PROCESS INPUT FILES
	arFiles := flag.Args()
	if len(arFiles) == 0 {
		arFiles = append(arFiles, "-")
	}

	for _, szFname := range arFiles {

		if (szFname == "-") && (len(arFiles) == 1) {

			fnDebug("PROCESSING STDIN")
			oErr = UM.Encode(os.Stdin)
			if oErr != nil {
				return
			}

		} else {

			pF, oE2 := os.Open(szFname)
			if oE2 != nil {
				oErr = fmt.Errorf("UNABLE TO OPEN %s: %s", szFname, oE2.Error())
				return
			}

			fnDebug("PROCESSING ", szFname)
			oErr = UM.Encode(pF)
			pF.Close()
			if oErr != nil {
				return
			}
		}

		pWriter.WriteByte(ansi.CHR_LF)
		pWriter.Flush()
	}

	os.Exit(0)
}
<|endoftext|>


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

package frankenstein

import (
	"fmt"
	"hash/fnv"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"golang.org/x/net/context"

	"github.com/prometheus/prometheus/storage/metric"
)

var (
	numClientsDesc = prometheus.NewDesc(
		"prometheus_distributor_ingester_clients",
		"The current number of ingester clients.",
		nil, nil,
	)
)

// Distributor is a storage.SampleAppender and a frankenstein.Querier which
// forwards appends and queries to individual ingesters.
type Distributor struct {
	ring       *Ring
	cfg        DistributorConfig
	clientsMtx sync.RWMutex
	clients    map[string]*IngesterClient

	quit chan struct{}
	done chan struct{}

	queryDuration   *prometheus.HistogramVec
	consulUpdates   prometheus.Counter
	receivedSamples prometheus.Counter
	sendDuration    *prometheus.HistogramVec
}

// DistributorConfig contains the configuration require to
// create a Distributor
type DistributorConfig struct {
	ConsulClient  ConsulClient
	ConsulPrefix  string
	ClientFactory func(string) (*IngesterClient, error)
}

// IngesterDesc is the serialised state in Consul representing
// an individual ingester.
type IngesterDesc struct {
	ID       string   `json:"ID"`
	Hostname string   `json:"hostname"`
	Tokens   []uint32 `json:"tokens"`
}

// NewDistributor constructs a new Distributor
func NewDistributor(cfg DistributorConfig) (*Distributor, error) {
	d := &Distributor{
		ring:    NewRing(),
		cfg:     cfg,
		clients: map[string]*IngesterClient{},
		quit:    make(chan struct{}),
		done:    make(chan struct{}),
		queryDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Namespace: "prometheus",
			Name:      "distributor_query_duration_seconds",
			Help:      "Time spent executing expression queries.",
			Buckets:   prometheus.DefBuckets,
		}, []string{"status_code"}),
		consulUpdates: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "prometheus",
			Name:      "distributor_consul_updates_total",
			Help:      "The total number of received Consul updates.",
		}),
		receivedSamples: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "prometheus",
			Name:      "distributor_received_samples_total",
			Help:      "The total number of received samples.",
		}),
		sendDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Namespace: "prometheus",
			Name:      "distributor_send_duration_seconds",
			Help:      "Time spent sending sample batches to ingesters.",
			Buckets:   prometheus.DefBuckets,
		}, []string{"status_code"}),
	}
	go d.loop()
	return d, nil
}

// Stop the distributor.
func (d *Distributor) Stop() {
	close(d.quit)
	<-d.done
}

func (d *Distributor) loop() {
	defer close(d.done)
	factory := func() interface{} { return &IngesterDesc{} }
	d.cfg.ConsulClient.WatchPrefix(d.cfg.ConsulPrefix, factory, d.quit, func(key string, value interface{}) bool {
		c := *value.(*IngesterDesc)
		log.Infof("Got update to ingester %d", c.ID)
		d.consulUpdates.Inc()
		d.ring.Update(c)
		return true
	})
}

func (d *Distributor) getClientFor(hostname string) (*IngesterClient, error) {
	d.clientsMtx.RLock()
	client, ok := d.clients[hostname]
	d.clientsMtx.RUnlock()
	if ok {
		return client, nil
	}

	d.clientsMtx.Lock()
	defer d.clientsMtx.Unlock()
	client, ok = d.clients[hostname]
	if ok {
		return client, nil
	}

	client, err := d.cfg.ClientFactory(hostname)
	if err != nil {
		return nil, err
	}
	d.clients[hostname] = client
	return client, nil
}

func tokenForMetric(metric model.Metric) uint32 {
	name := metric[model.MetricNameLabel]
	return tokenFor(name)
}

func tokenFor(name model.LabelValue) uint32 {
	h := fnv.New32()
	h.Write([]byte(name))
	return h.Sum32()
}

// Append implements SampleAppender.
func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error {
	d.receivedSamples.Add(float64(len(samples)))

	samplesByIngester := map[string][]*model.Sample{}
	for _, sample := range samples {
		key := tokenForMetric(sample.Metric)
		collector, err := d.ring.Get(key)
		if err != nil {
			return err
		}
		otherSamples := samplesByIngester[collector.Hostname]
		samplesByIngester[collector.Hostname] = append(otherSamples, sample)
	}

	// TODO paralellise
	for hostname, samples := range samplesByIngester {
		err := d.sendSamples(ctx, hostname, samples)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *Distributor) sendSamples(ctx context.Context, hostname string, samples []*model.Sample) error {
	client, err := d.getClientFor(hostname)
	if err != nil {
		return err
	}
	return timeRequestHistogramStatus(d.sendDuration, nil, func() error {
		return client.Append(ctx, samples)
	})
}

func metricNameFromLabelMatchers(matchers ...*metric.LabelMatcher) (model.LabelValue, error) {
	for _, m := range matchers {
		if m.Name == model.MetricNameLabel {
			if m.Type != metric.Equal {
				return "", fmt.Errorf("non-equality matchers are not supported on the metric name")
			}
			return m.Value, nil
		}
	}
	return "", fmt.Errorf("no metric name matcher found")
}

// Query implements Querier.
func (d *Distributor) Query(ctx context.Context, from, to model.Time, matchers ...*metric.LabelMatcher) (model.Matrix, error) {
	var result model.Matrix
	err := timeRequestHistogramStatus(d.queryDuration, nil, func() error {
		metricName, err := metricNameFromLabelMatchers(matchers...)
		if err != nil {
			return err
		}

		collector, err := d.ring.Get(tokenFor(metricName))
		if err != nil {
			return err
		}

		client, err := d.getClientFor(collector.Hostname)
		if err != nil {
			return err
		}

		result, err = client.Query(ctx, from, to, matchers...)
		if err != nil {
			return err
		}
		return nil
	})
	return result, err
}

// LabelValuesForLabelName returns all of the label values that are associated with a given label name.
func (d *Distributor) LabelValuesForLabelName(ctx context.Context, labelName model.LabelName) (model.LabelValues, error) {
	valueSet := map[model.LabelValue]struct{}{}
	for _, c := range d.ring.GetAll() {
		client, err := d.getClientFor(c.Hostname)
		if err != nil {
			return nil, err
		}
		vals, err := client.LabelValuesForLabelName(ctx, labelName)
		if err != nil {
			return nil, err
		}
		for _, v := range vals {
			valueSet[v] = struct{}{}
		}
	}

	values := make(model.LabelValues, 0, len(valueSet))
	for v := range valueSet {
		values = append(values, v)
	}
	return values, nil
}

// NeedsThrottling implements SampleAppender.
func (*Distributor) NeedsThrottling(_ context.Context) bool {
	return false
}

// Describe implements prometheus.Collector.
func (d *Distributor) Describe(ch chan<- *prometheus.Desc) {
	d.queryDuration.Describe(ch)
	ch <- d.consulUpdates.Desc()
	ch <- d.receivedSamples.Desc()
	d.sendDuration.Describe(ch)
	d.ring.Describe(ch)
	ch <- numClientsDesc
}

// Collect implements prometheus.Collector.
func (d *Distributor) Collect(ch chan<- prometheus.Metric) {
	d.queryDuration.Collect(ch)
	ch <- d.consulUpdates
	ch <- d.receivedSamples
	d.sendDuration.Collect(ch)
	d.ring.Collect(ch)

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

package frankenstein

import (
	"fmt"
	"hash/fnv"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"golang.org/x/net/context"

	"github.com/prometheus/prometheus/storage/metric"
)

var (
	numClientsDesc = prometheus.NewDesc(
		"prometheus_distributor_ingester_clients",
		"The current number of ingester clients.",
		nil, nil,
	)
)

// Distributor is a storage.SampleAppender and a frankenstein.Querier which
// forwards appends and queries to individual ingesters.
type Distributor struct {
	ring       *Ring
	cfg        DistributorConfig
	clientsMtx sync.RWMutex
	clients    map[string]*IngesterClient

	quit chan struct{}
	done chan struct{}

	queryDuration   *prometheus.HistogramVec
	consulUpdates   prometheus.Counter
	receivedSamples prometheus.Counter
	sendDuration    *prometheus.HistogramVec
}

// DistributorConfig contains the configuration require to
// create a Distributor
type DistributorConfig struct {
	ConsulClient  ConsulClient
	ConsulPrefix  string
	ClientFactory func(string) (*IngesterClient, error)
}

// IngesterDesc is the serialised state in Consul representing
// an individual ingester.
type IngesterDesc struct {
	ID       string   `json:"ID"`
	Hostname string   `json:"hostname"`
	Tokens   []uint32 `json:"tokens"`
}

// NewDistributor constructs a new Distributor
func NewDistributor(cfg DistributorConfig) (*Distributor, error) {
	d := &Distributor{
		ring:    NewRing(),
		cfg:     cfg,
		clients: map[string]*IngesterClient{},
		quit:    make(chan struct{}),
		done:    make(chan struct{}),
		queryDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Namespace: "prometheus",
			Name:      "distributor_query_duration_seconds",
			Help:      "Time spent executing expression queries.",
			Buckets:   prometheus.DefBuckets,
		}, []string{"status_code"}),
		consulUpdates: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "prometheus",
			Name:      "distributor_consul_updates_total",
			Help:      "The total number of received Consul updates.",
		}),
		receivedSamples: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "prometheus",
			Name:      "distributor_received_samples_total",
			Help:      "The total number of received samples.",
		}),
		sendDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Namespace: "prometheus",
			Name:      "distributor_send_duration_seconds",
			Help:      "Time spent sending sample batches to ingesters.",
			Buckets:   prometheus.DefBuckets,
		}, []string{"status_code"}),
	}
	go d.loop()
	return d, nil
}

// Stop the distributor.
func (d *Distributor) Stop() {
	close(d.quit)
	<-d.done
}

func (d *Distributor) loop() {
	defer close(d.done)
	factory := func() interface{} { return &IngesterDesc{} }
	d.cfg.ConsulClient.WatchPrefix(d.cfg.ConsulPrefix, factory, d.quit, func(key string, value interface{}) bool {
		c := *value.(*IngesterDesc)
		log.Infof("Got update to ingester %d", c.ID)
		d.consulUpdates.Inc()
		d.ring.Update(c)
		return true
	})
}

func (d *Distributor) getClientFor(hostname string) (*IngesterClient, error) {
	d.clientsMtx.RLock()
	client, ok := d.clients[hostname]
	d.clientsMtx.RUnlock()
	if ok {
		return client, nil
	}

	d.clientsMtx.Lock()
	defer d.clientsMtx.Unlock()
	client, ok = d.clients[hostname]
	if ok {
		return client, nil
	}

	client, err := d.cfg.ClientFactory(hostname)
	if err != nil {
		return nil, err
	}
	d.clients[hostname] = client
	return client, nil
}

func tokenForMetric(metric model.Metric) uint32 {
	name := metric[model.MetricNameLabel]
	return tokenFor(name)
}

func tokenFor(name model.LabelValue) uint32 {
	h := fnv.New32()
	h.Write([]byte(name))
	return h.Sum32()
}

// Append implements SampleAppender.
func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error {
	d.receivedSamples.Add(float64(len(samples)))

	samplesByIngester := map[string][]*model.Sample{}
	for _, sample := range samples {
		key := tokenForMetric(sample.Metric)
		collector, err := d.ring.Get(key)
		if err != nil {
			return err
		}
		otherSamples := samplesByIngester[collector.Hostname]
		samplesByIngester[collector.Hostname] = append(otherSamples, sample)
	}

	errs := make(chan error)
	for hostname, samples := range samplesByIngester {
		go func(ctx context.Context, hostname string, samples []*model.Sample) {
			errs <- d.sendSamples(ctx, hostname, samples)
		}(ctx, hostname, samples)
	}
	var lastErr error
	for i := 0; i < len(samplesByIngester); i++ {
		if err := <-errs; err != nil {
			lastErr = err
		}
	}
	return lastErr
}

func (d *Distributor) sendSamples(ctx context.Context, hostname string, samples []*model.Sample) error {
	client, err := d.getClientFor(hostname)
	if err != nil {
		return err
	}
	return timeRequestHistogramStatus(d.sendDuration, nil, func() error {
		return client.Append(ctx, samples)
	})
}

func metricNameFromLabelMatchers(matchers ...*metric.LabelMatcher) (model.LabelValue, error) {
	for _, m := range matchers {
		if m.Name == model.MetricNameLabel {
			if m.Type != metric.Equal {
				return "", fmt.Errorf("non-equality matchers are not supported on the metric name")
			}
			return m.Value, nil
		}
	}
	return "", fmt.Errorf("no metric name matcher found")
}

// Query implements Querier.
func (d *Distributor) Query(ctx context.Context, from, to model.Time, matchers ...*metric.LabelMatcher) (model.Matrix, error) {
	var result model.Matrix
	err := timeRequestHistogramStatus(d.queryDuration, nil, func() error {
		metricName, err := metricNameFromLabelMatchers(matchers...)
		if err != nil {
			return err
		}

		collector, err := d.ring.Get(tokenFor(metricName))
		if err != nil {
			return err
		}

		client, err := d.getClientFor(collector.Hostname)
		if err != nil {
			return err
		}

		result, err = client.Query(ctx, from, to, matchers...)
		if err != nil {
			return err
		}
		return nil
	})
	return result, err
}

// LabelValuesForLabelName returns all of the label values that are associated with a given label name.
func (d *Distributor) LabelValuesForLabelName(ctx context.Context, labelName model.LabelName) (model.LabelValues, error) {
	valueSet := map[model.LabelValue]struct{}{}
	for _, c := range d.ring.GetAll() {
		client, err := d.getClientFor(c.Hostname)
		if err != nil {
			return nil, err
		}
		vals, err := client.LabelValuesForLabelName(ctx, labelName)
		if err != nil {
			return nil, err
		}
		for _, v := range vals {
			valueSet[v] = struct{}{}
		}
	}

	values := make(model.LabelValues, 0, len(valueSet))
	for v := range valueSet {
		values = append(values, v)
	}
	return values, nil
}

// NeedsThrottling implements SampleAppender.
func (*Distributor) NeedsThrottling(_ context.Context) bool {
	return false
}

// Describe implements prometheus.Collector.
func (d *Distributor) Describe(ch chan<- *prometheus.Desc) {
	d.queryDuration.Describe(ch)
	ch <- d.consulUpdates.Desc()
	ch <- d.receivedSamples.Desc()
	d.sendDuration.Describe(ch)
	d.ring.Describe(ch)
	ch <- numClientsDesc
}

// Collect implements prometheus.Collector.
func (d *Distributor) Collect(ch chan<- prometheus.Metric) {
	d.queryDuration.Collect(ch)
	ch <- d.consulUpdates
	ch <- d.receivedSamples
	d.sendDuration.Collect(ch)
	d.ring.Collect(ch)

	d.clientsMtx.RLock()
	defer d.clientsMtx.RUnlock()
	ch <- prometheus.MustNewConstMetric(
		numClientsDesc,
		prometheus.GaugeValue,
		float64(len(d.clients)),
	)
}
<|endoftext|>


<commit_before>package realms

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/matrix-org/go-neb/types"
	"net/http"
)

type jiraRealm struct {
	id             string
	ConsumerName   string
	ConsumerKey    string
	ConsumerSecret string
	PrivateKeyPEM  string
}

func (r *jiraRealm) ID() string {
	return r.id
}

func (r *jiraRealm) Type() string {
	return "jira"
}

func (r *jiraRealm) Register() error {
	if r.ConsumerName == "" || r.ConsumerKey == "" || r.ConsumerSecret == "" || r.PrivateKeyPEM == "" {
		return errors.New("ConsumerName, ConsumerKey, ConsumerSecret, PrivateKeyPEM must be specified.")
	}
	// Make sure the private key PEM is actually a private key.
	_, err := loadPrivateKey(r.PrivateKeyPEM)
	if err != nil {
		return err
	}
	return nil
}

func (r *jiraRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
	reqAuth := struct {
		JIRAURL string
	}{}
	if err := json.Unmarshal(req, reqAuth); err != nil {
		log.WithError(err).Print("Error parsing request JSON")
		return nil
	}
	if reqAuth.JIRAURL == "" {
		log.Print("Missing JIRAURL")
		return nil
	}
	// TODO: Check to see if JIRA endpoint is valid and known
	return nil
}

func (r *jiraRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {

}

func (r *jiraRealm) AuthSession(id, userID, realmID string) types.AuthSession {
	return nil
}

func loadPrivateKey(privKeyPEM string) (*rsa.PrivateKey, error) {
	// Decode PEM to grab the private key type
	block, _ := pem.Decode([]byte(privKeyPEM))
	if block == nil {
		return nil, errors.New("No PEM formatted block found")
	}

	// TODO: Handle passwords on private keys.
	// decBytes, err = x509.DecryptPEMBlock(block, []byte{}) // no pass

	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

func init() {
	types.RegisterAuthRealm(func(realmID string) types.AuthRealm {
		return &jiraRealm{id: realmID}
	})
}
<commit_msg>Parse out the public key<commit_after>package realms

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/matrix-org/go-neb/types"
	"net/http"
)

type jiraRealm struct {
	id             string
	privateKey     *rsa.PrivateKey
	JIRAEndpoint   string
	ConsumerName   string
	ConsumerKey    string
	ConsumerSecret string
	PublicKeyPEM   string // clobbered based on PrivateKeyPEM
	PrivateKeyPEM  string
}

func (r *jiraRealm) ID() string {
	return r.id
}

func (r *jiraRealm) Type() string {
	return "jira"
}

func (r *jiraRealm) Register() error {
	if r.ConsumerName == "" || r.ConsumerKey == "" || r.ConsumerSecret == "" || r.PrivateKeyPEM == "" {
		return errors.New("ConsumerName, ConsumerKey, ConsumerSecret, PrivateKeyPEM must be specified.")
	}
	log.Print("Registering..")
	// Make sure the private key PEM is actually a private key.
	err := r.parsePrivateKey()
	if err != nil {
		return err
	}

	// TODO: Check to see if JIRA endpoint is valid and known

	return nil
}

func (r *jiraRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
	return nil
}

func (r *jiraRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {

}

func (r *jiraRealm) AuthSession(id, userID, realmID string) types.AuthSession {
	return nil
}

func (r *jiraRealm) parsePrivateKey() error {
	pk, err := loadPrivateKey(r.PrivateKeyPEM)
	if err != nil {
		return err
	}
	pub, err := publicKeyAsPEM(pk)
	if err != nil {
		return err
	}
	r.PublicKeyPEM = pub
	r.privateKey = pk
	return nil
}

func loadPrivateKey(privKeyPEM string) (*rsa.PrivateKey, error) {
	// Decode PEM to grab the private key type
	block, _ := pem.Decode([]byte(privKeyPEM))
	if block == nil {
		return nil, errors.New("No PEM formatted block found")
	}

	// TODO: Handle passwords on private keys.
	// decBytes, err = x509.DecryptPEMBlock(block, []byte{}) // no pass

	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

func publicKeyAsPEM(pkey *rsa.PrivateKey) (string, error) {
	// https://github.com/golang-samples/cipher/blob/master/crypto/rsa_keypair.go
	der, err := x509.MarshalPKIXPublicKey(&pkey.PublicKey)
	if err != nil {
		return "", err
	}
	block := pem.Block{
		Type:    "PUBLIC KEY",
		Headers: nil,
		Bytes:   der,
	}
	return string(pem.EncodeToMemory(&block)), nil
}

func init() {
	types.RegisterAuthRealm(func(realmID string) types.AuthRealm {
		return &jiraRealm{id: realmID}
	})
}
<|endoftext|>